mirror of
https://github.com/ansible/awx.git
synced 2026-01-23 07:28:02 -03:30
Upgrading some novaclient specific vendored modules
This commit is contained in:
parent
3d8955b298
commit
768d9d65b9
@ -42,8 +42,8 @@ netaddr==0.7.14 (netaddr/*)
|
||||
os_client_config==0.6.0 (os_client_config/*)
|
||||
ordereddict==1.1 (ordereddict.py, needed for Python 2.6 support)
|
||||
os_diskconfig_python_novaclient_ext==0.1.2 (os_diskconfig_python_novaclient_ext/*)
|
||||
os_networksv2_python_novaclient_ext==0.21 (os_networksv2_python_novaclient_ext.py)
|
||||
os_virtual_interfacesv2_python_novaclient_ext==0.15 (os_virtual_interfacesv2_python_novaclient_ext.py)
|
||||
os_networksv2_python_novaclient_ext==0.25 (os_networksv2_python_novaclient_ext.py)
|
||||
os_virtual_interfacesv2_python_novaclient_ext==0.19 (os_virtual_interfacesv2_python_novaclient_ext.py)
|
||||
oslo.i18n==1.5.0 (oslo_i18n/* oslo.i18n/* which goes in oslo/i18n... created empty __init__.py at oslo/)
|
||||
oslo.config==1.9.3 (oslo_config/* oslo/config/* but not __init__.py from oslo/ used empty one instead)
|
||||
oslo.serialization==1.4.0 (oslo_serialization/* oslo/serialization/* but not __init__.py from oslo/ used empty one instead)
|
||||
@ -59,7 +59,7 @@ 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.18.1 (novaclient/*, excluded bin/nova)
|
||||
python-novaclient==2.23.0 (novaclient/*, excluded bin/nova)
|
||||
python-swiftclient==2.2.0 (swiftclient/*, excluded bin/swift)
|
||||
python-troveclient==1.0.9 (troveclient/*)
|
||||
pytz==2014.10 (pytz/*)
|
||||
|
||||
@ -20,13 +20,17 @@ 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 import utils
|
||||
from novaclient.openstack.common import cliutils
|
||||
|
||||
Resource = base.Resource
|
||||
|
||||
@ -42,24 +46,17 @@ def getid(obj):
|
||||
return obj
|
||||
|
||||
|
||||
class Manager(utils.HookableMixin):
|
||||
class Manager(base.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)
|
||||
@ -78,22 +75,86 @@ class Manager(utils.HookableMixin):
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
self._clear_completion_cache_for_class(obj_class)
|
||||
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]
|
||||
|
||||
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 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
|
||||
|
||||
return objs
|
||||
@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)
|
||||
|
||||
def _get(self, url, response_key):
|
||||
_resp, body = self.api.client.get(url)
|
||||
obj = self.resource_class(self, body[response_key], loaded=True)
|
||||
self._write_object_to_completion_cache(obj)
|
||||
return obj
|
||||
return self.resource_class(self, body[response_key], loaded=True)
|
||||
|
||||
def _create(self, url, body, response_key, return_raw=False, **kwargs):
|
||||
self.run_hooks('modify_body_for_create', body, **kwargs)
|
||||
@ -101,9 +162,9 @@ class Manager(utils.HookableMixin):
|
||||
if return_raw:
|
||||
return body[response_key]
|
||||
|
||||
obj = self.resource_class(self, body[response_key])
|
||||
self._write_object_to_completion_cache(obj)
|
||||
return obj
|
||||
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])
|
||||
|
||||
def _delete(self, url):
|
||||
_resp, _body = self.api.client.delete(url)
|
||||
@ -131,9 +192,6 @@ class ManagerWithFind(Manager):
|
||||
def find(self, **kwargs):
|
||||
"""
|
||||
Find a single item with attributes matching ``**kwargs``.
|
||||
|
||||
This isn't very efficient: it loads the entire list then filters on
|
||||
the Python side.
|
||||
"""
|
||||
matches = self.findall(**kwargs)
|
||||
num_matches = len(matches)
|
||||
@ -148,9 +206,6 @@ class ManagerWithFind(Manager):
|
||||
def findall(self, **kwargs):
|
||||
"""
|
||||
Find all items with attributes matching ``**kwargs``.
|
||||
|
||||
This isn't very efficient: it loads the entire list then filters on
|
||||
the Python side.
|
||||
"""
|
||||
found = []
|
||||
searches = kwargs.items()
|
||||
@ -173,6 +228,24 @@ class ManagerWithFind(Manager):
|
||||
del tmp_kwargs['is_public']
|
||||
searches = tmp_kwargs.items()
|
||||
|
||||
if 'search_opts' in list_argspec.args:
|
||||
# pass search_opts in to do server side based filtering.
|
||||
# TODO(jogo) not all search_opts support regex, find way to
|
||||
# identify when to use regex and when to use string matching.
|
||||
# volumes does not support regex while servers does. So when
|
||||
# doing findall on servers some client side filtering is still
|
||||
# needed.
|
||||
if "human_id" in kwargs:
|
||||
list_kwargs['search_opts'] = {"name": kwargs["human_id"]}
|
||||
elif "name" in kwargs:
|
||||
list_kwargs['search_opts'] = {"name": kwargs["name"]}
|
||||
elif "display_name" in kwargs:
|
||||
list_kwargs['search_opts'] = {"name": kwargs["display_name"]}
|
||||
if "all_tenants" in kwargs:
|
||||
all_tenants = kwargs['all_tenants']
|
||||
list_kwargs['search_opts']['all_tenants'] = all_tenants
|
||||
searches = [(k, v) for k, v in searches if k != 'all_tenants']
|
||||
|
||||
listing = self.list(**list_kwargs)
|
||||
|
||||
for obj in listing:
|
||||
@ -204,11 +277,15 @@ class BootingManagerWithFind(ManagerWithFind):
|
||||
|
||||
mapping_parts = mapping.split(':')
|
||||
source_id = mapping_parts[0]
|
||||
bdm_dict['uuid'] = source_id
|
||||
bdm_dict['boot_index'] = 0
|
||||
if len(mapping_parts) == 1:
|
||||
bdm_dict['volume_id'] = source_id
|
||||
bdm_dict['source_type'] = 'volume'
|
||||
|
||||
elif len(mapping_parts) > 1:
|
||||
source_type = mapping_parts[1]
|
||||
bdm_dict['source_type'] = source_type
|
||||
if source_type.startswith('snap'):
|
||||
bdm_dict['snapshot_id'] = source_id
|
||||
else:
|
||||
|
||||
@ -20,14 +20,17 @@
|
||||
OpenStack Client interface. Handles the REST calls and responses.
|
||||
"""
|
||||
|
||||
import errno
|
||||
import copy
|
||||
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
|
||||
|
||||
@ -39,12 +42,19 @@ except ImportError:
|
||||
from six.moves.urllib import parse
|
||||
|
||||
from novaclient import exceptions
|
||||
from novaclient.openstack.common.gettextutils import _
|
||||
from novaclient.openstack.common import network_utils
|
||||
from novaclient.i18n import _
|
||||
from novaclient import service_catalog
|
||||
from novaclient import utils
|
||||
|
||||
SENSITIVE_HEADERS = ('X-Auth-Token',)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
class _ClientConnectionPool(object):
|
||||
@ -57,140 +67,41 @@ class _ClientConnectionPool(object):
|
||||
Store and reuse HTTP adapters per Service URL.
|
||||
"""
|
||||
if url not in self._adapters:
|
||||
self._adapters[url] = adapters.HTTPAdapter()
|
||||
self._adapters[url] = TCPKeepAliveAdapter()
|
||||
|
||||
return self._adapters[url]
|
||||
|
||||
|
||||
class CompletionCache(object):
|
||||
"""The completion cache is how we support tab-completion with novaclient.
|
||||
class SessionClient(adapter.LegacyJsonAdapter):
|
||||
|
||||
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(object):
|
||||
|
||||
def __init__(self, session, auth, interface, service_type, region_name):
|
||||
self.session = session
|
||||
self.auth = auth
|
||||
|
||||
self.interface = interface
|
||||
self.service_type = service_type
|
||||
self.region_name = region_name
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.times = []
|
||||
super(SessionClient, self).__init__(*args, **kwargs)
|
||||
|
||||
def request(self, url, method, **kwargs):
|
||||
kwargs.setdefault('user_agent', 'python-novaclient')
|
||||
kwargs.setdefault('auth', self.auth)
|
||||
kwargs.setdefault('authenticated', False)
|
||||
# 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)
|
||||
|
||||
try:
|
||||
kwargs['json'] = kwargs.pop('body')
|
||||
except KeyError:
|
||||
pass
|
||||
end_time = time.time()
|
||||
self.times.append(('%s %s' % (method, url),
|
||||
start_time, end_time))
|
||||
|
||||
headers = kwargs.setdefault('headers', {})
|
||||
headers.setdefault('Accept', 'application/json')
|
||||
|
||||
endpoint_filter = kwargs.setdefault('endpoint_filter', {})
|
||||
endpoint_filter.setdefault('interface', self.interface)
|
||||
endpoint_filter.setdefault('service_type', self.service_type)
|
||||
endpoint_filter.setdefault('region_name', self.region_name)
|
||||
|
||||
resp = self.session.request(url, method, raise_exc=False, **kwargs)
|
||||
|
||||
body = None
|
||||
if resp.text:
|
||||
try:
|
||||
body = resp.json()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if resp.status_code >= 400:
|
||||
if raise_exc and resp.status_code >= 400:
|
||||
raise exceptions.from_response(resp, body, url, method)
|
||||
|
||||
return resp, body
|
||||
|
||||
def _cs_request(self, url, method, **kwargs):
|
||||
# this function is mostly redundant but makes compatibility easier
|
||||
kwargs.setdefault('authenticated', True)
|
||||
return self.request(url, method, **kwargs)
|
||||
def get_timings(self):
|
||||
return self.times
|
||||
|
||||
def get(self, url, **kwargs):
|
||||
return self._cs_request(url, 'GET', **kwargs)
|
||||
|
||||
def post(self, url, **kwargs):
|
||||
return self._cs_request(url, 'POST', **kwargs)
|
||||
|
||||
def put(self, url, **kwargs):
|
||||
return self._cs_request(url, 'PUT', **kwargs)
|
||||
|
||||
def delete(self, url, **kwargs):
|
||||
return self._cs_request(url, 'DELETE', **kwargs)
|
||||
def reset_timings(self):
|
||||
self.times = []
|
||||
|
||||
|
||||
def _original_only(f):
|
||||
@ -234,7 +145,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
|
||||
@ -301,6 +212,11 @@ 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
|
||||
|
||||
@ -318,21 +234,47 @@ class HTTPClient(object):
|
||||
def reset_timings(self):
|
||||
self.times = []
|
||||
|
||||
def safe_header(self, name, value):
|
||||
if name in SENSITIVE_HEADERS:
|
||||
# because in python3 byte string handling is ... ug
|
||||
v = value.encode('utf-8')
|
||||
h = hashlib.sha1(v)
|
||||
d = h.hexdigest()
|
||||
return name, "{SHA1}%s" % d
|
||||
else:
|
||||
return name, value
|
||||
def _redact(self, target, path, text=None):
|
||||
"""Replace the value of a key in `target`.
|
||||
|
||||
The key can be at the top level by specifying a list with a single
|
||||
key as the path. Nested dictionaries are also supported by passing a
|
||||
list of keys to be navigated to find the one that should be replaced.
|
||||
In this case the last one is the one that will be replaced.
|
||||
|
||||
:param dict target: the dictionary that may have a key to be redacted;
|
||||
modified in place
|
||||
:param list path: a list representing the nested structure in `target`
|
||||
that should be redacted; modified in place
|
||||
:param string text: optional text to use as a replacement for the
|
||||
redacted key. if text is not specified, the
|
||||
default text will be sha1 hash of the value being
|
||||
redacted
|
||||
"""
|
||||
|
||||
key = path.pop()
|
||||
|
||||
# move to the most nested dict
|
||||
for p in path:
|
||||
try:
|
||||
target = target[p]
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
if key in target:
|
||||
if text:
|
||||
target[key] = text
|
||||
else:
|
||||
# because in python3 byte string handling is ... ug
|
||||
value = target[key].encode('utf-8')
|
||||
sha1sum = hashlib.sha1(value)
|
||||
target[key] = "{SHA1}%s" % sha1sum.hexdigest()
|
||||
|
||||
def http_log_req(self, method, url, kwargs):
|
||||
if not self.http_log_debug:
|
||||
return
|
||||
|
||||
string_parts = ['curl -i']
|
||||
string_parts = ['curl -g -i']
|
||||
|
||||
if not kwargs.get('verify', True):
|
||||
string_parts.append(' --insecure')
|
||||
@ -340,24 +282,38 @@ class HTTPClient(object):
|
||||
string_parts.append(" '%s'" % url)
|
||||
string_parts.append(' -X %s' % method)
|
||||
|
||||
headers = copy.deepcopy(kwargs['headers'])
|
||||
self._redact(headers, ['X-Auth-Token'])
|
||||
# because dict ordering changes from 2 to 3
|
||||
keys = sorted(kwargs['headers'].keys())
|
||||
keys = sorted(headers.keys())
|
||||
for name in keys:
|
||||
value = kwargs['headers'][name]
|
||||
header = ' -H "%s: %s"' % self.safe_header(name, value)
|
||||
value = headers[name]
|
||||
header = ' -H "%s: %s"' % (name, value)
|
||||
string_parts.append(header)
|
||||
|
||||
if 'data' in kwargs:
|
||||
string_parts.append(" -d '%s'" % (kwargs['data']))
|
||||
data = json.loads(kwargs['data'])
|
||||
self._redact(data, ['auth', 'passwordCredentials', 'password'])
|
||||
string_parts.append(" -d '%s'" % json.dumps(data))
|
||||
self._logger.debug("REQ: %s" % "".join(string_parts))
|
||||
|
||||
def http_log_resp(self, resp):
|
||||
if not self.http_log_debug:
|
||||
return
|
||||
|
||||
if resp.text and resp.status_code != 400:
|
||||
try:
|
||||
body = json.loads(resp.text)
|
||||
self._redact(body, ['access', 'token', 'id'])
|
||||
except ValueError:
|
||||
body = None
|
||||
else:
|
||||
body = None
|
||||
|
||||
self._logger.debug("RESP: [%(status)s] %(headers)s\nRESP BODY: "
|
||||
"%(text)s\n", {'status': resp.status_code,
|
||||
'headers': resp.headers,
|
||||
'text': resp.text})
|
||||
'text': json.dumps(body)})
|
||||
|
||||
def open_session(self):
|
||||
if not self._connection_pool:
|
||||
@ -379,10 +335,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
|
||||
@ -422,7 +378,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)
|
||||
@ -446,6 +402,20 @@ class HTTPClient(object):
|
||||
def _cs_request(self, url, method, **kwargs):
|
||||
if not self.management_url:
|
||||
self.authenticate()
|
||||
if url is None:
|
||||
# To get API version information, it is necessary to GET
|
||||
# a nova endpoint directly without "v2/<tenant-id>".
|
||||
magic_tuple = parse.urlsplit(self.management_url)
|
||||
scheme, netloc, path, query, frag = magic_tuple
|
||||
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
|
||||
|
||||
# Perform the request once. If we get a 401 back then it
|
||||
# might be because the auth token expired, so try to
|
||||
@ -455,8 +425,7 @@ class HTTPClient(object):
|
||||
if self.projectid:
|
||||
kwargs['headers']['X-Auth-Project-Id'] = self.projectid
|
||||
|
||||
resp, body = self._time_request(self.management_url + url, method,
|
||||
**kwargs)
|
||||
resp, body = self._time_request(url, method, **kwargs)
|
||||
return resp, body
|
||||
except exceptions.Unauthorized as e:
|
||||
try:
|
||||
@ -467,8 +436,7 @@ class HTTPClient(object):
|
||||
self.keyring_saved = False
|
||||
self.authenticate()
|
||||
kwargs['headers']['X-Auth-Token'] = self.auth_token
|
||||
resp, body = self._time_request(self.management_url + url,
|
||||
method, **kwargs)
|
||||
resp, body = self._time_request(url, method, **kwargs)
|
||||
return resp, body
|
||||
except exceptions.Unauthorized:
|
||||
raise e
|
||||
@ -490,6 +458,19 @@ 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
|
||||
@ -506,14 +487,7 @@ class HTTPClient(object):
|
||||
self.auth_token = self.service_catalog.get_token()
|
||||
self.tenant_id = self.service_catalog.get_tenant_id()
|
||||
|
||||
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('/')
|
||||
self.management_url = self.get_service_url(self.service_type)
|
||||
return None
|
||||
except exceptions.AmbiguousEndpoints:
|
||||
print(_("Found more than one valid endpoint. Use a more "
|
||||
@ -553,7 +527,11 @@ class HTTPClient(object):
|
||||
extract_token=False)
|
||||
|
||||
def authenticate(self):
|
||||
magic_tuple = network_utils.urlsplit(self.auth_url)
|
||||
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
|
||||
if port is None:
|
||||
@ -698,13 +676,17 @@ def _construct_http_client(username=None, password=None, project_id=None,
|
||||
auth_system='keystone', auth_plugin=None,
|
||||
auth_token=None, cacert=None, tenant_id=None,
|
||||
user_id=None, connection_pool=False, session=None,
|
||||
auth=None):
|
||||
auth=None, user_agent='python-novaclient',
|
||||
interface=None, **kwargs):
|
||||
if session:
|
||||
return SessionClient(session=session,
|
||||
auth=auth,
|
||||
interface=endpoint_type,
|
||||
interface=interface or endpoint_type,
|
||||
service_type=service_type,
|
||||
region_name=region_name)
|
||||
region_name=region_name,
|
||||
service_name=service_name,
|
||||
user_agent=user_agent,
|
||||
**kwargs)
|
||||
else:
|
||||
# FIXME(jamielennox): username and password are now optional. Need
|
||||
# to test that they were provided in this mode.
|
||||
@ -736,9 +718,9 @@ def _construct_http_client(username=None, password=None, project_id=None,
|
||||
|
||||
def get_client_class(version):
|
||||
version_map = {
|
||||
'1.1': 'novaclient.v1_1.client.Client',
|
||||
'2': 'novaclient.v1_1.client.Client',
|
||||
'3': 'novaclient.v3.client.Client',
|
||||
'1.1': 'novaclient.v2.client.Client',
|
||||
'2': 'novaclient.v2.client.Client',
|
||||
'3': 'novaclient.v2.client.Client',
|
||||
}
|
||||
try:
|
||||
client_path = version_map[str(version)]
|
||||
@ -748,7 +730,7 @@ def get_client_class(version):
|
||||
'keys': ', '.join(version_map.keys())}
|
||||
raise exceptions.UnsupportedVersion(msg)
|
||||
|
||||
return utils.import_class(client_path)
|
||||
return importutils.import_class(client_path)
|
||||
|
||||
|
||||
def Client(version, *args, **kwargs):
|
||||
|
||||
@ -22,7 +22,7 @@ class DecryptionFailure(Exception):
|
||||
|
||||
|
||||
def decrypt_password(private_key, password):
|
||||
"""Base64 decodes password and unecrypts it with private key.
|
||||
"""Base64 decodes password and unencrypts it with private key.
|
||||
|
||||
Requires openssl binary available in the path.
|
||||
"""
|
||||
|
||||
@ -105,6 +105,19 @@ class ClientException(Exception):
|
||||
return formatted_string
|
||||
|
||||
|
||||
class RetryAfterException(ClientException):
|
||||
"""
|
||||
The base exception class for ClientExceptions that use Retry-After header.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
try:
|
||||
self.retry_after = int(kwargs.pop('retry_after'))
|
||||
except (KeyError, ValueError):
|
||||
self.retry_after = 0
|
||||
|
||||
super(RetryAfterException, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class BadRequest(ClientException):
|
||||
"""
|
||||
HTTP 400 - Bad request: you sent some malformed data.
|
||||
@ -154,23 +167,15 @@ class Conflict(ClientException):
|
||||
message = "Conflict"
|
||||
|
||||
|
||||
class OverLimit(ClientException):
|
||||
class OverLimit(RetryAfterException):
|
||||
"""
|
||||
HTTP 413 - Over limit: you're over the API limits for this time period.
|
||||
"""
|
||||
http_status = 413
|
||||
message = "Over limit"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
try:
|
||||
self.retry_after = int(kwargs.pop('retry_after'))
|
||||
except (KeyError, ValueError):
|
||||
self.retry_after = 0
|
||||
|
||||
super(OverLimit, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class RateLimit(OverLimit):
|
||||
class RateLimit(RetryAfterException):
|
||||
"""
|
||||
HTTP 429 - Rate limit: you've sent too many requests for this time period.
|
||||
"""
|
||||
@ -220,6 +225,8 @@ def from_response(response, body, url, method=None):
|
||||
if resp.status_code != 200:
|
||||
raise exception_from_response(resp, rest.text)
|
||||
"""
|
||||
cls = _code_map.get(response.status_code, ClientException)
|
||||
|
||||
kwargs = {
|
||||
'code': response.status_code,
|
||||
'method': method,
|
||||
@ -230,7 +237,8 @@ def from_response(response, body, url, method=None):
|
||||
if response.headers:
|
||||
kwargs['request_id'] = response.headers.get('x-compute-request-id')
|
||||
|
||||
if 'retry-after' in response.headers:
|
||||
if (issubclass(cls, RetryAfterException) and
|
||||
'retry-after' in response.headers):
|
||||
kwargs['retry_after'] = response.headers.get('retry-after')
|
||||
|
||||
if body:
|
||||
@ -245,5 +253,9 @@ def from_response(response, body, url, method=None):
|
||||
kwargs['message'] = message
|
||||
kwargs['details'] = details
|
||||
|
||||
cls = _code_map.get(response.status_code, ClientException)
|
||||
return cls(**kwargs)
|
||||
|
||||
|
||||
class ResourceNotFound(Exception):
|
||||
"""Error in getting the resource."""
|
||||
pass
|
||||
|
||||
@ -14,10 +14,11 @@
|
||||
# under the License.
|
||||
|
||||
from novaclient import base
|
||||
from novaclient.openstack.common.apiclient import base as common_base
|
||||
from novaclient import utils
|
||||
|
||||
|
||||
class Extension(utils.HookableMixin):
|
||||
class Extension(common_base.HookableMixin):
|
||||
"""Extension descriptor."""
|
||||
|
||||
SUPPORTED_HOOKS = ('__pre_parse_args__', '__post_parse_args__')
|
||||
|
||||
35
awx/lib/site-packages/novaclient/i18n.py
Normal file
35
awx/lib/site-packages/novaclient/i18n.py
Normal file
@ -0,0 +1,35 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""oslo.i18n integration module for novaclient.
|
||||
|
||||
See http://docs.openstack.org/developer/oslo.i18n/usage.html .
|
||||
|
||||
"""
|
||||
|
||||
from oslo import i18n
|
||||
|
||||
|
||||
_translators = i18n.TranslatorFactory(domain='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
|
||||
@ -1,17 +0,0 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import six
|
||||
|
||||
|
||||
six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox'))
|
||||
40
awx/lib/site-packages/novaclient/openstack/common/_i18n.py
Normal file
40
awx/lib/site-packages/novaclient/openstack/common/_i18n.py
Normal file
@ -0,0 +1,40 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""oslo.i18n integration module.
|
||||
|
||||
See http://docs.openstack.org/developer/oslo.i18n/usage.html
|
||||
|
||||
"""
|
||||
|
||||
import oslo.i18n
|
||||
|
||||
|
||||
# NOTE(dhellmann): This reference to o-s-l-o will be replaced by the
|
||||
# application name when this module is synced into the separate
|
||||
# repository. It is OK to have more than one translation function
|
||||
# using the same domain, since there will still only be one message
|
||||
# catalog.
|
||||
_translators = oslo.i18n.TranslatorFactory(domain='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
|
||||
@ -26,12 +26,12 @@ 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
|
||||
|
||||
|
||||
def getid(obj):
|
||||
@ -99,12 +99,13 @@ class BaseManager(HookableMixin):
|
||||
super(BaseManager, self).__init__()
|
||||
self.client = client
|
||||
|
||||
def _list(self, url, response_key, obj_class=None, json=None):
|
||||
def _list(self, url, response_key=None, obj_class=None, json=None):
|
||||
"""List the collection.
|
||||
|
||||
:param url: a partial URL, e.g., '/servers'
|
||||
:param response_key: the key to be looked up in response dictionary,
|
||||
e.g., 'servers'
|
||||
e.g., 'servers'. If response_key is None - all response body
|
||||
will be used.
|
||||
: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
|
||||
@ -118,7 +119,7 @@ class BaseManager(HookableMixin):
|
||||
if obj_class is None:
|
||||
obj_class = self.resource_class
|
||||
|
||||
data = body[response_key]
|
||||
data = body[response_key] if response_key is not None else body
|
||||
# NOTE(ja): keystone returns values as list as {'values': [ ... ]}
|
||||
# unlike other services which just return the list...
|
||||
try:
|
||||
@ -128,15 +129,17 @@ class BaseManager(HookableMixin):
|
||||
|
||||
return [obj_class(self, res, loaded=True) for res in data if res]
|
||||
|
||||
def _get(self, url, response_key):
|
||||
def _get(self, url, response_key=None):
|
||||
"""Get an object from collection.
|
||||
|
||||
:param url: a partial URL, e.g., '/servers'
|
||||
:param response_key: the key to be looked up in response dictionary,
|
||||
e.g., 'server'
|
||||
e.g., 'server'. If response_key is None - all response body
|
||||
will be used.
|
||||
"""
|
||||
body = self.client.get(url).json()
|
||||
return self.resource_class(self, body[response_key], loaded=True)
|
||||
data = body[response_key] if response_key is not None else body
|
||||
return self.resource_class(self, data, loaded=True)
|
||||
|
||||
def _head(self, url):
|
||||
"""Retrieve request headers for an object.
|
||||
@ -146,21 +149,23 @@ class BaseManager(HookableMixin):
|
||||
resp = self.client.head(url)
|
||||
return resp.status_code == 204
|
||||
|
||||
def _post(self, url, json, response_key, return_raw=False):
|
||||
def _post(self, url, json, response_key=None, 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., 'servers'
|
||||
e.g., 'server'. If response_key is None - all response body
|
||||
will be used.
|
||||
: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 body[response_key]
|
||||
return self.resource_class(self, body[response_key])
|
||||
return data
|
||||
return self.resource_class(self, data)
|
||||
|
||||
def _put(self, url, json=None, response_key=None):
|
||||
"""Update an object with PUT method.
|
||||
@ -169,7 +174,8 @@ 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'
|
||||
e.g., 'servers'. If response_key is None - all response body
|
||||
will be used.
|
||||
"""
|
||||
resp = self.client.put(url, json=json)
|
||||
# PUT requests may not return a body
|
||||
@ -187,7 +193,8 @@ 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'
|
||||
e.g., 'servers'. If response_key is None - all response body
|
||||
will be used.
|
||||
"""
|
||||
body = self.client.patch(url, json=json).json()
|
||||
if response_key is not None:
|
||||
@ -465,7 +472,7 @@ class Resource(object):
|
||||
|
||||
def __getattr__(self, k):
|
||||
if k not in self.__dict__:
|
||||
#NOTE(bcwaldon): disallow lazy-loading if already loaded once
|
||||
# NOTE(bcwaldon): disallow lazy-loading if already loaded once
|
||||
if not self.is_loaded():
|
||||
self.get()
|
||||
return self.__getattr__(k)
|
||||
|
||||
@ -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 -i",
|
||||
"curl -g -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.get("headers", {}))
|
||||
kwargs.setdefault("headers", {})
|
||||
kwargs["headers"]["User-Agent"] = self.user_agent
|
||||
if self.original_ip:
|
||||
kwargs["headers"]["Forwarded"] = "for=%s;by=%s" % (
|
||||
@ -247,6 +247,10 @@ 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(
|
||||
@ -357,8 +361,7 @@ 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)
|
||||
|
||||
@ -25,7 +25,7 @@ import sys
|
||||
|
||||
import six
|
||||
|
||||
from novaclient.openstack.common.gettextutils import _
|
||||
from novaclient.openstack.common._i18n import _
|
||||
|
||||
|
||||
class ClientException(Exception):
|
||||
@ -34,14 +34,6 @@ 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
|
||||
@ -57,11 +49,6 @@ class CommandError(ClientException):
|
||||
pass
|
||||
|
||||
|
||||
class ResourceNotFound(ClientException):
|
||||
"""Error in getting the resource."""
|
||||
pass
|
||||
|
||||
|
||||
class AuthorizationFailure(ClientException):
|
||||
"""Cannot authorize API client."""
|
||||
pass
|
||||
@ -432,7 +419,7 @@ def from_response(response, method, url):
|
||||
"""
|
||||
|
||||
req_id = response.headers.get("x-openstack-request-id")
|
||||
#NOTE(hdd) true for older versions of nova and cinder
|
||||
# NOTE(hdd) true for older versions of nova and cinder
|
||||
if not req_id:
|
||||
req_id = response.headers.get("x-compute-request-id")
|
||||
kwargs = {
|
||||
@ -452,8 +439,8 @@ def from_response(response, method, url):
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
if isinstance(body, dict):
|
||||
error = list(body.values())[0]
|
||||
if isinstance(body, dict) and isinstance(body.get("error"), dict):
|
||||
error = body["error"]
|
||||
kwargs["message"] = error.get("message")
|
||||
kwargs["details"] = error.get("details")
|
||||
elif content_type.startswith("text/"):
|
||||
|
||||
@ -33,7 +33,9 @@ from six.moves.urllib import parse
|
||||
from novaclient.openstack.common.apiclient import client
|
||||
|
||||
|
||||
def assert_has_keys(dct, required=[], optional=[]):
|
||||
def assert_has_keys(dct, required=None, optional=None):
|
||||
required = required or []
|
||||
optional = optional or []
|
||||
for k in required:
|
||||
try:
|
||||
assert k in dct
|
||||
@ -79,7 +81,7 @@ class FakeHTTPClient(client.HTTPClient):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.callstack = []
|
||||
self.fixtures = kwargs.pop("fixtures", None) or {}
|
||||
if not args and not "auth_plugin" in kwargs:
|
||||
if not args and "auth_plugin" not in kwargs:
|
||||
args = (None, )
|
||||
super(FakeHTTPClient, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
@ -0,0 +1,87 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo.utils import encodeutils
|
||||
import six
|
||||
|
||||
from novaclient.openstack.common._i18n import _
|
||||
from novaclient.openstack.common.apiclient import exceptions
|
||||
from novaclient.openstack.common import uuidutils
|
||||
|
||||
|
||||
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:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
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:
|
||||
if six.PY2:
|
||||
tmp_id = encodeutils.safe_encode(name_or_id)
|
||||
else:
|
||||
tmp_id = encodeutils.safe_decode(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)
|
||||
@ -24,14 +24,21 @@ 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.apiclient import exceptions
|
||||
from novaclient.openstack.common.gettextutils import _
|
||||
from novaclient.openstack.common import strutils
|
||||
from novaclient.openstack.common import uuidutils
|
||||
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)
|
||||
|
||||
|
||||
def validate_args(fn, *args, **kwargs):
|
||||
@ -56,7 +63,7 @@ def validate_args(fn, *args, **kwargs):
|
||||
required_args = argspec.args[:len(argspec.args) - num_defaults]
|
||||
|
||||
def isbound(method):
|
||||
return getattr(method, 'im_self', None) is not None
|
||||
return getattr(method, '__self__', None) is not None
|
||||
|
||||
if isbound(fn):
|
||||
required_args.pop(0)
|
||||
@ -64,7 +71,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 exceptions.MissingArgs(missing)
|
||||
raise MissingArgs(missing)
|
||||
|
||||
|
||||
def arg(*args, **kwargs):
|
||||
@ -132,7 +139,7 @@ def isunauthenticated(func):
|
||||
|
||||
|
||||
def print_list(objs, fields, formatters=None, sortby_index=0,
|
||||
mixed_case_fields=None):
|
||||
mixed_case_fields=None, field_labels=None):
|
||||
"""Print a list or objects as a table, one row per object.
|
||||
|
||||
:param objs: iterable of :class:`Resource`
|
||||
@ -141,14 +148,22 @@ 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': fields[sortby_index]}
|
||||
pt = prettytable.PrettyTable(fields, caching=False)
|
||||
kwargs = {'sortby': field_labels[sortby_index]}
|
||||
pt = prettytable.PrettyTable(field_labels)
|
||||
pt.align = 'l'
|
||||
|
||||
for o in objs:
|
||||
@ -165,7 +180,7 @@ def print_list(objs, fields, formatters=None, sortby_index=0,
|
||||
row.append(data)
|
||||
pt.add_row(row)
|
||||
|
||||
print(strutils.safe_encode(pt.get_string(**kwargs)))
|
||||
print(encodeutils.safe_encode(pt.get_string(**kwargs)))
|
||||
|
||||
|
||||
def print_dict(dct, dict_property="Property", wrap=0):
|
||||
@ -175,7 +190,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'], caching=False)
|
||||
pt = prettytable.PrettyTable([dict_property, 'Value'])
|
||||
pt.align = 'l'
|
||||
for k, v in six.iteritems(dct):
|
||||
# convert dict to str to check length
|
||||
@ -193,7 +208,7 @@ def print_dict(dct, dict_property="Property", wrap=0):
|
||||
col1 = ''
|
||||
else:
|
||||
pt.add_row([k, v])
|
||||
print(strutils.safe_encode(pt.get_string()))
|
||||
print(encodeutils.safe_encode(pt.get_string()))
|
||||
|
||||
|
||||
def get_password(max_password_prompts=3):
|
||||
@ -217,76 +232,16 @@ 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:
|
||||
@service_type('volume')
|
||||
def mymethod(f):
|
||||
...
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@service_type('volume')
|
||||
def mymethod(f):
|
||||
...
|
||||
"""
|
||||
def inner(f):
|
||||
f.service_type = stype
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -28,7 +28,14 @@ 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
|
||||
import six
|
||||
|
||||
@ -45,14 +52,12 @@ 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.openstack.common import strutils
|
||||
from novaclient import utils
|
||||
from novaclient.v1_1 import shell as shell_v1_1
|
||||
from novaclient.v3 import shell as shell_v3
|
||||
from novaclient.v2 import shell as shell_v2
|
||||
|
||||
DEFAULT_OS_COMPUTE_API_VERSION = "1.1"
|
||||
DEFAULT_OS_COMPUTE_API_VERSION = "2"
|
||||
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
|
||||
@ -134,7 +139,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]):
|
||||
@ -154,7 +159,7 @@ class SecretsHelper(object):
|
||||
self._password = self.args.os_password
|
||||
else:
|
||||
verify_pass = strutils.bool_from_string(
|
||||
utils.env("OS_VERIFY_PASSWORD", default=False), True)
|
||||
cliutils.env("OS_VERIFY_PASSWORD", default=False), True)
|
||||
self._password = self._prompt_password(verify_pass)
|
||||
if not self._password:
|
||||
raise exc.CommandError(
|
||||
@ -219,17 +224,51 @@ class NovaClientArgumentParser(argparse.ArgumentParser):
|
||||
exits.
|
||||
"""
|
||||
self.print_usage(sys.stderr)
|
||||
#FIXME(lzyeval): if changes occur in argparse.ArgParser._check_value
|
||||
# FIXME(lzyeval): if changes occur in argparse.ArgParser._check_value
|
||||
choose_from = ' (choose from'
|
||||
progparts = self.prog.partition(' ')
|
||||
self.exit(2, _("error: %(errmsg)s\nTry '%(mainp)s help %(subp)s'"
|
||||
" for more information.\n") %
|
||||
{'errmsg': message.split(choose_from)[0],
|
||||
'mainp': progparts[0],
|
||||
'subp': progparts[2]})
|
||||
" for more information.\n") %
|
||||
{'errmsg': message.split(choose_from)[0],
|
||||
'mainp': progparts[0],
|
||||
'subp': progparts[2]})
|
||||
|
||||
def _get_option_tuples(self, option_string):
|
||||
"""returns (action, option, value) candidates for an option prefix
|
||||
|
||||
Returns [first candidate] if all candidates refers to current and
|
||||
deprecated forms of the same options: "nova boot ... --key KEY"
|
||||
parsing succeed because --key could only match --key-name,
|
||||
--key_name which are current/deprecated forms of the same option.
|
||||
"""
|
||||
option_tuples = (super(NovaClientArgumentParser, self)
|
||||
._get_option_tuples(option_string))
|
||||
if len(option_tuples) > 1:
|
||||
normalizeds = [option.replace('_', '-')
|
||||
for action, option, value in option_tuples]
|
||||
if len(set(normalizeds)) == 1:
|
||||
return option_tuples[:1]
|
||||
return option_tuples
|
||||
|
||||
|
||||
class OpenStackComputeShell(object):
|
||||
times = []
|
||||
|
||||
def _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(
|
||||
@ -242,7 +281,8 @@ class OpenStackComputeShell(object):
|
||||
)
|
||||
|
||||
# Global arguments
|
||||
parser.add_argument('-h', '--help',
|
||||
parser.add_argument(
|
||||
'-h', '--help',
|
||||
action='store_true',
|
||||
help=argparse.SUPPRESS,
|
||||
)
|
||||
@ -251,154 +291,151 @@ 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(
|
||||
utils.env('OS_CACHE', default=False), True),
|
||||
cliutils.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('--timeout',
|
||||
default=600,
|
||||
metavar='<seconds>',
|
||||
type=positive_non_zero_float,
|
||||
help=_("Set HTTP call timeout (in seconds)"))
|
||||
parser.add_argument(
|
||||
'--os-auth-token',
|
||||
default=cliutils.env('OS_AUTH_TOKEN'),
|
||||
help='Defaults to env[OS_AUTH_TOKEN]')
|
||||
|
||||
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',
|
||||
parser.add_argument(
|
||||
'--os_username',
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
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',
|
||||
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=utils.env('OS_TENANT_NAME', 'NOVA_PROJECT_ID'),
|
||||
default=cliutils.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=utils.env('OS_TENANT_ID'),
|
||||
default=cliutils.env('OS_TENANT_ID'),
|
||||
help=_('Defaults to env[OS_TENANT_ID].'))
|
||||
|
||||
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',
|
||||
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=utils.env('OS_REGION_NAME', 'NOVA_REGION_NAME'),
|
||||
default=cliutils.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=utils.env('OS_AUTH_SYSTEM'),
|
||||
default=cliutils.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=utils.env('NOVA_SERVICE_NAME'),
|
||||
default=cliutils.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=utils.env('NOVA_VOLUME_SERVICE_NAME'),
|
||||
default=cliutils.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('--endpoint-type',
|
||||
parser.add_argument(
|
||||
'--os-endpoint-type',
|
||||
metavar='<endpoint-type>',
|
||||
default=utils.env('NOVA_ENDPOINT_TYPE',
|
||||
default=DEFAULT_NOVA_ENDPOINT_TYPE),
|
||||
help=_('Defaults to env[NOVA_ENDPOINT_TYPE] or ')
|
||||
+ DEFAULT_NOVA_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)
|
||||
# NOTE(dtroyer): We can't add --endpoint_type here due to argparse
|
||||
# thinking usage-list --end is ambiguous; but it
|
||||
# works fine with only --endpoint-type present
|
||||
# Go figure. I'm leaving this here for doc purposes.
|
||||
#parser.add_argument('--endpoint_type',
|
||||
# help=argparse.SUPPRESS)
|
||||
# parser.add_argument('--endpoint_type',
|
||||
# help=argparse.SUPPRESS)
|
||||
|
||||
parser.add_argument('--os-compute-api-version',
|
||||
parser.add_argument(
|
||||
'--os-compute-api-version',
|
||||
metavar='<compute-api-ver>',
|
||||
default=utils.env('OS_COMPUTE_API_VERSION',
|
||||
default=DEFAULT_OS_COMPUTE_API_VERSION),
|
||||
default=cliutils.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('--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',
|
||||
parser.add_argument(
|
||||
'--bypass-url',
|
||||
metavar='<bypass-url>',
|
||||
dest='bypass_url',
|
||||
default=utils.env('NOVACLIENT_BYPASS_URL'),
|
||||
default=cliutils.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):
|
||||
@ -409,12 +446,12 @@ class OpenStackComputeShell(object):
|
||||
|
||||
try:
|
||||
actions_module = {
|
||||
'1.1': shell_v1_1,
|
||||
'2': shell_v1_1,
|
||||
'3': shell_v3,
|
||||
'1.1': shell_v2,
|
||||
'2': shell_v2,
|
||||
'3': shell_v2,
|
||||
}[version]
|
||||
except KeyError:
|
||||
actions_module = shell_v1_1
|
||||
actions_module = shell_v2
|
||||
|
||||
self._find_actions(subparsers, actions_module)
|
||||
self._find_actions(subparsers, self)
|
||||
@ -454,6 +491,10 @@ 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")
|
||||
|
||||
@ -474,7 +515,8 @@ 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
|
||||
)
|
||||
@ -490,13 +532,14 @@ 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,
|
||||
)
|
||||
@ -515,6 +558,20 @@ 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()
|
||||
@ -526,7 +583,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
|
||||
@ -538,7 +595,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:
|
||||
@ -574,6 +631,9 @@ 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.
|
||||
@ -591,6 +651,11 @@ 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)
|
||||
@ -600,14 +665,21 @@ class OpenStackComputeShell(object):
|
||||
except KeyError:
|
||||
service_type = DEFAULT_NOVA_SERVICE_TYPE_MAP[
|
||||
DEFAULT_OS_COMPUTE_API_VERSION]
|
||||
service_type = utils.get_service_type(args.func) or service_type
|
||||
service_type = cliutils.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))
|
||||
|
||||
#FIXME(usrleon): Here should be restrict for project id same as
|
||||
# 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:
|
||||
if auth_plugin:
|
||||
@ -615,54 +687,86 @@ 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 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 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_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]"))
|
||||
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))
|
||||
|
||||
if (options.os_compute_api_version and
|
||||
options.os_compute_api_version != '1.0'):
|
||||
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 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_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]"))
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
# Now check for the password/token of which pieces of the
|
||||
# identifying keyring key can come from the underlying client
|
||||
@ -695,13 +799,17 @@ 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):
|
||||
self.cs.authenticate()
|
||||
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()
|
||||
except exc.Unauthorized:
|
||||
raise exc.CommandError(_("Invalid OpenStack Nova credentials."))
|
||||
except exc.AuthorizationFailure:
|
||||
raise exc.CommandError(_("Unable to authorize user"))
|
||||
|
||||
if os_compute_api_version == "3" and service_type != 'image':
|
||||
if options.os_compute_api_version == "3" and service_type != 'image':
|
||||
# NOTE(cyeoh): create an image based client because the
|
||||
# images api is no longer proxied by the V3 API and we
|
||||
# sometimes need to be able to look up images information
|
||||
@ -724,12 +832,13 @@ 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.cs.get_timings())
|
||||
self._dump_timings(self.times + self.cs.get_timings())
|
||||
|
||||
def _dump_timings(self, timings):
|
||||
class Tyme(object):
|
||||
@ -764,8 +873,11 @@ class OpenStackComputeShell(object):
|
||||
commands.remove('bash_completion')
|
||||
print(' '.join(commands | options))
|
||||
|
||||
@utils.arg('command', metavar='<subcommand>', nargs='?',
|
||||
help='Display help for <subcommand>')
|
||||
@cliutils.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.
|
||||
@ -785,7 +897,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
|
||||
@ -795,19 +907,19 @@ class OpenStackHelpFormatter(argparse.HelpFormatter):
|
||||
|
||||
def main():
|
||||
try:
|
||||
argv = [strutils.safe_decode(a) for a in sys.argv[1:]]
|
||||
argv = [encodeutils.safe_decode(a) for a in sys.argv[1:]]
|
||||
OpenStackComputeShell().main(argv)
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(e, exc_info=1)
|
||||
details = {'name': strutils.safe_encode(e.__class__.__name__),
|
||||
'msg': strutils.safe_encode(six.text_type(e))}
|
||||
details = {'name': encodeutils.safe_encode(e.__class__.__name__),
|
||||
'msg': encodeutils.safe_encode(six.text_type(e))}
|
||||
print("ERROR (%(name)s): %(msg)s" % details,
|
||||
file=sys.stderr)
|
||||
sys.exit(1)
|
||||
except KeyboardInterrupt as e:
|
||||
print("Shutting down novaclient", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
except KeyboardInterrupt:
|
||||
print("... terminating nova client", file=sys.stderr)
|
||||
sys.exit(130)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
50
awx/lib/site-packages/novaclient/tests/functional/README.rst
Normal file
50
awx/lib/site-packages/novaclient/tests/functional/README.rst
Normal file
@ -0,0 +1,50 @@
|
||||
=====================================
|
||||
python-novaclient functional testing
|
||||
=====================================
|
||||
|
||||
Idea
|
||||
------
|
||||
|
||||
Over time we have noticed two issues with novaclient unit tests.
|
||||
|
||||
* Does not exercise the CLI
|
||||
* We can get the expected server behavior wrong, and test the wrong thing.
|
||||
|
||||
We are using functional tests, run against a running cloud
|
||||
(primarily devstack), to address these two cases.
|
||||
|
||||
Additionally these functional tests can be considered example uses
|
||||
of python-novaclient.
|
||||
|
||||
These tests started out in tempest as read only nova CLI tests, to make sure
|
||||
the CLI didn't simply stacktrace when being used (which happened on
|
||||
multiple occasions).
|
||||
|
||||
|
||||
Testing Theory
|
||||
----------------
|
||||
|
||||
We are treating python-novaclient as legacy code, so we do not want to spend a
|
||||
lot of effort adding in missing features. In the future the CLI will move to
|
||||
python-openstackclient, and the python API will be based on the OpenStack
|
||||
SDK project. But until that happens we still need better functional testing,
|
||||
to prevent regressions etc.
|
||||
|
||||
|
||||
Since python-novaclient has two uses, CLI and python API, we should have two
|
||||
sets of functional tests. CLI and python API. The python API tests should
|
||||
never use the CLI. But the CLI tests can use the python API where adding
|
||||
native support to the CLI for the required functionality would involve a
|
||||
non trivial amount of work.
|
||||
|
||||
Functional Test Guidelines
|
||||
---------------------------
|
||||
|
||||
* Consume credentials via standard client environmental variables::
|
||||
|
||||
OS_USERNAME
|
||||
OS_PASSWORD
|
||||
OS_TENANT_NAME
|
||||
OS_AUTH_URL
|
||||
|
||||
* Try not to require an additional configuration file
|
||||
44
awx/lib/site-packages/novaclient/tests/functional/base.py
Normal file
44
awx/lib/site-packages/novaclient/tests/functional/base.py
Normal file
@ -0,0 +1,44 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
|
||||
from tempest_lib.cli import base
|
||||
|
||||
|
||||
class ClientTestBase(base.ClientTestBase):
|
||||
"""
|
||||
This is a first pass at a simple read only python-novaclient test. This
|
||||
only exercises client commands that are read only.
|
||||
|
||||
This should test commands:
|
||||
* as a regular user
|
||||
* as a admin user
|
||||
* with and without optional parameters
|
||||
* initially just check return codes, and later test command outputs
|
||||
|
||||
"""
|
||||
def _get_clients(self):
|
||||
cli_dir = os.environ.get(
|
||||
'OS_NOVACLIENT_EXEC_DIR',
|
||||
os.path.join(os.path.abspath('.'), '.tox/functional/bin'))
|
||||
|
||||
return base.CLIClient(
|
||||
username=os.environ.get('OS_USERNAME'),
|
||||
password=os.environ.get('OS_PASSWORD'),
|
||||
tenant_name=os.environ.get('OS_TENANT_NAME'),
|
||||
uri=os.environ.get('OS_AUTH_URL'),
|
||||
cli_dir=cli_dir)
|
||||
|
||||
def nova(self, *args, **kwargs):
|
||||
return self.clients.nova(*args,
|
||||
**kwargs)
|
||||
50
awx/lib/site-packages/novaclient/tests/functional/hooks/post_test_hook.sh
Executable file
50
awx/lib/site-packages/novaclient/tests/functional/hooks/post_test_hook.sh
Executable file
@ -0,0 +1,50 @@
|
||||
#!/bin/bash -xe
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# This script is executed inside post_test_hook function in devstack gate.
|
||||
|
||||
function generate_testr_results {
|
||||
if [ -f .testrepository/0 ]; then
|
||||
sudo .tox/functional/bin/testr last --subunit > $WORKSPACE/testrepository.subunit
|
||||
sudo mv $WORKSPACE/testrepository.subunit $BASE/logs/testrepository.subunit
|
||||
sudo .tox/functional/bin/python /usr/local/jenkins/slave_scripts/subunit2html.py $BASE/logs/testrepository.subunit $BASE/logs/testr_results.html
|
||||
sudo gzip -9 $BASE/logs/testrepository.subunit
|
||||
sudo gzip -9 $BASE/logs/testr_results.html
|
||||
sudo chown jenkins:jenkins $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz
|
||||
sudo chmod a+r $BASE/logs/testrepository.subunit.gz $BASE/logs/testr_results.html.gz
|
||||
fi
|
||||
}
|
||||
|
||||
export NOVACLIENT_DIR="$BASE/new/python-novaclient"
|
||||
|
||||
# Get admin credentials
|
||||
cd $BASE/new/devstack
|
||||
source openrc admin admin
|
||||
|
||||
# Go to the novaclient dir
|
||||
cd $NOVACLIENT_DIR
|
||||
|
||||
sudo chown -R jenkins:stack $NOVACLIENT_DIR
|
||||
|
||||
# Run tests
|
||||
echo "Running novaclient functional test suite"
|
||||
set +e
|
||||
# Preserve env for OS_ credentials
|
||||
sudo -E -H -u jenkins tox -efunctional
|
||||
EXIT_CODE=$?
|
||||
set -e
|
||||
|
||||
# Collect and parse result
|
||||
generate_testr_results
|
||||
exit $EXIT_CODE
|
||||
@ -0,0 +1,155 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
|
||||
import novaclient.client
|
||||
from novaclient.tests.functional import base
|
||||
|
||||
|
||||
# TODO(sdague): content that probably should be in utils, also throw
|
||||
# Exceptions when they fail.
|
||||
def pick_flavor(flavors):
|
||||
"""Given a flavor list pick a reasonable one."""
|
||||
for flavor in flavors:
|
||||
if flavor.name == 'm1.tiny':
|
||||
return flavor
|
||||
|
||||
for flavor in flavors:
|
||||
if flavor.name == 'm1.small':
|
||||
return flavor
|
||||
|
||||
|
||||
def pick_image(images):
|
||||
for image in images:
|
||||
if image.name.startswith('cirros') and image.name.endswith('-uec'):
|
||||
return image
|
||||
|
||||
|
||||
def volume_id_from_cli_create(output):
|
||||
"""Scrape the volume id out of the 'volume create' command
|
||||
|
||||
The cli for Nova automatically routes requests to the volumes
|
||||
service end point. However the nova api low level commands don't
|
||||
redirect to the correct service endpoint, so for volumes commands
|
||||
(even setup ones) we use the cli for magic routing.
|
||||
|
||||
This function lets us get the id out of the prettytable that's
|
||||
dumped on the cli during create.
|
||||
|
||||
"""
|
||||
for line in output.split("\n"):
|
||||
fields = line.split()
|
||||
if len(fields) > 4:
|
||||
if fields[1] == "id":
|
||||
return fields[3]
|
||||
|
||||
|
||||
def volume_at_status(output, volume_id, status):
|
||||
for line in output.split("\n"):
|
||||
fields = line.split()
|
||||
if len(fields) > 4:
|
||||
if fields[1] == volume_id:
|
||||
return fields[3] == status
|
||||
raise Exception("Volume %s did not reach status '%s' in output: %s"
|
||||
% (volume_id, status, output))
|
||||
|
||||
|
||||
class TestInstanceCLI(base.ClientTestBase):
|
||||
def setUp(self):
|
||||
super(TestInstanceCLI, self).setUp()
|
||||
# TODO(sdague): while we collect this information in
|
||||
# tempest-lib, we do it in a way that's not available for top
|
||||
# level tests. Long term this probably needs to be in the base
|
||||
# class.
|
||||
user = os.environ['OS_USERNAME']
|
||||
passwd = os.environ['OS_PASSWORD']
|
||||
tenant = os.environ['OS_TENANT_NAME']
|
||||
auth_url = os.environ['OS_AUTH_URL']
|
||||
|
||||
# TODO(sdague): we made a lot of fun of the glanceclient team
|
||||
# for version as int in first parameter. I guess we know where
|
||||
# they copied it from.
|
||||
self.client = novaclient.client.Client(
|
||||
2, user, passwd, tenant,
|
||||
auth_url=auth_url)
|
||||
|
||||
# pick some reasonable flavor / image combo
|
||||
self.flavor = pick_flavor(self.client.flavors.list())
|
||||
self.image = pick_image(self.client.images.list())
|
||||
|
||||
def test_attach_volume(self):
|
||||
"""Test we can attach a volume via the cli.
|
||||
|
||||
This test was added after bug 1423695. That bug exposed
|
||||
inconsistencies in how to talk to API services from the CLI
|
||||
vs. API level. The volumes api calls that were designed to
|
||||
populate the completion cache were incorrectly routed to the
|
||||
Nova endpoint. Novaclient volumes support actually talks to
|
||||
Cinder endpoint directly.
|
||||
|
||||
This would case volume-attach to return a bad error code,
|
||||
however it does this *after* the attach command is correctly
|
||||
dispatched. So the volume-attach still works, but the user is
|
||||
presented a 404 error.
|
||||
|
||||
This test ensures we can do a through path test of: boot,
|
||||
create volume, attach volume, detach volume, delete volume,
|
||||
destroy.
|
||||
|
||||
"""
|
||||
# TODO(sdague): better random name
|
||||
name = str(uuid.uuid4())
|
||||
|
||||
# Boot via the cli, as we're primarily testing the cli in this test
|
||||
self.nova('boot', params="--flavor %s --image %s %s --poll" %
|
||||
(self.flavor.name, self.image.name, name))
|
||||
|
||||
# Be nice about cleaning up, however, use the API for this to avoid
|
||||
# parsing text.
|
||||
servers = self.client.servers.list(search_opts={"name": name})
|
||||
# the name is a random uuid, there better only be one
|
||||
self.assertEqual(1, len(servers), servers)
|
||||
server = servers[0]
|
||||
self.addCleanup(server.delete)
|
||||
|
||||
# create a volume for attachment. We use the CLI because it
|
||||
# magic routes to cinder, however the low level API does not.
|
||||
volume_id = volume_id_from_cli_create(
|
||||
self.nova('volume-create', params="1"))
|
||||
self.addCleanup(self.nova, 'volume-delete', params=volume_id)
|
||||
|
||||
# allow volume to become available
|
||||
for x in xrange(60):
|
||||
volumes = self.nova('volume-list')
|
||||
if volume_at_status(volumes, volume_id, 'available'):
|
||||
break
|
||||
time.sleep(1)
|
||||
else:
|
||||
self.fail("Volume %s not available after 60s" % volume_id)
|
||||
|
||||
# attach the volume
|
||||
self.nova('volume-attach', params="%s %s" % (name, volume_id))
|
||||
|
||||
# volume needs to transition to 'in-use' to be attached
|
||||
for x in xrange(60):
|
||||
volumes = self.nova('volume-list')
|
||||
if volume_at_status(volumes, volume_id, 'in-use'):
|
||||
break
|
||||
time.sleep(1)
|
||||
else:
|
||||
self.fail("Volume %s not attached after 60s" % volume_id)
|
||||
|
||||
# clean up on success
|
||||
self.nova('volume-detach', params="%s %s" % (name, volume_id))
|
||||
@ -0,0 +1,171 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from tempest_lib import decorators
|
||||
from tempest_lib import exceptions
|
||||
|
||||
from novaclient.tests.functional import base
|
||||
|
||||
|
||||
class SimpleReadOnlyNovaClientTest(base.ClientTestBase):
|
||||
|
||||
"""
|
||||
read only functional python-novaclient tests.
|
||||
|
||||
This only exercises client commands that are read only.
|
||||
"""
|
||||
|
||||
def test_admin_fake_action(self):
|
||||
self.assertRaises(exceptions.CommandFailed,
|
||||
self.nova,
|
||||
'this-does-nova-exist')
|
||||
|
||||
# NOTE(jogo): Commands in order listed in 'nova help'
|
||||
|
||||
def test_admin_absolute_limites(self):
|
||||
self.nova('absolute-limits')
|
||||
self.nova('absolute-limits', params='--reserved')
|
||||
|
||||
def test_admin_aggregate_list(self):
|
||||
self.nova('aggregate-list')
|
||||
|
||||
def test_admin_availability_zone_list(self):
|
||||
self.assertIn("internal", self.nova('availability-zone-list'))
|
||||
|
||||
def test_admin_cloudpipe_list(self):
|
||||
self.nova('cloudpipe-list')
|
||||
|
||||
def test_admin_credentials(self):
|
||||
self.nova('credentials')
|
||||
|
||||
# "Neutron does not provide this feature"
|
||||
def test_admin_dns_domains(self):
|
||||
self.nova('dns-domains')
|
||||
|
||||
@decorators.skip_because(bug="1157349")
|
||||
def test_admin_dns_list(self):
|
||||
self.nova('dns-list')
|
||||
|
||||
def test_admin_endpoints(self):
|
||||
self.nova('endpoints')
|
||||
|
||||
def test_admin_flavor_acces_list(self):
|
||||
self.assertRaises(exceptions.CommandFailed,
|
||||
self.nova,
|
||||
'flavor-access-list')
|
||||
# Failed to get access list for public flavor type
|
||||
self.assertRaises(exceptions.CommandFailed,
|
||||
self.nova,
|
||||
'flavor-access-list',
|
||||
params='--flavor m1.tiny')
|
||||
|
||||
def test_admin_flavor_list(self):
|
||||
self.assertIn("Memory_MB", self.nova('flavor-list'))
|
||||
|
||||
def test_admin_floating_ip_bulk_list(self):
|
||||
self.nova('floating-ip-bulk-list')
|
||||
|
||||
def test_admin_floating_ip_list(self):
|
||||
self.nova('floating-ip-list')
|
||||
|
||||
def test_admin_floating_ip_pool_list(self):
|
||||
self.nova('floating-ip-pool-list')
|
||||
|
||||
def test_admin_host_list(self):
|
||||
self.nova('host-list')
|
||||
|
||||
def test_admin_hypervisor_list(self):
|
||||
self.nova('hypervisor-list')
|
||||
|
||||
def test_admin_image_list(self):
|
||||
self.nova('image-list')
|
||||
|
||||
@decorators.skip_because(bug="1157349")
|
||||
def test_admin_interface_list(self):
|
||||
self.nova('interface-list')
|
||||
|
||||
def test_admin_keypair_list(self):
|
||||
self.nova('keypair-list')
|
||||
|
||||
def test_admin_list(self):
|
||||
self.nova('list')
|
||||
self.nova('list', params='--all-tenants 1')
|
||||
self.nova('list', params='--all-tenants 0')
|
||||
self.assertRaises(exceptions.CommandFailed,
|
||||
self.nova,
|
||||
'list',
|
||||
params='--all-tenants bad')
|
||||
|
||||
def test_admin_network_list(self):
|
||||
self.nova('network-list')
|
||||
|
||||
def test_admin_rate_limits(self):
|
||||
self.nova('rate-limits')
|
||||
|
||||
def test_admin_secgroup_list(self):
|
||||
self.nova('secgroup-list')
|
||||
|
||||
@decorators.skip_because(bug="1157349")
|
||||
def test_admin_secgroup_list_rules(self):
|
||||
self.nova('secgroup-list-rules')
|
||||
|
||||
def test_admin_server_group_list(self):
|
||||
self.nova('server-group-list')
|
||||
|
||||
def test_admin_servce_list(self):
|
||||
self.nova('service-list')
|
||||
|
||||
def test_admin_usage(self):
|
||||
self.nova('usage')
|
||||
|
||||
def test_admin_usage_list(self):
|
||||
self.nova('usage-list')
|
||||
|
||||
def test_admin_volume_list(self):
|
||||
self.nova('volume-list')
|
||||
|
||||
def test_admin_volume_snapshot_list(self):
|
||||
self.nova('volume-snapshot-list')
|
||||
|
||||
def test_admin_volume_type_list(self):
|
||||
self.nova('volume-type-list')
|
||||
|
||||
def test_admin_help(self):
|
||||
self.nova('help')
|
||||
|
||||
def test_admin_list_extensions(self):
|
||||
self.nova('list-extensions')
|
||||
|
||||
def test_admin_net_list(self):
|
||||
self.nova('net-list')
|
||||
|
||||
def test_agent_list(self):
|
||||
self.nova('agent-list')
|
||||
self.nova('agent-list', flags='--debug')
|
||||
|
||||
def test_migration_list(self):
|
||||
self.nova('migration-list')
|
||||
self.nova('migration-list', flags='--debug')
|
||||
|
||||
# Optional arguments:
|
||||
|
||||
def test_admin_version(self):
|
||||
self.nova('', flags='--version')
|
||||
|
||||
def test_admin_debug_list(self):
|
||||
self.nova('list', flags='--debug')
|
||||
|
||||
def test_admin_timeout(self):
|
||||
self.nova('list', flags='--timeout %d' % 10)
|
||||
|
||||
def test_admin_timing(self):
|
||||
self.nova('list', flags='--timing')
|
||||
@ -0,0 +1,111 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
|
||||
import six.moves
|
||||
|
||||
from novaclient import client
|
||||
from novaclient import exceptions
|
||||
from novaclient.tests.functional import base
|
||||
|
||||
|
||||
def wait_for_delete(test, name, thing, get_func):
|
||||
thing.delete()
|
||||
for x in six.moves.range(60):
|
||||
try:
|
||||
thing = get_func(thing.id)
|
||||
except exceptions.NotFound:
|
||||
break
|
||||
time.sleep(1)
|
||||
else:
|
||||
test.fail('%s %s still not deleted after 60s' % (name, thing.id))
|
||||
|
||||
|
||||
class TestVolumesAPI(base.ClientTestBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestVolumesAPI, self).setUp()
|
||||
user = os.environ['OS_USERNAME']
|
||||
passwd = os.environ['OS_PASSWORD']
|
||||
tenant = os.environ['OS_TENANT_NAME']
|
||||
auth_url = os.environ['OS_AUTH_URL']
|
||||
|
||||
self.client = client.Client(2, user, passwd, tenant, auth_url=auth_url)
|
||||
|
||||
def test_volumes_snapshots_types_create_get_list_delete(self):
|
||||
# Create a volume
|
||||
volume = self.client.volumes.create(1)
|
||||
|
||||
# Make sure we can still list servers after using the volume endpoint
|
||||
self.client.servers.list()
|
||||
|
||||
# This cleanup tests volume delete
|
||||
self.addCleanup(volume.delete)
|
||||
|
||||
# Wait for the volume to become available
|
||||
for x in six.moves.range(60):
|
||||
volume = self.client.volumes.get(volume.id)
|
||||
if volume.status == 'available':
|
||||
break
|
||||
elif volume.status == 'error':
|
||||
self.fail('Volume %s is in error state' % volume.id)
|
||||
time.sleep(1)
|
||||
else:
|
||||
self.fail('Volume %s not available after 60s' % volume.id)
|
||||
|
||||
# List all volumes
|
||||
self.client.volumes.list()
|
||||
|
||||
# Create a volume snapshot
|
||||
snapshot = self.client.volume_snapshots.create(volume.id)
|
||||
|
||||
# This cleanup tests volume snapshot delete. The volume
|
||||
# can't be deleted until the dependent snapshot is gone
|
||||
self.addCleanup(wait_for_delete, self, 'Snapshot', snapshot,
|
||||
self.client.volume_snapshots.get)
|
||||
|
||||
# Wait for the snapshot to become available
|
||||
for x in six.moves.range(60):
|
||||
snapshot = self.client.volume_snapshots.get(snapshot.id)
|
||||
if snapshot.status == 'available':
|
||||
break
|
||||
elif snapshot.status == 'error':
|
||||
self.fail('Snapshot %s is in error state' % snapshot.id)
|
||||
time.sleep(1)
|
||||
else:
|
||||
self.fail('Snapshot %s not available after 60s' % snapshot.id)
|
||||
|
||||
# List snapshots
|
||||
self.client.volume_snapshots.list()
|
||||
|
||||
# List servers again to make sure things are still good
|
||||
self.client.servers.list()
|
||||
|
||||
# Create a volume type
|
||||
# TODO(melwitt): Use a better random name
|
||||
name = str(uuid.uuid4())
|
||||
volume_type = self.client.volume_types.create(name)
|
||||
|
||||
# This cleanup tests volume type delete
|
||||
self.addCleanup(self.client.volume_types.delete, volume_type.id)
|
||||
|
||||
# Get the volume type
|
||||
volume_type = self.client.volume_types.get(volume_type.id)
|
||||
|
||||
# List all volume types
|
||||
self.client.volume_types.list()
|
||||
|
||||
# One more servers list
|
||||
self.client.servers.list()
|
||||
92
awx/lib/site-packages/novaclient/tests/unit/fakes.py
Normal file
92
awx/lib/site-packages/novaclient/tests/unit/fakes.py
Normal file
@ -0,0 +1,92 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
A fake server that "responds" to API methods with pre-canned responses.
|
||||
|
||||
All of these responses come from the spec, so if for some reason the spec's
|
||||
wrong the tests might raise AssertionError. I've indicated in comments the
|
||||
places where actual behavior differs from the spec.
|
||||
"""
|
||||
|
||||
from novaclient import base
|
||||
|
||||
|
||||
def assert_has_keys(dict, required=[], optional=[]):
|
||||
keys = dict.keys()
|
||||
for k in required:
|
||||
try:
|
||||
assert k in keys
|
||||
except AssertionError:
|
||||
extra_keys = set(keys).difference(set(required + optional))
|
||||
raise AssertionError("found unexpected keys: %s" %
|
||||
list(extra_keys))
|
||||
|
||||
|
||||
class FakeClient(object):
|
||||
|
||||
def assert_called(self, method, url, body=None, pos=-1):
|
||||
"""
|
||||
Assert than an API method was just called.
|
||||
"""
|
||||
expected = (method, url)
|
||||
called = self.client.callstack[pos][0:2]
|
||||
|
||||
assert self.client.callstack, \
|
||||
"Expected %s %s but no calls were made." % expected
|
||||
|
||||
assert expected == called, \
|
||||
'Expected %s %s; got %s %s' % (expected + called)
|
||||
|
||||
if body is not None:
|
||||
if self.client.callstack[pos][2] != body:
|
||||
raise AssertionError('%r != %r' %
|
||||
(self.client.callstack[pos][2], body))
|
||||
|
||||
def assert_called_anytime(self, method, url, body=None):
|
||||
"""
|
||||
Assert than an API method was called anytime in the test.
|
||||
"""
|
||||
expected = (method, url)
|
||||
|
||||
assert self.client.callstack, \
|
||||
"Expected %s %s but no calls were made." % expected
|
||||
|
||||
found = False
|
||||
for entry in self.client.callstack:
|
||||
if expected == entry[0:2]:
|
||||
found = True
|
||||
break
|
||||
|
||||
assert found, 'Expected %s; got %s' % (expected, self.client.callstack)
|
||||
if body is not None:
|
||||
try:
|
||||
assert entry[2] == body
|
||||
except AssertionError:
|
||||
print(entry[2])
|
||||
print("!=")
|
||||
print(body)
|
||||
raise
|
||||
|
||||
self.client.callstack = []
|
||||
|
||||
def clear_callstack(self):
|
||||
self.client.callstack = []
|
||||
|
||||
def authenticate(self):
|
||||
pass
|
||||
|
||||
|
||||
# Fake class that will be used as an extension
|
||||
class FakeManager(base.Manager):
|
||||
pass
|
||||
@ -0,0 +1,54 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class Fixture(base.Fixture):
|
||||
|
||||
base_url = 'os-agents'
|
||||
|
||||
def setUp(self):
|
||||
super(Fixture, self).setUp()
|
||||
|
||||
post_os_agents = {
|
||||
'agent': {
|
||||
'url': '/xxx/xxx/xxx',
|
||||
'hypervisor': 'kvm',
|
||||
'md5hash': 'add6bb58e139be103324d04d82d8f546',
|
||||
'version': '7.0',
|
||||
'architecture': 'x86',
|
||||
'os': 'win',
|
||||
'id': 1
|
||||
}
|
||||
}
|
||||
|
||||
self.requests.register_uri('POST', self.url(),
|
||||
json=post_os_agents,
|
||||
headers=self.json_headers)
|
||||
|
||||
put_os_agents_1 = {
|
||||
"agent": {
|
||||
"url": "/yyy/yyyy/yyyy",
|
||||
"version": "8.0",
|
||||
"md5hash": "add6bb58e139be103324d04d82d8f546",
|
||||
'id': 1
|
||||
}
|
||||
}
|
||||
|
||||
self.requests.register_uri('PUT', self.url(1),
|
||||
json=put_os_agents_1,
|
||||
headers=self.json_headers)
|
||||
|
||||
self.requests.register_uri('DELETE', self.url(1),
|
||||
headers=self.json_headers,
|
||||
status_code=202)
|
||||
@ -0,0 +1,52 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class Fixture(base.Fixture):
|
||||
|
||||
base_url = 'os-aggregates'
|
||||
|
||||
def setUp(self):
|
||||
super(Fixture, self).setUp()
|
||||
|
||||
get_os_aggregates = {"aggregates": [
|
||||
{'id': '1',
|
||||
'name': 'test',
|
||||
'availability_zone': 'nova1'},
|
||||
{'id': '2',
|
||||
'name': 'test2',
|
||||
'availability_zone': 'nova1'},
|
||||
]}
|
||||
|
||||
self.requests.register_uri('GET', self.url(),
|
||||
json=get_os_aggregates,
|
||||
headers=self.json_headers)
|
||||
|
||||
get_aggregates_1 = {'aggregate': get_os_aggregates['aggregates'][0]}
|
||||
|
||||
self.requests.register_uri('POST', self.url(),
|
||||
json=get_aggregates_1,
|
||||
headers=self.json_headers)
|
||||
|
||||
for agg_id in (1, 2):
|
||||
for method in ('GET', 'PUT'):
|
||||
self.requests.register_uri(method, self.url(agg_id),
|
||||
json=get_aggregates_1,
|
||||
headers=self.json_headers)
|
||||
|
||||
self.requests.register_uri('POST', self.url(agg_id, 'action'),
|
||||
json=get_aggregates_1,
|
||||
headers=self.json_headers)
|
||||
|
||||
self.requests.register_uri('DELETE', self.url(1), status_code=202)
|
||||
@ -0,0 +1,91 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class V1(base.Fixture):
|
||||
|
||||
base_url = 'os-availability-zone'
|
||||
|
||||
zone_info_key = 'availabilityZoneInfo'
|
||||
zone_name_key = 'zoneName'
|
||||
zone_state_key = 'zoneState'
|
||||
|
||||
def setUp(self):
|
||||
super(V1, self).setUp()
|
||||
|
||||
get_os_availability_zone = {
|
||||
self.zone_info_key: [
|
||||
{
|
||||
self.zone_name_key: "zone-1",
|
||||
self.zone_state_key: {"available": True},
|
||||
"hosts": None
|
||||
},
|
||||
{
|
||||
self.zone_name_key: "zone-2",
|
||||
self.zone_state_key: {"available": False},
|
||||
"hosts": None
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
self.requests.register_uri('GET', self.url(),
|
||||
json=get_os_availability_zone,
|
||||
headers=self.json_headers)
|
||||
|
||||
get_os_zone_detail = {
|
||||
self.zone_info_key: [
|
||||
{
|
||||
self.zone_name_key: "zone-1",
|
||||
self.zone_state_key: {"available": True},
|
||||
"hosts": {
|
||||
"fake_host-1": {
|
||||
"nova-compute": {
|
||||
"active": True,
|
||||
"available": True,
|
||||
"updated_at": '2012-12-26 14:45:25'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
self.zone_name_key: "internal",
|
||||
self.zone_state_key: {"available": True},
|
||||
"hosts": {
|
||||
"fake_host-1": {
|
||||
"nova-sched": {
|
||||
"active": True,
|
||||
"available": True,
|
||||
"updated_at": '2012-12-26 14:45:25'
|
||||
}
|
||||
},
|
||||
"fake_host-2": {
|
||||
"nova-network": {
|
||||
"active": True,
|
||||
"available": False,
|
||||
"updated_at": '2012-12-26 14:45:24'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
self.zone_name_key: "zone-2",
|
||||
self.zone_state_key: {"available": False},
|
||||
"hosts": None
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
self.requests.register_uri('GET', self.url('detail'),
|
||||
json=get_os_zone_detail,
|
||||
headers=self.json_headers)
|
||||
@ -0,0 +1,40 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import fixtures
|
||||
from six.moves.urllib import parse
|
||||
|
||||
COMPUTE_URL = 'http://compute.host'
|
||||
|
||||
|
||||
class Fixture(fixtures.Fixture):
|
||||
|
||||
base_url = None
|
||||
json_headers = {'Content-Type': 'application/json'}
|
||||
|
||||
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):
|
||||
url_args = [self.compute_url]
|
||||
|
||||
if self.base_url:
|
||||
url_args.append(self.base_url)
|
||||
|
||||
url = '/'.join(str(a).strip('/') for a in tuple(url_args) + args)
|
||||
|
||||
if kwargs:
|
||||
url += '?%s' % parse.urlencode(kwargs, doseq=True)
|
||||
|
||||
return url
|
||||
@ -0,0 +1,55 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class Fixture(base.Fixture):
|
||||
|
||||
base_url = 'os-certificates'
|
||||
|
||||
def get_os_certificates_root(self, **kw):
|
||||
return (
|
||||
200,
|
||||
{},
|
||||
{'certificate': {'private_key': None, 'data': 'foo'}}
|
||||
)
|
||||
|
||||
def post_os_certificates(self, **kw):
|
||||
return (
|
||||
200,
|
||||
{},
|
||||
{'certificate': {'private_key': 'foo', 'data': 'bar'}}
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
super(Fixture, self).setUp()
|
||||
|
||||
get_os_certificate = {
|
||||
'certificate': {
|
||||
'private_key': None,
|
||||
'data': 'foo'
|
||||
}
|
||||
}
|
||||
self.requests.register_uri('GET', self.url('root'),
|
||||
json=get_os_certificate,
|
||||
headers=self.json_headers)
|
||||
|
||||
post_os_certificates = {
|
||||
'certificate': {
|
||||
'private_key': 'foo',
|
||||
'data': 'bar'
|
||||
}
|
||||
}
|
||||
self.requests.register_uri('POST', self.url(),
|
||||
json=post_os_certificates,
|
||||
headers=self.json_headers)
|
||||
@ -0,0 +1,65 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import fixtures
|
||||
from keystoneclient.auth.identity import v2
|
||||
from keystoneclient import fixture
|
||||
from keystoneclient import session
|
||||
|
||||
from novaclient.v2 import client as v2client
|
||||
|
||||
IDENTITY_URL = 'http://identityserver:5000/v2.0'
|
||||
COMPUTE_URL = 'http://compute.host'
|
||||
|
||||
|
||||
class V1(fixtures.Fixture):
|
||||
|
||||
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 = 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()
|
||||
|
||||
auth_url = '%s/tokens' % self.identity_url
|
||||
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):
|
||||
return v2client.Client(username='xx',
|
||||
api_key='xx',
|
||||
project_id='xx',
|
||||
auth_url=self.identity_url)
|
||||
|
||||
|
||||
class SessionV1(V1):
|
||||
|
||||
def new_client(self):
|
||||
self.session = session.Session()
|
||||
self.session.auth = v2.Password(self.identity_url, 'xx', 'xx')
|
||||
return v2client.Client(session=self.session)
|
||||
@ -0,0 +1,37 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class Fixture(base.Fixture):
|
||||
|
||||
base_url = 'os-cloudpipe'
|
||||
|
||||
def setUp(self):
|
||||
super(Fixture, self).setUp()
|
||||
|
||||
get_os_cloudpipe = {'cloudpipes': [{'project_id': 1}]}
|
||||
self.requests.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}
|
||||
self.requests.register_uri('POST', self.url(),
|
||||
json=post_os_cloudpipe,
|
||||
headers=self.json_headers,
|
||||
status_code=202)
|
||||
|
||||
self.requests.register_uri('PUT', self.url('configure-project'),
|
||||
headers=self.json_headers,
|
||||
status_code=202)
|
||||
@ -0,0 +1,39 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class Fixture(base.Fixture):
|
||||
|
||||
base_url = 'os-fixed-ips'
|
||||
|
||||
def setUp(self):
|
||||
super(Fixture, self).setUp()
|
||||
|
||||
get_os_fixed_ips = {
|
||||
"fixed_ip": {
|
||||
'cidr': '192.168.1.0/24',
|
||||
'address': '192.168.1.1',
|
||||
'hostname': 'foo',
|
||||
'host': 'bar'
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@ -0,0 +1,211 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo.serialization import jsonutils
|
||||
|
||||
from novaclient.tests.unit import fakes
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class FloatingFixture(base.Fixture):
|
||||
|
||||
base_url = 'os-floating-ips'
|
||||
|
||||
def setUp(self):
|
||||
super(FloatingFixture, self).setUp()
|
||||
|
||||
floating_ips = [{'id': 1, 'fixed_ip': '10.0.0.1', 'ip': '11.0.0.1'},
|
||||
{'id': 2, 'fixed_ip': '10.0.0.2', 'ip': '11.0.0.2'}]
|
||||
|
||||
get_os_floating_ips = {'floating_ips': floating_ips}
|
||||
self.requests.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}
|
||||
self.requests.register_uri('GET', self.url(ip['id']),
|
||||
json=get_os_floating_ip,
|
||||
headers=self.json_headers)
|
||||
|
||||
self.requests.register_uri('DELETE', self.url(ip['id']),
|
||||
headers=self.json_headers,
|
||||
status_code=204)
|
||||
|
||||
def post_os_floating_ips(request, context):
|
||||
body = jsonutils.loads(request.body)
|
||||
ip = floating_ips[0].copy()
|
||||
ip['pool'] = body.get('pool')
|
||||
return {'floating_ip': ip}
|
||||
self.requests.register_uri('POST', self.url(),
|
||||
json=post_os_floating_ips,
|
||||
headers=self.json_headers)
|
||||
|
||||
|
||||
class DNSFixture(base.Fixture):
|
||||
|
||||
base_url = 'os-floating-ip-dns'
|
||||
|
||||
def setUp(self):
|
||||
super(DNSFixture, self).setUp()
|
||||
|
||||
get_os_floating_ip_dns = {
|
||||
'domain_entries': [
|
||||
{'domain': 'example.org'},
|
||||
{'domain': 'example.com'}
|
||||
]
|
||||
}
|
||||
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': {
|
||||
'ip': "10.10.10.10",
|
||||
'name': 'testname',
|
||||
'type': "A",
|
||||
'domain': 'testdomain'
|
||||
}
|
||||
}
|
||||
url = self.url('testdomain', 'entries', 'testname')
|
||||
self.requests.register_uri('GET', url,
|
||||
json=get_dns_testdomain_entries_testname,
|
||||
headers=self.json_headers,
|
||||
status_code=205)
|
||||
|
||||
self.requests.register_uri('DELETE', self.url('testdomain'))
|
||||
|
||||
url = self.url('testdomain', 'entries', 'testname')
|
||||
self.requests.register_uri('DELETE', url)
|
||||
|
||||
def put_dns_testdomain_entries_testname(request, context):
|
||||
body = jsonutils.loads(request.body)
|
||||
fakes.assert_has_keys(body['dns_entry'],
|
||||
required=['ip', 'dns_type'])
|
||||
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')
|
||||
self.requests.register_uri('GET', url, status_code=404)
|
||||
|
||||
get_os_floating_ip_dns_testdomain = {
|
||||
'dns_entries': [
|
||||
{
|
||||
'dns_entry': {
|
||||
'ip': '1.2.3.4',
|
||||
'name': "host1",
|
||||
'type': "A",
|
||||
'domain': 'testdomain'
|
||||
}
|
||||
},
|
||||
{
|
||||
'dns_entry': {
|
||||
'ip': '1.2.3.4',
|
||||
'name': "host2",
|
||||
'type': "A",
|
||||
'domain': 'testdomain'
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
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, context):
|
||||
body = jsonutils.loads(request.body)
|
||||
if body['domain_entry']['scope'] == 'private':
|
||||
fakes.assert_has_keys(body['domain_entry'],
|
||||
required=['availability_zone', 'scope'])
|
||||
elif body['domain_entry']['scope'] == 'public':
|
||||
fakes.assert_has_keys(body['domain_entry'],
|
||||
required=['project', 'scope'])
|
||||
else:
|
||||
fakes.assert_has_keys(body['domain_entry'],
|
||||
required=['project', 'scope'])
|
||||
|
||||
return request.body
|
||||
|
||||
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):
|
||||
|
||||
base_url = 'os-floating-ips-bulk'
|
||||
|
||||
def setUp(self):
|
||||
super(BulkFixture, self).setUp()
|
||||
|
||||
get_os_floating_ips_bulk = {
|
||||
'floating_ip_info': [
|
||||
{'id': 1, 'fixed_ip': '10.0.0.1', 'ip': '11.0.0.1'},
|
||||
{'id': 2, 'fixed_ip': '10.0.0.2', 'ip': '11.0.0.2'},
|
||||
]
|
||||
}
|
||||
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, context):
|
||||
body = jsonutils.loads(request.body)
|
||||
ip_range = body.get('ip_range')
|
||||
return {'floating_ips_bulk_delete': ip_range}
|
||||
|
||||
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, context):
|
||||
body = jsonutils.loads(request.body)
|
||||
params = body.get('floating_ips_bulk_create')
|
||||
pool = params.get('pool', 'defaultPool')
|
||||
interface = params.get('interface', 'defaultInterface')
|
||||
return {
|
||||
'floating_ips_bulk_create': {
|
||||
'ip_range': '192.168.1.0/30',
|
||||
'pool': pool,
|
||||
'interface': interface
|
||||
}
|
||||
}
|
||||
|
||||
self.requests.register_uri('POST', self.url(),
|
||||
json=post_os_floating_ips_bulk,
|
||||
headers=self.json_headers)
|
||||
|
||||
|
||||
class PoolsFixture(base.Fixture):
|
||||
|
||||
base_url = 'os-floating-ip-pools'
|
||||
|
||||
def setUp(self):
|
||||
super(PoolsFixture, self).setUp()
|
||||
|
||||
get_os_floating_ip_pools = {
|
||||
'floating_ip_pools': [
|
||||
{'name': 'foo'},
|
||||
{'name': 'bar'}
|
||||
]
|
||||
}
|
||||
self.requests.register_uri('GET', self.url(),
|
||||
json=get_os_floating_ip_pools,
|
||||
headers=self.json_headers)
|
||||
@ -0,0 +1,46 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class Fixture(base.Fixture):
|
||||
|
||||
base_url = 'os-fping'
|
||||
|
||||
def setUp(self):
|
||||
super(Fixture, self).setUp()
|
||||
|
||||
get_os_fping_1 = {
|
||||
'server': {
|
||||
"id": "1",
|
||||
"project_id": "fake-project",
|
||||
"alive": True,
|
||||
}
|
||||
}
|
||||
self.requests.register_uri('GET', self.url(1),
|
||||
json=get_os_fping_1,
|
||||
headers=self.json_headers)
|
||||
|
||||
get_os_fping = {
|
||||
'servers': [
|
||||
get_os_fping_1['server'],
|
||||
{
|
||||
"id": "2",
|
||||
"project_id": "fake-project",
|
||||
"alive": True,
|
||||
},
|
||||
]
|
||||
}
|
||||
self.requests.register_uri('GET', self.url(),
|
||||
json=get_os_fping,
|
||||
headers=self.json_headers)
|
||||
@ -0,0 +1,149 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo.serialization import jsonutils
|
||||
from six.moves.urllib import parse
|
||||
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class BaseFixture(base.Fixture):
|
||||
|
||||
base_url = 'os-hosts'
|
||||
|
||||
def setUp(self):
|
||||
super(BaseFixture, self).setUp()
|
||||
|
||||
get_os_hosts_host = {
|
||||
'host': [
|
||||
{'resource': {'project': '(total)', 'host': 'dummy',
|
||||
'cpu': 16, 'memory_mb': 32234, 'disk_gb': 128}},
|
||||
{'resource': {'project': '(used_now)', 'host': 'dummy',
|
||||
'cpu': 1, 'memory_mb': 2075, 'disk_gb': 45}},
|
||||
{'resource': {'project': '(used_max)', 'host': 'dummy',
|
||||
'cpu': 1, 'memory_mb': 2048, 'disk_gb': 30}},
|
||||
{'resource': {'project': 'admin', 'host': 'dummy',
|
||||
'cpu': 1, 'memory_mb': 2048, 'disk_gb': 30}}
|
||||
]
|
||||
}
|
||||
|
||||
headers = {'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)
|
||||
try:
|
||||
zone = qs['zone'][0]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
service = qs['service'][0]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return {
|
||||
'hosts': [
|
||||
{
|
||||
'host': 'host1',
|
||||
'service': service or 'nova-compute',
|
||||
'zone': zone
|
||||
},
|
||||
{
|
||||
'host': 'host1',
|
||||
'service': service or 'nova-cert',
|
||||
'zone': zone
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
self.requests.register_uri('GET', self.url(),
|
||||
json=get_os_hosts,
|
||||
headers=headers)
|
||||
|
||||
get_os_hosts_sample_host = {
|
||||
'host': [
|
||||
{'resource': {'host': 'sample_host'}}
|
||||
],
|
||||
}
|
||||
self.requests.register_uri('GET', self.url('sample_host'),
|
||||
json=get_os_hosts_sample_host,
|
||||
headers=headers)
|
||||
|
||||
self.requests.register_uri('PUT', self.url('sample_host', 1),
|
||||
json=self.put_host_1(),
|
||||
headers=headers)
|
||||
|
||||
self.requests.register_uri('PUT', self.url('sample_host', 2),
|
||||
json=self.put_host_2(),
|
||||
headers=headers)
|
||||
|
||||
self.requests.register_uri('PUT', self.url('sample_host', 3),
|
||||
json=self.put_host_3(),
|
||||
headers=headers)
|
||||
|
||||
self.requests.register_uri('GET', self.url('sample_host', 'reboot'),
|
||||
json=self.get_host_reboot(),
|
||||
headers=headers)
|
||||
|
||||
self.requests.register_uri('GET', self.url('sample_host', 'startup'),
|
||||
json=self.get_host_startup(),
|
||||
headers=headers)
|
||||
|
||||
self.requests.register_uri('GET', self.url('sample_host', 'shutdown'),
|
||||
json=self.get_host_shutdown(),
|
||||
headers=headers)
|
||||
|
||||
def put_os_hosts_sample_host(request, context):
|
||||
result = {'host': 'dummy'}
|
||||
result.update(jsonutils.loads(request.body))
|
||||
return result
|
||||
|
||||
self.requests.register_uri('PUT', self.url('sample_host'),
|
||||
json=put_os_hosts_sample_host,
|
||||
headers=headers)
|
||||
|
||||
|
||||
class V1(BaseFixture):
|
||||
|
||||
def put_host_1(self):
|
||||
return {'host': 'sample-host_1',
|
||||
'status': 'enabled'}
|
||||
|
||||
def put_host_2(self):
|
||||
return {'host': 'sample-host_2',
|
||||
'maintenance_mode': 'on_maintenance'}
|
||||
|
||||
def put_host_3(self):
|
||||
return {'host': 'sample-host_3',
|
||||
'status': 'enabled',
|
||||
'maintenance_mode': 'on_maintenance'}
|
||||
|
||||
def get_host_reboot(self):
|
||||
return {'host': 'sample_host',
|
||||
'power_action': 'reboot'}
|
||||
|
||||
def get_host_startup(self):
|
||||
return {'host': 'sample_host',
|
||||
'power_action': 'startup'}
|
||||
|
||||
def get_host_shutdown(self):
|
||||
return {'host': 'sample_host',
|
||||
'power_action': 'shutdown'}
|
||||
@ -0,0 +1,182 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class V1(base.Fixture):
|
||||
|
||||
base_url = 'os-hypervisors'
|
||||
|
||||
def setUp(self):
|
||||
super(V1, self).setUp()
|
||||
|
||||
get_os_hypervisors = {
|
||||
'hypervisors': [
|
||||
{'id': 1234, 'hypervisor_hostname': 'hyper1'},
|
||||
{'id': 5678, 'hypervisor_hostname': 'hyper2'},
|
||||
]
|
||||
}
|
||||
|
||||
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',
|
||||
},
|
||||
'vcpus': 4,
|
||||
'memory_mb': 10 * 1024,
|
||||
'local_gb': 250,
|
||||
'vcpus_used': 2,
|
||||
'memory_mb_used': 5 * 1024,
|
||||
'local_gb_used': 125,
|
||||
'hypervisor_type': 'xen',
|
||||
'hypervisor_version': 3,
|
||||
'hypervisor_hostname': 'hyper1',
|
||||
'free_ram_mb': 5 * 1024,
|
||||
'free_disk_gb': 125,
|
||||
'current_workload': 2,
|
||||
'running_vms': 2,
|
||||
'cpu_info': 'cpu_info',
|
||||
'disk_available_least': 100
|
||||
},
|
||||
{
|
||||
'id': 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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
self.requests.register_uri('GET', self.url('detail'),
|
||||
json=get_os_hypervisors_detail,
|
||||
headers=self.headers)
|
||||
|
||||
get_os_hypervisors_stats = {
|
||||
'hypervisor_statistics': {
|
||||
'count': 2,
|
||||
'vcpus': 8,
|
||||
'memory_mb': 20 * 1024,
|
||||
'local_gb': 500,
|
||||
'vcpus_used': 4,
|
||||
'memory_mb_used': 10 * 1024,
|
||||
'local_gb_used': 250,
|
||||
'free_ram_mb': 10 * 1024,
|
||||
'free_disk_gb': 250,
|
||||
'current_workload': 4,
|
||||
'running_vms': 4,
|
||||
'disk_available_least': 200,
|
||||
}
|
||||
}
|
||||
|
||||
self.requests.register_uri('GET', self.url('statistics'),
|
||||
json=get_os_hypervisors_stats,
|
||||
headers=self.headers)
|
||||
|
||||
get_os_hypervisors_search = {
|
||||
'hypervisors': [
|
||||
{'id': 1234, 'hypervisor_hostname': 'hyper1'},
|
||||
{'id': 5678, 'hypervisor_hostname': 'hyper2'}
|
||||
]
|
||||
}
|
||||
|
||||
self.requests.register_uri('GET', self.url('hyper', 'search'),
|
||||
json=get_os_hypervisors_search,
|
||||
headers=self.headers)
|
||||
|
||||
get_hyper_server = {
|
||||
'hypervisors': [
|
||||
{
|
||||
'id': 1234,
|
||||
'hypervisor_hostname': 'hyper1',
|
||||
'servers': [
|
||||
{'name': 'inst1', 'uuid': 'uuid1'},
|
||||
{'name': 'inst2', 'uuid': 'uuid2'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 5678,
|
||||
'hypervisor_hostname': 'hyper2',
|
||||
'servers': [
|
||||
{'name': 'inst3', 'uuid': 'uuid3'},
|
||||
{'name': 'inst4', 'uuid': 'uuid4'}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
self.requests.register_uri('GET', self.url('hyper', 'servers'),
|
||||
json=get_hyper_server,
|
||||
headers=self.headers)
|
||||
|
||||
get_os_hypervisors_1234 = {
|
||||
'hypervisor': {
|
||||
'id': 1234,
|
||||
'service': {'id': 1, 'host': 'compute1'},
|
||||
'vcpus': 4,
|
||||
'memory_mb': 10 * 1024,
|
||||
'local_gb': 250,
|
||||
'vcpus_used': 2,
|
||||
'memory_mb_used': 5 * 1024,
|
||||
'local_gb_used': 125,
|
||||
'hypervisor_type': 'xen',
|
||||
'hypervisor_version': 3,
|
||||
'hypervisor_hostname': 'hyper1',
|
||||
'free_ram_mb': 5 * 1024,
|
||||
'free_disk_gb': 125,
|
||||
'current_workload': 2,
|
||||
'running_vms': 2,
|
||||
'cpu_info': 'cpu_info',
|
||||
'disk_available_least': 100
|
||||
}
|
||||
}
|
||||
|
||||
self.requests.register_uri('GET', self.url(1234),
|
||||
json=get_os_hypervisors_1234,
|
||||
headers=self.headers)
|
||||
|
||||
get_os_hypervisors_uptime = {
|
||||
'hypervisor': {
|
||||
'id': 1234,
|
||||
'hypervisor_hostname': 'hyper1',
|
||||
'uptime': 'fake uptime'
|
||||
}
|
||||
}
|
||||
|
||||
self.requests.register_uri('GET', self.url(1234, 'uptime'),
|
||||
json=get_os_hypervisors_uptime,
|
||||
headers=self.headers)
|
||||
@ -0,0 +1,86 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo.serialization import jsonutils
|
||||
|
||||
from novaclient.tests.unit import fakes
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class V1(base.Fixture):
|
||||
|
||||
base_url = 'images'
|
||||
|
||||
def setUp(self):
|
||||
super(V1, self).setUp()
|
||||
|
||||
get_images = {
|
||||
'images': [
|
||||
{'id': 1, 'name': 'CentOS 5.2'},
|
||||
{'id': 2, 'name': 'My Server Backup'}
|
||||
]
|
||||
}
|
||||
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
|
||||
self.requests.register_uri('GET', self.url(),
|
||||
json=get_images,
|
||||
headers=headers)
|
||||
|
||||
image_1 = {
|
||||
'id': 1,
|
||||
'name': 'CentOS 5.2',
|
||||
"updated": "2010-10-10T12:00:00Z",
|
||||
"created": "2010-08-10T12:00:00Z",
|
||||
"status": "ACTIVE",
|
||||
"metadata": {
|
||||
"test_key": "test_value",
|
||||
},
|
||||
"links": {},
|
||||
}
|
||||
|
||||
image_2 = {
|
||||
"id": 2,
|
||||
"name": "My Server Backup",
|
||||
"serverId": 1234,
|
||||
"updated": "2010-10-10T12:00:00Z",
|
||||
"created": "2010-08-10T12:00:00Z",
|
||||
"status": "SAVING",
|
||||
"progress": 80,
|
||||
"links": {},
|
||||
}
|
||||
|
||||
self.requests.register_uri('GET', self.url('detail'),
|
||||
json={'images': [image_1, image_2]},
|
||||
headers=headers)
|
||||
|
||||
self.requests.register_uri('GET', self.url(1),
|
||||
json={'image': image_1},
|
||||
headers=headers)
|
||||
|
||||
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'])
|
||||
return {'metadata': image_1['metadata']}
|
||||
|
||||
self.requests.register_uri('POST', self.url(1, 'metadata'),
|
||||
json=post_images_1_metadata,
|
||||
headers=headers)
|
||||
|
||||
for u in (1, '1/metadata/test_key'):
|
||||
self.requests.register_uri('DELETE', self.url(u), status_code=204)
|
||||
|
||||
|
||||
class V3(V1):
|
||||
|
||||
base_url = 'v1/images'
|
||||
@ -0,0 +1,47 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo.serialization import jsonutils
|
||||
|
||||
from novaclient.tests.unit import fakes
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class V1(base.Fixture):
|
||||
|
||||
base_url = 'os-keypairs'
|
||||
|
||||
def setUp(self):
|
||||
super(V1, self).setUp()
|
||||
keypair = {'fingerprint': 'FAKE_KEYPAIR', 'name': 'test'}
|
||||
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
|
||||
self.requests.register_uri('GET', self.url(),
|
||||
json={'keypairs': [keypair]},
|
||||
headers=headers)
|
||||
|
||||
self.requests.register_uri('GET', self.url('test'),
|
||||
json={'keypair': keypair},
|
||||
headers=headers)
|
||||
|
||||
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 {'keypair': keypair}
|
||||
|
||||
self.requests.register_uri('POST', self.url(),
|
||||
json=post_os_keypairs,
|
||||
headers=headers)
|
||||
@ -0,0 +1,80 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class Fixture(base.Fixture):
|
||||
|
||||
base_url = 'limits'
|
||||
|
||||
def setUp(self):
|
||||
super(Fixture, self).setUp()
|
||||
|
||||
get_limits = {
|
||||
"limits": {
|
||||
"rate": [
|
||||
{
|
||||
"uri": "*",
|
||||
"regex": ".*",
|
||||
"limit": [
|
||||
{
|
||||
"value": 10,
|
||||
"verb": "POST",
|
||||
"remaining": 2,
|
||||
"unit": "MINUTE",
|
||||
"next-available": "2011-12-15T22:42:45Z"
|
||||
},
|
||||
{
|
||||
"value": 10,
|
||||
"verb": "PUT",
|
||||
"remaining": 2,
|
||||
"unit": "MINUTE",
|
||||
"next-available": "2011-12-15T22:42:45Z"
|
||||
},
|
||||
{
|
||||
"value": 100,
|
||||
"verb": "DELETE",
|
||||
"remaining": 100,
|
||||
"unit": "MINUTE",
|
||||
"next-available": "2011-12-15T22:42:45Z"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"uri": "*/servers",
|
||||
"regex": "^/servers",
|
||||
"limit": [
|
||||
{
|
||||
"verb": "POST",
|
||||
"value": 25,
|
||||
"remaining": 24,
|
||||
"unit": "DAY",
|
||||
"next-available": "2011-12-15T22:42:45Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"absolute": {
|
||||
"maxTotalRAMSize": 51200,
|
||||
"maxServerMeta": 5,
|
||||
"maxImageMeta": 5,
|
||||
"maxPersonality": 5,
|
||||
"maxPersonalitySize": 10240
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
self.requests.register_uri('GET', self.url(),
|
||||
json=get_limits,
|
||||
headers=headers)
|
||||
@ -0,0 +1,62 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo.serialization import jsonutils
|
||||
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class Fixture(base.Fixture):
|
||||
|
||||
base_url = 'os-networks'
|
||||
|
||||
def setUp(self):
|
||||
super(Fixture, self).setUp()
|
||||
|
||||
get_os_networks = {
|
||||
'networks': [
|
||||
{
|
||||
"label": "1",
|
||||
"cidr": "10.0.0.0/24",
|
||||
'project_id': '4ffc664c198e435e9853f2538fbcd7a7',
|
||||
'id': '1'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
|
||||
self.requests.register_uri('GET', self.url(),
|
||||
json=get_os_networks,
|
||||
headers=headers)
|
||||
|
||||
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"}}
|
||||
|
||||
self.requests.register_uri('GET', self.url(1),
|
||||
json=get_os_networks_1,
|
||||
headers=headers)
|
||||
|
||||
self.requests.register_uri('DELETE',
|
||||
self.url('networkdelete'),
|
||||
status_code=202)
|
||||
|
||||
for u in ('add', 'networkdisassociate/action', 'networktest/action',
|
||||
'1/action', '2/action'):
|
||||
self.requests.register_uri('POST', self.url(u), status_code=202)
|
||||
@ -0,0 +1,66 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class V1(base.Fixture):
|
||||
|
||||
base_url = 'os-quota-sets'
|
||||
|
||||
def setUp(self):
|
||||
super(V1, self).setUp()
|
||||
|
||||
uuid = '97f4c221-bff4-4578-b030-0df4ef119353'
|
||||
uuid2 = '97f4c221bff44578b0300df4ef119353'
|
||||
test_json = {'quota_set': self.test_quota('test')}
|
||||
self.headers = {'Content-Type': 'application/json'}
|
||||
|
||||
for u in ('test', 'tenant-id', 'tenant-id/defaults',
|
||||
'%s/defaults' % uuid2):
|
||||
self.requests.register_uri('GET', self.url(u),
|
||||
json=test_json,
|
||||
headers=self.headers)
|
||||
|
||||
self.requests.register_uri('PUT', self.url(uuid),
|
||||
json={'quota_set': self.test_quota(uuid)},
|
||||
headers=self.headers)
|
||||
|
||||
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):
|
||||
self.requests.register_uri('DELETE', self.url(u), status_code=202)
|
||||
|
||||
def test_quota(self, tenant_id='test'):
|
||||
return {
|
||||
'tenant_id': tenant_id,
|
||||
'metadata_items': [],
|
||||
'injected_file_content_bytes': 1,
|
||||
'injected_file_path_bytes': 1,
|
||||
'ram': 1,
|
||||
'floating_ips': 1,
|
||||
'instances': 1,
|
||||
'injected_files': 1,
|
||||
'cores': 1,
|
||||
'keypairs': 1,
|
||||
'security_groups': 1,
|
||||
'security_group_rules': 1
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo.serialization import jsonutils
|
||||
|
||||
from novaclient.tests.unit import fakes
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class Fixture(base.Fixture):
|
||||
|
||||
base_url = 'os-security-group-rules'
|
||||
|
||||
def setUp(self):
|
||||
super(Fixture, self).setUp()
|
||||
|
||||
rule = {
|
||||
'id': 1,
|
||||
'parent_group_id': 1,
|
||||
'group_id': 2,
|
||||
'ip_protocol': 'TCP',
|
||||
'from_port': '22',
|
||||
'to_port': 22,
|
||||
'cidr': '10.0.0.0/8'
|
||||
}
|
||||
|
||||
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):
|
||||
self.requests.register_uri('DELETE', self.url(u), status_code=202)
|
||||
|
||||
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 {'security_group_rule': rule}
|
||||
|
||||
self.requests.register_uri('POST', self.url(),
|
||||
json=post_rules,
|
||||
headers=headers,
|
||||
status_code=202)
|
||||
@ -0,0 +1,100 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo.serialization import jsonutils
|
||||
|
||||
from novaclient.tests.unit import fakes
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class Fixture(base.Fixture):
|
||||
|
||||
base_url = 'os-security-groups'
|
||||
|
||||
def setUp(self):
|
||||
super(Fixture, self).setUp()
|
||||
|
||||
security_group_1 = {
|
||||
"name": "test",
|
||||
"description": "FAKE_SECURITY_GROUP",
|
||||
"tenant_id": "4ffc664c198e435e9853f2538fbcd7a7",
|
||||
"id": 1,
|
||||
"rules": [
|
||||
{
|
||||
"id": 11,
|
||||
"group": {},
|
||||
"ip_protocol": "TCP",
|
||||
"from_port": 22,
|
||||
"to_port": 22,
|
||||
"parent_group_id": 1,
|
||||
"ip_range": {"cidr": "10.0.0.0/8"}
|
||||
},
|
||||
{
|
||||
"id": 12,
|
||||
"group": {
|
||||
"tenant_id": "272bee4c1e624cd4a72a6b0ea55b4582",
|
||||
"name": "test2"
|
||||
},
|
||||
"ip_protocol": "TCP",
|
||||
"from_port": 222,
|
||||
"to_port": 222,
|
||||
"parent_group_id": 1,
|
||||
"ip_range": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
security_group_2 = {
|
||||
"name": "test2",
|
||||
"description": "FAKE_SECURITY_GROUP2",
|
||||
"tenant_id": "272bee4c1e624cd4a72a6b0ea55b4582",
|
||||
"id": 2,
|
||||
"rules": []
|
||||
}
|
||||
|
||||
get_groups = {'security_groups': [security_group_1, security_group_2]}
|
||||
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}
|
||||
self.requests.register_uri('GET', self.url(1),
|
||||
json=get_group_1,
|
||||
headers=headers)
|
||||
|
||||
self.requests.register_uri('DELETE', self.url(1), status_code=202)
|
||||
|
||||
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'])
|
||||
return {'security_group': security_group_1}
|
||||
|
||||
self.requests.register_uri('POST', self.url(),
|
||||
json=post_os_security_groups,
|
||||
headers=headers,
|
||||
status_code=202)
|
||||
|
||||
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 body
|
||||
|
||||
self.requests.register_uri('PUT', self.url(1),
|
||||
json=put_os_security_groups_1,
|
||||
headers=headers,
|
||||
status_code=205)
|
||||
@ -0,0 +1,74 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo.serialization import jsonutils
|
||||
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
|
||||
|
||||
class Fixture(base.Fixture):
|
||||
|
||||
base_url = 'os-server-groups'
|
||||
|
||||
def setUp(self):
|
||||
super(Fixture, self).setUp()
|
||||
|
||||
server_groups = [
|
||||
{
|
||||
"members": [],
|
||||
"metadata": {},
|
||||
"id": "2cbd51f4-fafe-4cdb-801b-cf913a6f288b",
|
||||
"policies": [],
|
||||
"name": "ig1"
|
||||
},
|
||||
{
|
||||
"members": [],
|
||||
"metadata": {},
|
||||
"id": "4473bb03-4370-4bfb-80d3-dc8cffc47d94",
|
||||
"policies": ["anti-affinity"],
|
||||
"name": "ig2"
|
||||
},
|
||||
{
|
||||
"members": [],
|
||||
"metadata": {"key": "value"},
|
||||
"id": "31ab9bdb-55e1-4ac3-b094-97eeb1b65cc4",
|
||||
"policies": [], "name": "ig3"
|
||||
},
|
||||
{
|
||||
"members": ["2dccb4a1-02b9-482a-aa23-5799490d6f5d"],
|
||||
"metadata": {},
|
||||
"id": "4890bb03-7070-45fb-8453-d34556c87d94",
|
||||
"policies": ["anti-affinity"],
|
||||
"name": "ig2"
|
||||
}
|
||||
]
|
||||
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
|
||||
self.requests.register_uri('GET', self.url(),
|
||||
json={'server_groups': server_groups},
|
||||
headers=headers)
|
||||
|
||||
server = server_groups[0]
|
||||
server_j = jsonutils.dumps({'server_group': server})
|
||||
|
||||
def _register(method, *args):
|
||||
self.requests.register_uri(method, self.url(*args), text=server_j)
|
||||
|
||||
_register('POST')
|
||||
_register('POST', server['id'])
|
||||
_register('GET', server['id'])
|
||||
_register('PUT', server['id'])
|
||||
_register('POST', server['id'], '/action')
|
||||
|
||||
self.requests.register_uri('DELETE', self.url(server['id']),
|
||||
status_code=202)
|
||||
@ -0,0 +1,427 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo.serialization import jsonutils
|
||||
|
||||
from novaclient.tests.unit import fakes
|
||||
from novaclient.tests.unit.fixture_data import base
|
||||
from novaclient.tests.unit.v2 import fakes as v2_fakes
|
||||
|
||||
|
||||
class Base(base.Fixture):
|
||||
|
||||
base_url = 'servers'
|
||||
|
||||
def setUp(self):
|
||||
super(Base, self).setUp()
|
||||
|
||||
get_servers = {
|
||||
"servers": [
|
||||
{'id': 1234, 'name': 'sample-server'},
|
||||
{'id': 5678, 'name': 'sample-server2'}
|
||||
]
|
||||
}
|
||||
|
||||
self.requests.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/unit/idfake.pem
|
||||
#
|
||||
# Encrypted password
|
||||
# OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r
|
||||
# qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho
|
||||
# QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw
|
||||
# /y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N
|
||||
# tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk
|
||||
# Hi/fmZZNQQqj1Ijq0caOIw==
|
||||
|
||||
get_server_password = {
|
||||
'password':
|
||||
'OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r'
|
||||
'qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho'
|
||||
'QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw'
|
||||
'/y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N'
|
||||
'tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk'
|
||||
'Hi/fmZZNQQqj1Ijq0caOIw=='}
|
||||
self.requests.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 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
|
||||
elif action == 'rebuild':
|
||||
body = body[action]
|
||||
adminPass = body.get('adminPass', 'randompassword')
|
||||
assert 'imageRef' in body
|
||||
_body = self.server_1234.copy()
|
||||
_body['adminPass'] = adminPass
|
||||
elif action == 'confirmResize':
|
||||
assert body[action] is None
|
||||
# This one method returns a different response code
|
||||
context.status_code = 204
|
||||
return None
|
||||
elif action == 'rescue':
|
||||
if body[action]:
|
||||
keys = set(body[action].keys())
|
||||
assert not (keys - set(['adminPass', 'rescue_image_ref']))
|
||||
else:
|
||||
assert body[action] is None
|
||||
_body = {'adminPass': 'RescuePassword'}
|
||||
elif action == 'createImage':
|
||||
assert set(body[action].keys()) == set(['name', 'metadata'])
|
||||
context.headers['location'] = "http://blah/images/456"
|
||||
elif action == 'os-getConsoleOutput':
|
||||
assert list(body[action]) == ['length']
|
||||
context.status_code = 202
|
||||
return {'output': 'foo'}
|
||||
elif action == 'os-getSerialConsole':
|
||||
assert list(body[action]) == ['type']
|
||||
elif action == 'evacuate':
|
||||
keys = list(body[action])
|
||||
if 'adminPass' in keys:
|
||||
keys.remove('adminPass')
|
||||
assert set(keys) == set(['host', 'onSharedStorage'])
|
||||
else:
|
||||
raise AssertionError("Unexpected server action: %s" % action)
|
||||
return {'server': _body}
|
||||
27
awx/lib/site-packages/novaclient/tests/unit/idfake.pem
Normal file
27
awx/lib/site-packages/novaclient/tests/unit/idfake.pem
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA9QstF/7prDY7a9La7GS9TpMX+MWWXQgK6pHRLakDFp1WX1Q3
|
||||
Vly7rWitaZUGirUPMm181oJXBwkKlAxFD7hKjyHYaSswNszPYIAsVkc1+AO5epXz
|
||||
g9kUBNtfg44Pg72UecwLrZ8JpmNZpJlKQOx6vF+yi7JmHrrIf6il/grIGUPzoT2L
|
||||
yReimpyPoBrGtXhJYaCJ/XbKg1idRZiQdmwh1F/OmZWn9p0wunnsv08a0+qIywuw
|
||||
WhG9/Zy9fjnEByfusS6gI0GIxDRL4RWzOqphd3PZzunwIBgEKFhgiki9+2DgcRVO
|
||||
9I5wnDvfwQREJRZWh1uJa5ZTcfPa1EzZryVeOQIDAQABAoIBABxO3Te/cBk/7p9n
|
||||
LXlPrfrszUEk+ljm+/PbQpIGy1+Kb5b1sKrebaP7ysS+vZG6lvXZZimVxx398mXm
|
||||
APhu7tYYL9r+bUR3ZqGcTQLumRJ8w6mgtxANPN3Oxfr5p1stxIBJjTPSgpfhNFLq
|
||||
joRvjUJDv+mZg2ibZVwyDHMLpdAdKp+3XMdyTLZcH9esqwii+natix7rHd1RuF85
|
||||
L1dfpxjkItwhgHsfdYS++5X3fRByFOhQ+Nhabh/kPQbQMcteRn1bN6zeCWBSglNb
|
||||
Ka/ZrXb6ApRUc22Ji62mNO2ZPPekLJeCHk2h2E7ezYX+sGDNvvd/jHVDJJ20FjD1
|
||||
Z9KXuK0CgYEA/2vniy9yWd925QQtWbmrxgy6yj89feMH/LTv4qP298rGZ2nqxsyd
|
||||
9pdBdb4NMsi4HmV5PG1hp3VRNBHl53DNh5eqzT8WEXnIF+sbrIU3KzrCVAx1kZTl
|
||||
+OWKA6aVUsvvO3y85SOvInnsV+IsOGmU4/WBSjYoe39Bo7mq/YuZB9MCgYEA9ZlB
|
||||
KBm6PjFdHQGNgedXahWzRcwC+ALCYqequPYqJolNzhrK4Uc2sWPSGdnldcHZ4XCQ
|
||||
wbfCxUSwrMpA1oyuIQ0U4aowmOw5DjIueBWI8XBYEVRBlwvJwbXpBZ/DspGzTUDx
|
||||
MBrrEwEaMadQvxhRnAzhp0rQAepatcz6Fgb1JkMCgYBMwDLiew5kfSav6JJsDMPW
|
||||
DksurNQgeNEUmZYfx19V1EPMHWKj/CZXS9oqtEIpCXFyCNHmW4PlmvYcrGgmJJpN
|
||||
7UAwzo0mES8UKNy2+Yy7W7u7H8dQSKrWILtZH3xtVcR8Xp4wSIm+1V40hkz9YpSP
|
||||
71y7XQzLF1E1DnyYFZOVawKBgAFrmHfd5jjT2kD/sEzPBK9lXrsJmf7LLUqaw578
|
||||
NXQxmRSXDRNOcR+Hf0CNBQmwTE1EdGHaaTLw2cC2Drfu6lbgl31SmaNYwl+1pJUn
|
||||
MrqKtseq4BI6jDkljypsKRqQQyQwOvTXQwLCH9+nowzn3Bj17hwkj51jOJESlWOp
|
||||
OKO3AoGBALm+jjqyqX7gSnqK3FAumB8mlhv3yI1Wr1ctwe18mKfKbz17HxXRu9pF
|
||||
K/6e7WMCA1p+jhoE8gj1h2WBcH0nV2qt8Ye8gJBbCi4dhI08o4AfrIV47oZx1RlO
|
||||
qYcA1U9lyaODY5SL8+6PHOy5J/aYtuA+wvfEnWiCIdKQrhWetcn3
|
||||
-----END RSA PRIVATE KEY-----
|
||||
328
awx/lib/site-packages/novaclient/tests/unit/test_auth_plugins.py
Normal file
328
awx/lib/site-packages/novaclient/tests/unit/test_auth_plugins.py
Normal file
@ -0,0 +1,328 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import argparse
|
||||
|
||||
from keystoneclient import fixture
|
||||
import mock
|
||||
import pkg_resources
|
||||
import requests
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
from novaclient import auth_plugin
|
||||
from novaclient import exceptions
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import client
|
||||
|
||||
|
||||
def mock_http_request(resp=None):
|
||||
"""Mock an HTTP Request."""
|
||||
if not resp:
|
||||
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,
|
||||
"text": json.dumps(resp),
|
||||
})
|
||||
return mock.Mock(return_value=(auth_response))
|
||||
|
||||
|
||||
def requested_headers(cs):
|
||||
"""Return requested passed headers."""
|
||||
return {
|
||||
'User-Agent': cs.client.USER_AGENT,
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
|
||||
|
||||
class DeprecatedAuthPluginTest(utils.TestCase):
|
||||
def test_auth_system_success(self):
|
||||
class MockEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self):
|
||||
return self.authenticate
|
||||
|
||||
def authenticate(self, cls, auth_url):
|
||||
cls._authenticate(auth_url, {"fake": "me"})
|
||||
|
||||
def mock_iter_entry_points(_type, name):
|
||||
if _type == 'openstack.client.authenticate':
|
||||
return [MockEntrypoint("fake", "fake", ["fake"])]
|
||||
else:
|
||||
return []
|
||||
|
||||
mock_request = mock_http_request()
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points",
|
||||
mock_iter_entry_points)
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_auth_call():
|
||||
plugin = auth_plugin.DeprecatedAuthPlugin("fake")
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
utils.AUTH_URL_V2, auth_system="fake",
|
||||
auth_plugin=plugin)
|
||||
cs.client.authenticate()
|
||||
|
||||
headers = requested_headers(cs)
|
||||
token_url = cs.client.auth_url + "/tokens"
|
||||
|
||||
mock_request.assert_called_with(
|
||||
"POST",
|
||||
token_url,
|
||||
headers=headers,
|
||||
data='{"fake": "me"}',
|
||||
allow_redirects=True,
|
||||
**self.TEST_REQUEST_BASE)
|
||||
|
||||
test_auth_call()
|
||||
|
||||
def test_auth_system_not_exists(self):
|
||||
def mock_iter_entry_points(_t, name=None):
|
||||
return [pkg_resources.EntryPoint("fake", "fake", ["fake"])]
|
||||
|
||||
mock_request = mock_http_request()
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points",
|
||||
mock_iter_entry_points)
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_auth_call():
|
||||
auth_plugin.discover_auth_systems()
|
||||
plugin = auth_plugin.DeprecatedAuthPlugin("notexists")
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
utils.AUTH_URL_V2, auth_system="notexists",
|
||||
auth_plugin=plugin)
|
||||
self.assertRaises(exceptions.AuthSystemNotFound,
|
||||
cs.client.authenticate)
|
||||
|
||||
test_auth_call()
|
||||
|
||||
def test_auth_system_defining_auth_url(self):
|
||||
class MockAuthUrlEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self):
|
||||
return self.auth_url
|
||||
|
||||
def auth_url(self):
|
||||
return "http://faked/v2.0"
|
||||
|
||||
class MockAuthenticateEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self):
|
||||
return self.authenticate
|
||||
|
||||
def authenticate(self, cls, auth_url):
|
||||
cls._authenticate(auth_url, {"fake": "me"})
|
||||
|
||||
def mock_iter_entry_points(_type, name):
|
||||
if _type == 'openstack.client.auth_url':
|
||||
return [MockAuthUrlEntrypoint("fakewithauthurl",
|
||||
"fakewithauthurl",
|
||||
["auth_url"])]
|
||||
elif _type == 'openstack.client.authenticate':
|
||||
return [MockAuthenticateEntrypoint("fakewithauthurl",
|
||||
"fakewithauthurl",
|
||||
["authenticate"])]
|
||||
else:
|
||||
return []
|
||||
|
||||
mock_request = mock_http_request()
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points",
|
||||
mock_iter_entry_points)
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_auth_call():
|
||||
plugin = auth_plugin.DeprecatedAuthPlugin("fakewithauthurl")
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
auth_system="fakewithauthurl",
|
||||
auth_plugin=plugin)
|
||||
cs.client.authenticate()
|
||||
self.assertEqual("http://faked/v2.0", cs.client.auth_url)
|
||||
|
||||
test_auth_call()
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points")
|
||||
def test_client_raises_exc_without_auth_url(self, mock_iter_entry_points):
|
||||
class MockAuthUrlEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self):
|
||||
return self.auth_url
|
||||
|
||||
def auth_url(self):
|
||||
return None
|
||||
|
||||
mock_iter_entry_points.side_effect = lambda _t, name: [
|
||||
MockAuthUrlEntrypoint("fakewithauthurl",
|
||||
"fakewithauthurl",
|
||||
["auth_url"])]
|
||||
|
||||
plugin = auth_plugin.DeprecatedAuthPlugin("fakewithauthurl")
|
||||
self.assertRaises(
|
||||
exceptions.EndpointNotFound,
|
||||
client.Client, "username", "password", "project_id",
|
||||
auth_system="fakewithauthurl", auth_plugin=plugin)
|
||||
|
||||
|
||||
class AuthPluginTest(utils.TestCase):
|
||||
@mock.patch.object(requests, "request")
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points")
|
||||
def test_auth_system_success(self, mock_iter_entry_points, mock_request):
|
||||
"""Test that we can authenticate using the auth system."""
|
||||
class MockEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self):
|
||||
return FakePlugin
|
||||
|
||||
class FakePlugin(auth_plugin.BaseAuthPlugin):
|
||||
def authenticate(self, cls, auth_url):
|
||||
cls._authenticate(auth_url, {"fake": "me"})
|
||||
|
||||
mock_iter_entry_points.side_effect = lambda _t: [
|
||||
MockEntrypoint("fake", "fake", ["FakePlugin"])]
|
||||
|
||||
mock_request.side_effect = mock_http_request()
|
||||
|
||||
auth_plugin.discover_auth_systems()
|
||||
plugin = auth_plugin.load_plugin("fake")
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
utils.AUTH_URL_V2, auth_system="fake",
|
||||
auth_plugin=plugin)
|
||||
cs.client.authenticate()
|
||||
|
||||
headers = requested_headers(cs)
|
||||
token_url = cs.client.auth_url + "/tokens"
|
||||
|
||||
mock_request.assert_called_with(
|
||||
"POST",
|
||||
token_url,
|
||||
headers=headers,
|
||||
data='{"fake": "me"}',
|
||||
allow_redirects=True,
|
||||
**self.TEST_REQUEST_BASE)
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points")
|
||||
def test_discover_auth_system_options(self, mock_iter_entry_points):
|
||||
"""Test that we can load the auth system options."""
|
||||
class FakePlugin(auth_plugin.BaseAuthPlugin):
|
||||
@staticmethod
|
||||
def add_opts(parser):
|
||||
parser.add_argument('--auth_system_opt',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help="Fake option")
|
||||
return parser
|
||||
|
||||
class MockEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self):
|
||||
return FakePlugin
|
||||
|
||||
mock_iter_entry_points.side_effect = lambda _t: [
|
||||
MockEntrypoint("fake", "fake", ["FakePlugin"])]
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
auth_plugin.discover_auth_systems()
|
||||
auth_plugin.load_auth_system_opts(parser)
|
||||
opts, args = parser.parse_known_args(['--auth_system_opt'])
|
||||
|
||||
self.assertTrue(opts.auth_system_opt)
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points")
|
||||
def test_parse_auth_system_options(self, mock_iter_entry_points):
|
||||
"""Test that we can parse the auth system options."""
|
||||
class MockEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self):
|
||||
return FakePlugin
|
||||
|
||||
class FakePlugin(auth_plugin.BaseAuthPlugin):
|
||||
def __init__(self):
|
||||
self.opts = {"fake_argument": True}
|
||||
|
||||
def parse_opts(self, args):
|
||||
return self.opts
|
||||
|
||||
mock_iter_entry_points.side_effect = lambda _t: [
|
||||
MockEntrypoint("fake", "fake", ["FakePlugin"])]
|
||||
|
||||
auth_plugin.discover_auth_systems()
|
||||
plugin = auth_plugin.load_plugin("fake")
|
||||
|
||||
plugin.parse_opts([])
|
||||
self.assertIn("fake_argument", plugin.opts)
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points")
|
||||
def test_auth_system_defining_url(self, mock_iter_entry_points):
|
||||
"""Test the auth_system defining an url."""
|
||||
class MockEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self):
|
||||
return FakePlugin
|
||||
|
||||
class FakePlugin(auth_plugin.BaseAuthPlugin):
|
||||
def get_auth_url(self):
|
||||
return "http://faked/v2.0"
|
||||
|
||||
mock_iter_entry_points.side_effect = lambda _t: [
|
||||
MockEntrypoint("fake", "fake", ["FakePlugin"])]
|
||||
|
||||
auth_plugin.discover_auth_systems()
|
||||
plugin = auth_plugin.load_plugin("fake")
|
||||
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
auth_system="fakewithauthurl",
|
||||
auth_plugin=plugin)
|
||||
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):
|
||||
"""Test that no authenticate raises a proper exception."""
|
||||
class MockEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self):
|
||||
return FakePlugin
|
||||
|
||||
class FakePlugin(auth_plugin.BaseAuthPlugin):
|
||||
pass
|
||||
|
||||
mock_iter_entry_points.side_effect = lambda _t: [
|
||||
MockEntrypoint("fake", "fake", ["FakePlugin"])]
|
||||
|
||||
auth_plugin.discover_auth_systems()
|
||||
plugin = auth_plugin.load_plugin("fake")
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.EndpointNotFound,
|
||||
client.Client, "username", "password", "project_id",
|
||||
auth_system="fake", auth_plugin=plugin)
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points")
|
||||
def test_exception_if_no_url(self, mock_iter_entry_points):
|
||||
"""Test that no auth_url at all raises exception."""
|
||||
class MockEntrypoint(pkg_resources.EntryPoint):
|
||||
def load(self):
|
||||
return FakePlugin
|
||||
|
||||
class FakePlugin(auth_plugin.BaseAuthPlugin):
|
||||
pass
|
||||
|
||||
mock_iter_entry_points.side_effect = lambda _t: [
|
||||
MockEntrypoint("fake", "fake", ["FakePlugin"])]
|
||||
|
||||
auth_plugin.discover_auth_systems()
|
||||
plugin = auth_plugin.load_plugin("fake")
|
||||
|
||||
self.assertRaises(
|
||||
exceptions.EndpointNotFound,
|
||||
client.Client, "username", "password", "project_id",
|
||||
auth_system="fake", auth_plugin=plugin)
|
||||
69
awx/lib/site-packages/novaclient/tests/unit/test_base.py
Normal file
69
awx/lib/site-packages/novaclient/tests/unit/test_base.py
Normal file
@ -0,0 +1,69 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient import base
|
||||
from novaclient import exceptions
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.tests.unit.v2 import fakes
|
||||
from novaclient.v2 import flavors
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
|
||||
|
||||
class BaseTest(utils.TestCase):
|
||||
|
||||
def test_resource_repr(self):
|
||||
r = base.Resource(None, dict(foo="bar", baz="spam"))
|
||||
self.assertEqual("<Resource baz=spam, foo=bar>", repr(r))
|
||||
|
||||
def test_getid(self):
|
||||
self.assertEqual(4, base.getid(4))
|
||||
|
||||
class TmpObject(object):
|
||||
id = 4
|
||||
self.assertEqual(4, base.getid(TmpObject))
|
||||
|
||||
def test_resource_lazy_getattr(self):
|
||||
f = flavors.Flavor(cs.flavors, {'id': 1})
|
||||
self.assertEqual('256 mb server', f.name)
|
||||
cs.assert_called('GET', '/flavors/1')
|
||||
|
||||
# Missing stuff still fails after a second get
|
||||
self.assertRaises(AttributeError, getattr, f, 'blahblah')
|
||||
|
||||
def test_eq(self):
|
||||
# Two resources of the same type with the same id: equal
|
||||
r1 = base.Resource(None, {'id': 1, 'name': 'hi'})
|
||||
r2 = base.Resource(None, {'id': 1, 'name': 'hello'})
|
||||
self.assertEqual(r1, r2)
|
||||
|
||||
# Two resoruces of different types: never equal
|
||||
r1 = base.Resource(None, {'id': 1})
|
||||
r2 = flavors.Flavor(None, {'id': 1})
|
||||
self.assertNotEqual(r1, r2)
|
||||
|
||||
# Two resources with no ID: equal if their info is equal
|
||||
r1 = base.Resource(None, {'name': 'joe', 'age': 12})
|
||||
r2 = base.Resource(None, {'name': 'joe', 'age': 12})
|
||||
self.assertEqual(r1, r2)
|
||||
|
||||
def test_findall_invalid_attribute(self):
|
||||
# Make sure findall with an invalid attribute doesn't cause errors.
|
||||
# The following should not raise an exception.
|
||||
cs.flavors.findall(vegetable='carrot')
|
||||
|
||||
# However, find() should raise an error
|
||||
self.assertRaises(exceptions.NotFound,
|
||||
cs.flavors.find,
|
||||
vegetable='carrot')
|
||||
390
awx/lib/site-packages/novaclient/tests/unit/test_client.py
Normal file
390
awx/lib/site-packages/novaclient/tests/unit/test_client.py
Normal file
@ -0,0 +1,390 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import json
|
||||
import logging
|
||||
import socket
|
||||
|
||||
import fixtures
|
||||
import mock
|
||||
import requests
|
||||
|
||||
import novaclient.client
|
||||
import novaclient.extension
|
||||
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")
|
||||
def test_get(self, mock_http_adapter):
|
||||
mock_http_adapter.side_effect = lambda: mock.Mock()
|
||||
pool = novaclient.client._ClientConnectionPool()
|
||||
self.assertEqual(pool.get("abc"), pool.get("abc"))
|
||||
self.assertNotEqual(pool.get("abc"), pool.get("def"))
|
||||
|
||||
|
||||
class ClientTest(utils.TestCase):
|
||||
|
||||
def test_client_with_timeout(self):
|
||||
instance = novaclient.client.HTTPClient(user='user',
|
||||
password='password',
|
||||
projectid='project',
|
||||
timeout=2,
|
||||
auth_url="http://www.blah.com")
|
||||
self.assertEqual(2, instance.timeout)
|
||||
mock_request = mock.Mock()
|
||||
mock_request.return_value = requests.Response()
|
||||
mock_request.return_value.status_code = 200
|
||||
mock_request.return_value.headers = {
|
||||
'x-server-management-url': 'blah.com',
|
||||
'x-auth-token': 'blah',
|
||||
}
|
||||
with mock.patch('requests.request', mock_request):
|
||||
instance.authenticate()
|
||||
requests.request.assert_called_with(
|
||||
mock.ANY, mock.ANY, timeout=2, headers=mock.ANY,
|
||||
verify=mock.ANY)
|
||||
|
||||
def test_client_reauth(self):
|
||||
instance = novaclient.client.HTTPClient(user='user',
|
||||
password='password',
|
||||
projectid='project',
|
||||
timeout=2,
|
||||
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)
|
||||
with mock.patch('requests.request', mock_request):
|
||||
try:
|
||||
instance.get('/servers/detail')
|
||||
except Exception:
|
||||
pass
|
||||
get_headers = {'X-Auth-Project-Id': 'project',
|
||||
'X-Auth-Token': 'foobar',
|
||||
'User-Agent': 'python-novaclient',
|
||||
'Accept': 'application/json'}
|
||||
reauth_headers = {'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'python-novaclient'}
|
||||
data = {
|
||||
"auth": {
|
||||
"tenantName": "project",
|
||||
"passwordCredentials": {
|
||||
"username": "user",
|
||||
"password": "password"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expected = [mock.call('GET',
|
||||
'http://example.com/servers/detail',
|
||||
timeout=mock.ANY,
|
||||
headers=get_headers,
|
||||
verify=mock.ANY),
|
||||
mock.call('POST', 'http://www.blah.com/tokens',
|
||||
timeout=mock.ANY,
|
||||
headers=reauth_headers,
|
||||
allow_redirects=mock.ANY,
|
||||
data=mock.ANY,
|
||||
verify=mock.ANY)]
|
||||
self.assertEqual(expected, mock_request.call_args_list)
|
||||
token_post_call = mock_request.call_args_list[1]
|
||||
self.assertEqual(data, json.loads(token_post_call[1]['data']))
|
||||
|
||||
@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
|
||||
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
|
||||
# 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')
|
||||
self.assertEqual(output, novaclient.v2.client.Client)
|
||||
|
||||
def test_get_client_class_v2(self):
|
||||
output = novaclient.client.get_client_class('2')
|
||||
self.assertEqual(output, novaclient.v2.client.Client)
|
||||
|
||||
def test_get_client_class_v2_int(self):
|
||||
output = novaclient.client.get_client_class(2)
|
||||
self.assertEqual(output, novaclient.v2.client.Client)
|
||||
|
||||
def test_get_client_class_v1_1(self):
|
||||
output = novaclient.client.get_client_class('1.1')
|
||||
self.assertEqual(output, novaclient.v2.client.Client)
|
||||
|
||||
def test_get_client_class_unknown(self):
|
||||
self.assertRaises(novaclient.exceptions.UnsupportedVersion,
|
||||
novaclient.client.get_client_class, '0')
|
||||
|
||||
def test_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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
def test_client_set_management_url_v1_1(self):
|
||||
cs = novaclient.v2.client.Client("user", "password", "project_id",
|
||||
auth_url="foo/v2")
|
||||
cs.set_management_url("blabla")
|
||||
self.assertEqual("blabla", cs.client.management_url)
|
||||
|
||||
def test_client_get_reset_timings_v1_1(self):
|
||||
cs = novaclient.v2.client.Client("user", "password", "project_id",
|
||||
auth_url="foo/v2")
|
||||
self.assertEqual(0, len(cs.get_timings()))
|
||||
cs.client.times.append("somevalue")
|
||||
self.assertEqual(1, len(cs.get_timings()))
|
||||
self.assertEqual("somevalue", cs.get_timings()[0])
|
||||
|
||||
cs.reset_timings()
|
||||
self.assertEqual(0, len(cs.get_timings()))
|
||||
|
||||
@mock.patch('novaclient.client.HTTPClient')
|
||||
def test_contextmanager_v1_1(self, mock_http_client):
|
||||
fake_client = mock.Mock()
|
||||
mock_http_client.return_value = fake_client
|
||||
with novaclient.v2.client.Client("user", "password", "project_id",
|
||||
auth_url="foo/v2"):
|
||||
pass
|
||||
self.assertTrue(fake_client.open_session.called)
|
||||
self.assertTrue(fake_client.close_session.called)
|
||||
|
||||
def test_get_password_simple(self):
|
||||
cs = novaclient.client.HTTPClient("user", "password", "", "")
|
||||
cs.password_func = mock.Mock()
|
||||
self.assertEqual("password", cs._get_password())
|
||||
self.assertFalse(cs.password_func.called)
|
||||
|
||||
def test_get_password_none(self):
|
||||
cs = novaclient.client.HTTPClient("user", None, "", "")
|
||||
self.assertIsNone(cs._get_password())
|
||||
|
||||
def test_get_password_func(self):
|
||||
cs = novaclient.client.HTTPClient("user", None, "", "")
|
||||
cs.password_func = mock.Mock(return_value="password")
|
||||
self.assertEqual("password", cs._get_password())
|
||||
cs.password_func.assert_called_once_with()
|
||||
|
||||
cs.password_func = mock.Mock()
|
||||
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("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("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):
|
||||
fake_session = mock.Mock()
|
||||
mock_session.return_value = fake_session
|
||||
cs = novaclient.client.HTTPClient("user", None, "", "")
|
||||
cs.open_session()
|
||||
self.assertEqual(cs._session, fake_session)
|
||||
cs.close_session()
|
||||
self.assertIsNone(cs._session)
|
||||
|
||||
def test_session_connection_pool(self):
|
||||
cs = novaclient.client.HTTPClient("user", None, "",
|
||||
"", connection_pool=True)
|
||||
cs.open_session()
|
||||
self.assertIsNone(cs._session)
|
||||
cs.close_session()
|
||||
self.assertIsNone(cs._session)
|
||||
|
||||
def test_get_session(self):
|
||||
cs = novaclient.client.HTTPClient("user", None, "", "")
|
||||
self.assertIsNone(cs._get_session("http://nooooooooo.com"))
|
||||
|
||||
@mock.patch("novaclient.client.requests.Session")
|
||||
def test_get_session_open_session(self, mock_session):
|
||||
fake_session = mock.Mock()
|
||||
mock_session.return_value = fake_session
|
||||
cs = novaclient.client.HTTPClient("user", None, "", "")
|
||||
cs.open_session()
|
||||
self.assertEqual(fake_session, cs._get_session("http://example.com"))
|
||||
|
||||
@mock.patch("novaclient.client.requests.Session")
|
||||
@mock.patch("novaclient.client._ClientConnectionPool")
|
||||
def test_get_session_connection_pool(self, mock_pool, mock_session):
|
||||
service_url = "http://example.com"
|
||||
|
||||
pool = mock.MagicMock()
|
||||
pool.get.return_value = "http_adapter"
|
||||
mock_pool.return_value = pool
|
||||
cs = novaclient.client.HTTPClient("user", None, "",
|
||||
"", connection_pool=True)
|
||||
cs._current_url = "http://another.com"
|
||||
|
||||
session = cs._get_session(service_url)
|
||||
self.assertEqual(session, mock_session.return_value)
|
||||
pool.get.assert_called_once_with(service_url)
|
||||
mock_session().mount.assert_called_once_with(service_url,
|
||||
'http_adapter')
|
||||
|
||||
def test_init_without_connection_pool(self):
|
||||
cs = novaclient.client.HTTPClient("user", None, "", "")
|
||||
self.assertIsNone(cs._connection_pool)
|
||||
|
||||
@mock.patch("novaclient.client._ClientConnectionPool")
|
||||
def test_init_with_proper_connection_pool(self, mock_pool):
|
||||
fake_pool = mock.Mock()
|
||||
mock_pool.return_value = fake_pool
|
||||
cs = novaclient.client.HTTPClient("user", None, "",
|
||||
connection_pool=True)
|
||||
self.assertEqual(cs._connection_pool, fake_pool)
|
||||
|
||||
def test_log_req(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
|
||||
cs.http_log_req('GET', '/foo', {'headers': {}})
|
||||
cs.http_log_req('GET', '/foo', {'headers':
|
||||
{'X-Auth-Token': 'totally_bogus'}})
|
||||
cs.http_log_req('GET', '/foo', {'headers':
|
||||
{'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')
|
||||
|
||||
self.assertIn("REQ: curl -g -i '/foo' -X GET", output)
|
||||
self.assertIn(
|
||||
"REQ: curl -g -i '/foo' -X GET -H "
|
||||
'"X-Auth-Token: {SHA1}b42162b6ffdbd7c3c37b7c95b7ba9f51dda0236d"',
|
||||
output)
|
||||
self.assertIn(
|
||||
"REQ: curl -g -i '/foo' -X GET -H "
|
||||
'"X-Auth-Token: {SHA1}b42162b6ffdbd7c3c37b7c95b7ba9f51dda0236d"'
|
||||
' -H "X-Foo: bar"',
|
||||
output)
|
||||
self.assertIn(
|
||||
"REQ: curl -g -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)
|
||||
80
awx/lib/site-packages/novaclient/tests/unit/test_discover.py
Normal file
80
awx/lib/site-packages/novaclient/tests/unit/test_discover.py
Normal file
@ -0,0 +1,80 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import imp
|
||||
import inspect
|
||||
|
||||
import mock
|
||||
import pkg_resources
|
||||
|
||||
import novaclient.shell
|
||||
from novaclient.tests.unit import utils
|
||||
|
||||
|
||||
class DiscoverTest(utils.TestCase):
|
||||
|
||||
def test_discover_via_entry_points(self):
|
||||
|
||||
def mock_iter_entry_points(group):
|
||||
if group == 'novaclient.extension':
|
||||
fake_ep = mock.Mock()
|
||||
fake_ep.name = 'foo'
|
||||
fake_ep.module = imp.new_module('foo')
|
||||
fake_ep.load.return_value = fake_ep.module
|
||||
return [fake_ep]
|
||||
|
||||
@mock.patch.object(pkg_resources, 'iter_entry_points',
|
||||
mock_iter_entry_points)
|
||||
def test():
|
||||
shell = novaclient.shell.OpenStackComputeShell()
|
||||
for name, module in shell._discover_via_entry_points():
|
||||
self.assertEqual('foo', name)
|
||||
self.assertTrue(inspect.ismodule(module))
|
||||
|
||||
test()
|
||||
|
||||
def test_discover_extensions(self):
|
||||
|
||||
def mock_discover_via_python_path(self):
|
||||
yield 'foo', imp.new_module('foo')
|
||||
|
||||
def mock_discover_via_contrib_path(self, version):
|
||||
yield 'bar', imp.new_module('bar')
|
||||
|
||||
def mock_discover_via_entry_points(self):
|
||||
yield 'baz', imp.new_module('baz')
|
||||
|
||||
@mock.patch.object(novaclient.shell.OpenStackComputeShell,
|
||||
'_discover_via_python_path',
|
||||
mock_discover_via_python_path)
|
||||
@mock.patch.object(novaclient.shell.OpenStackComputeShell,
|
||||
'_discover_via_contrib_path',
|
||||
mock_discover_via_contrib_path)
|
||||
@mock.patch.object(novaclient.shell.OpenStackComputeShell,
|
||||
'_discover_via_entry_points',
|
||||
mock_discover_via_entry_points)
|
||||
def test():
|
||||
shell = novaclient.shell.OpenStackComputeShell()
|
||||
extensions = shell._discover_extensions('1.1')
|
||||
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)):
|
||||
ext = sorted_extensions[i]
|
||||
name = names[i]
|
||||
self.assertEqual(ext.name, name)
|
||||
self.assertTrue(inspect.ismodule(ext.module))
|
||||
|
||||
test()
|
||||
217
awx/lib/site-packages/novaclient/tests/unit/test_http.py
Normal file
217
awx/lib/site-packages/novaclient/tests/unit/test_http.py
Normal file
@ -0,0 +1,217 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
import requests
|
||||
import six
|
||||
|
||||
from novaclient import client
|
||||
from novaclient import exceptions
|
||||
from novaclient.tests.unit import utils
|
||||
|
||||
|
||||
fake_response = utils.TestResponse({
|
||||
"status_code": 200,
|
||||
"text": '{"hi": "there"}',
|
||||
})
|
||||
mock_request = mock.Mock(return_value=(fake_response))
|
||||
|
||||
refused_response = utils.TestResponse({
|
||||
"status_code": 400,
|
||||
"text": '[Errno 111] Connection refused',
|
||||
})
|
||||
refused_mock_request = mock.Mock(return_value=(refused_response))
|
||||
|
||||
bad_req_response = utils.TestResponse({
|
||||
"status_code": 400,
|
||||
"text": '',
|
||||
})
|
||||
bad_req_mock_request = mock.Mock(return_value=(bad_req_response))
|
||||
|
||||
unknown_error_response = utils.TestResponse({
|
||||
"status_code": 503,
|
||||
"text": '',
|
||||
})
|
||||
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",
|
||||
"project_id",
|
||||
utils.AUTH_URL_V2)
|
||||
return cl
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
class ClientTest(utils.TestCase):
|
||||
|
||||
def test_get(self):
|
||||
cl = get_authed_client()
|
||||
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
@mock.patch('time.time', mock.Mock(return_value=1234))
|
||||
def test_get_call():
|
||||
resp, body = cl.get("/hi")
|
||||
headers = {"X-Auth-Token": "token",
|
||||
"X-Auth-Project-Id": "project_id",
|
||||
"User-Agent": cl.USER_AGENT,
|
||||
'Accept': 'application/json'}
|
||||
mock_request.assert_called_with(
|
||||
"GET",
|
||||
"http://example.com/hi",
|
||||
headers=headers,
|
||||
**self.TEST_REQUEST_BASE)
|
||||
# Automatic JSON parsing
|
||||
self.assertEqual({"hi": "there"}, body)
|
||||
|
||||
test_get_call()
|
||||
|
||||
def test_post(self):
|
||||
cl = get_authed_client()
|
||||
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_post_call():
|
||||
cl.post("/hi", body=[1, 2, 3])
|
||||
headers = {
|
||||
"X-Auth-Token": "token",
|
||||
"X-Auth-Project-Id": "project_id",
|
||||
"Content-Type": "application/json",
|
||||
'Accept': 'application/json',
|
||||
"User-Agent": cl.USER_AGENT
|
||||
}
|
||||
mock_request.assert_called_with(
|
||||
"POST",
|
||||
"http://example.com/hi",
|
||||
headers=headers,
|
||||
data='[1, 2, 3]',
|
||||
**self.TEST_REQUEST_BASE)
|
||||
|
||||
test_post_call()
|
||||
|
||||
def test_auth_failure(self):
|
||||
cl = get_client()
|
||||
|
||||
# response must not have x-server-management-url header
|
||||
@mock.patch.object(requests.Session, "request", mock_request)
|
||||
def test_auth_call():
|
||||
self.assertRaises(exceptions.AuthorizationFailure, cl.authenticate)
|
||||
|
||||
test_auth_call()
|
||||
|
||||
def test_auth_failure_due_to_miss_of_auth_url(self):
|
||||
cl = client.HTTPClient("username", "password")
|
||||
|
||||
self.assertRaises(exceptions.AuthorizationFailure, cl.authenticate)
|
||||
|
||||
def test_connection_refused(self):
|
||||
cl = get_client()
|
||||
|
||||
@mock.patch.object(requests, "request", refused_mock_request)
|
||||
def test_refused_call():
|
||||
self.assertRaises(exceptions.ConnectionRefused, cl.get, "/hi")
|
||||
|
||||
test_refused_call()
|
||||
|
||||
def test_bad_request(self):
|
||||
cl = get_client()
|
||||
|
||||
@mock.patch.object(requests, "request", bad_req_mock_request)
|
||||
def test_refused_call():
|
||||
self.assertRaises(exceptions.BadRequest, cl.get, "/hi")
|
||||
|
||||
test_refused_call()
|
||||
|
||||
def test_client_logger(self):
|
||||
cl1 = client.HTTPClient("username", "password", "project_id",
|
||||
"auth_test", http_log_debug=True)
|
||||
self.assertEqual(1, len(cl1._logger.handlers))
|
||||
|
||||
cl2 = client.HTTPClient("username", "password", "project_id",
|
||||
"auth_test", http_log_debug=True)
|
||||
self.assertEqual(1, len(cl2._logger.handlers))
|
||||
|
||||
@mock.patch.object(requests, 'request', unknown_error_mock_request)
|
||||
def test_unknown_server_error(self):
|
||||
cl = get_client()
|
||||
# This would be cleaner with the context manager version of
|
||||
# assertRaises or assertRaisesRegexp, but both only appeared in
|
||||
# Python 2.7 and testtools doesn't match that implementation yet
|
||||
try:
|
||||
cl.get('/hi')
|
||||
except exceptions.ClientException as exc:
|
||||
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")
|
||||
@ -0,0 +1,73 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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 keystoneclient import fixture
|
||||
|
||||
from novaclient import exceptions
|
||||
from novaclient import service_catalog
|
||||
from novaclient.tests.unit import utils
|
||||
|
||||
|
||||
SERVICE_CATALOG = fixture.V2Token()
|
||||
SERVICE_CATALOG.set_scope()
|
||||
|
||||
_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):
|
||||
def test_building_a_service_catalog(self):
|
||||
sc = service_catalog.ServiceCatalog(SERVICE_CATALOG)
|
||||
|
||||
self.assertRaises(exceptions.AmbiguousEndpoints, sc.url_for,
|
||||
service_type='compute')
|
||||
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')
|
||||
|
||||
def test_building_a_service_catalog_insensitive_case(self):
|
||||
sc = service_catalog.ServiceCatalog(SERVICE_CATALOG)
|
||||
# Matching south (and catalog has South).
|
||||
self.assertRaises(exceptions.AmbiguousEndpoints, sc.url_for,
|
||||
'region', 'south', service_type='volume')
|
||||
|
||||
def test_alternate_service_type(self):
|
||||
sc = service_catalog.ServiceCatalog(SERVICE_CATALOG)
|
||||
|
||||
self.assertRaises(exceptions.AmbiguousEndpoints, sc.url_for,
|
||||
service_type='volume')
|
||||
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')
|
||||
389
awx/lib/site-packages/novaclient/tests/unit/test_shell.py
Normal file
389
awx/lib/site-packages/novaclient/tests/unit/test_shell.py
Normal file
@ -0,0 +1,389 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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 distutils.version as dist_version
|
||||
import re
|
||||
import sys
|
||||
|
||||
import fixtures
|
||||
from keystoneclient import fixture
|
||||
import mock
|
||||
import prettytable
|
||||
import requests_mock
|
||||
import six
|
||||
from testtools import matchers
|
||||
|
||||
import novaclient.client
|
||||
from novaclient import exceptions
|
||||
import novaclient.shell
|
||||
from novaclient.tests.unit import utils
|
||||
|
||||
FAKE_ENV = {'OS_USERNAME': 'username',
|
||||
'OS_PASSWORD': 'password',
|
||||
'OS_TENANT_NAME': 'tenant_name',
|
||||
'OS_AUTH_URL': 'http://no.where/v2.0'}
|
||||
|
||||
FAKE_ENV2 = {'OS_USER_ID': 'user_id',
|
||||
'OS_PASSWORD': 'password',
|
||||
'OS_TENANT_ID': 'tenant_id',
|
||||
'OS_AUTH_URL': 'http://no.where/v2.0'}
|
||||
|
||||
FAKE_ENV3 = {'OS_USER_ID': 'user_id',
|
||||
'OS_PASSWORD': 'password',
|
||||
'OS_TENANT_ID': 'tenant_id',
|
||||
'OS_AUTH_URL': 'http://no.where/v2.0',
|
||||
'NOVA_ENDPOINT_TYPE': 'novaURL',
|
||||
'OS_ENDPOINT_TYPE': 'osURL'}
|
||||
|
||||
FAKE_ENV4 = {'OS_USER_ID': 'user_id',
|
||||
'OS_PASSWORD': 'password',
|
||||
'OS_TENANT_ID': 'tenant_id',
|
||||
'OS_AUTH_URL': 'http://no.where/v2.0',
|
||||
'NOVA_ENDPOINT_TYPE': 'internal',
|
||||
'OS_ENDPOINT_TYPE': 'osURL'}
|
||||
|
||||
|
||||
def _create_ver_list(versions):
|
||||
return {'versions': {'values': versions}}
|
||||
|
||||
|
||||
class ParserTest(utils.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ParserTest, self).setUp()
|
||||
self.parser = novaclient.shell.NovaClientArgumentParser()
|
||||
|
||||
def test_ambiguous_option(self):
|
||||
self.parser.add_argument('--tic')
|
||||
self.parser.add_argument('--tac')
|
||||
|
||||
try:
|
||||
self.parser.parse_args(['--t'])
|
||||
except SystemExit as err:
|
||||
self.assertEqual(2, err.code)
|
||||
else:
|
||||
self.fail('SystemExit not raised')
|
||||
|
||||
def test_not_really_ambiguous_option(self):
|
||||
# current/deprecated forms of the same option
|
||||
self.parser.add_argument('--tic-tac', action="store_true")
|
||||
self.parser.add_argument('--tic_tac', action="store_true")
|
||||
args = self.parser.parse_args(['--tic'])
|
||||
self.assertTrue(args.tic_tac)
|
||||
|
||||
|
||||
class ShellTest(utils.TestCase):
|
||||
|
||||
_msg_no_tenant_project = ("You must provide a project name or project"
|
||||
" id via --os-project-name, --os-project-id,"
|
||||
" env[OS_PROJECT_ID] or env[OS_PROJECT_NAME]."
|
||||
" You may use os-project and os-tenant"
|
||||
" interchangeably.")
|
||||
|
||||
def make_env(self, exclude=None, fake_env=FAKE_ENV):
|
||||
env = dict((k, v) for k, v in fake_env.items() if k != exclude)
|
||||
self.useFixture(fixtures.MonkeyPatch('os.environ', env))
|
||||
|
||||
def setUp(self):
|
||||
super(ShellTest, self).setUp()
|
||||
self.useFixture(fixtures.MonkeyPatch(
|
||||
'novaclient.client.get_client_class',
|
||||
mock.MagicMock))
|
||||
self.nc_util = mock.patch(
|
||||
'novaclient.openstack.common.cliutils.isunauthenticated').start()
|
||||
self.nc_util.return_value = False
|
||||
|
||||
def shell(self, argstr, exitcodes=(0,)):
|
||||
orig = sys.stdout
|
||||
orig_stderr = sys.stderr
|
||||
try:
|
||||
sys.stdout = six.StringIO()
|
||||
sys.stderr = six.StringIO()
|
||||
_shell = novaclient.shell.OpenStackComputeShell()
|
||||
_shell.main(argstr.split())
|
||||
except SystemExit:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
self.assertIn(exc_value.code, exitcodes)
|
||||
finally:
|
||||
stdout = sys.stdout.getvalue()
|
||||
sys.stdout.close()
|
||||
sys.stdout = orig
|
||||
stderr = sys.stderr.getvalue()
|
||||
sys.stderr.close()
|
||||
sys.stderr = orig_stderr
|
||||
return (stdout, stderr)
|
||||
|
||||
def register_keystone_discovery_fixture(self, mreq):
|
||||
v2_url = "http://no.where/v2.0"
|
||||
v2_version = fixture.V2Discovery(v2_url)
|
||||
mreq.register_uri(
|
||||
'GET', v2_url, json=_create_ver_list([v2_version]),
|
||||
status_code=200)
|
||||
|
||||
def test_help_unknown_command(self):
|
||||
self.assertRaises(exceptions.CommandError, self.shell, 'help foofoo')
|
||||
|
||||
def test_invalid_timeout(self):
|
||||
for f in [0, -1, -10]:
|
||||
cmd_text = '--timeout %s' % (f)
|
||||
stdout, stderr = self.shell(cmd_text, exitcodes=[0, 2])
|
||||
required = [
|
||||
'argument --timeout: %s must be greater than 0' % (f),
|
||||
]
|
||||
for r in required:
|
||||
self.assertIn(r, stderr)
|
||||
|
||||
def test_help(self):
|
||||
required = [
|
||||
'.*?^usage: ',
|
||||
'.*?^\s+root-password\s+Change the admin password',
|
||||
'.*?^See "nova help COMMAND" for help on a specific command',
|
||||
]
|
||||
stdout, stderr = self.shell('help')
|
||||
for r in required:
|
||||
self.assertThat((stdout + stderr),
|
||||
matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
|
||||
|
||||
def test_help_on_subcommand(self):
|
||||
required = [
|
||||
'.*?^usage: nova root-password',
|
||||
'.*?^Change the admin password',
|
||||
'.*?^Positional arguments:',
|
||||
]
|
||||
stdout, stderr = self.shell('help root-password')
|
||||
for r in required:
|
||||
self.assertThat((stdout + stderr),
|
||||
matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
|
||||
|
||||
def test_help_no_options(self):
|
||||
required = [
|
||||
'.*?^usage: ',
|
||||
'.*?^\s+root-password\s+Change the admin password',
|
||||
'.*?^See "nova help COMMAND" for help on a specific command',
|
||||
]
|
||||
stdout, stderr = self.shell('')
|
||||
for r in required:
|
||||
self.assertThat((stdout + stderr),
|
||||
matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
|
||||
|
||||
def test_bash_completion(self):
|
||||
stdout, stderr = self.shell('bash-completion')
|
||||
# just check we have some output
|
||||
required = [
|
||||
'.*--matching',
|
||||
'.*--wrap',
|
||||
'.*help',
|
||||
'.*secgroup-delete-rule',
|
||||
'.*--priority']
|
||||
for r in required:
|
||||
self.assertThat((stdout + stderr),
|
||||
matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
|
||||
|
||||
def test_no_username(self):
|
||||
required = ('You must provide a username or user id'
|
||||
' via --os-username, --os-user-id,'
|
||||
' env[OS_USERNAME] or env[OS_USER_ID]')
|
||||
self.make_env(exclude='OS_USERNAME')
|
||||
try:
|
||||
self.shell('list')
|
||||
except exceptions.CommandError as message:
|
||||
self.assertEqual(required, message.args[0])
|
||||
else:
|
||||
self.fail('CommandError not raised')
|
||||
|
||||
def test_no_user_id(self):
|
||||
required = ('You must provide a username or user id'
|
||||
' via --os-username, --os-user-id,'
|
||||
' env[OS_USERNAME] or env[OS_USER_ID]')
|
||||
self.make_env(exclude='OS_USER_ID', fake_env=FAKE_ENV2)
|
||||
try:
|
||||
self.shell('list')
|
||||
except exceptions.CommandError as message:
|
||||
self.assertEqual(required, message.args[0])
|
||||
else:
|
||||
self.fail('CommandError not raised')
|
||||
|
||||
def test_no_tenant_name(self):
|
||||
required = self._msg_no_tenant_project
|
||||
self.make_env(exclude='OS_TENANT_NAME')
|
||||
try:
|
||||
self.shell('list')
|
||||
except exceptions.CommandError as message:
|
||||
self.assertEqual(required, message.args[0])
|
||||
else:
|
||||
self.fail('CommandError not raised')
|
||||
|
||||
def test_no_tenant_id(self):
|
||||
required = self._msg_no_tenant_project
|
||||
self.make_env(exclude='OS_TENANT_ID', fake_env=FAKE_ENV2)
|
||||
try:
|
||||
self.shell('list')
|
||||
except exceptions.CommandError as message:
|
||||
self.assertEqual(required, message.args[0])
|
||||
else:
|
||||
self.fail('CommandError not raised')
|
||||
|
||||
def test_no_auth_url(self):
|
||||
required = ('You must provide an auth url'
|
||||
' via either --os-auth-url or env[OS_AUTH_URL] or'
|
||||
' specify an auth_system which defines a default url'
|
||||
' with --os-auth-system or env[OS_AUTH_SYSTEM]',)
|
||||
self.make_env(exclude='OS_AUTH_URL')
|
||||
try:
|
||||
self.shell('list')
|
||||
except exceptions.CommandError as message:
|
||||
self.assertEqual(required, message.args)
|
||||
else:
|
||||
self.fail('CommandError not raised')
|
||||
|
||||
@mock.patch('novaclient.client.Client')
|
||||
@requests_mock.Mocker()
|
||||
def test_nova_endpoint_type(self, mock_client, m_requests):
|
||||
self.make_env(fake_env=FAKE_ENV3)
|
||||
self.register_keystone_discovery_fixture(m_requests)
|
||||
self.shell('list')
|
||||
client_kwargs = mock_client.call_args_list[0][1]
|
||||
self.assertEqual(client_kwargs['endpoint_type'], 'novaURL')
|
||||
|
||||
@mock.patch('novaclient.client.Client')
|
||||
@requests_mock.Mocker()
|
||||
def test_endpoint_type_like_other_clients(self, mock_client, m_requests):
|
||||
self.make_env(fake_env=FAKE_ENV4)
|
||||
self.register_keystone_discovery_fixture(m_requests)
|
||||
self.shell('list')
|
||||
client_kwargs = mock_client.call_args_list[0][1]
|
||||
self.assertEqual(client_kwargs['endpoint_type'], 'internalURL')
|
||||
|
||||
@mock.patch('novaclient.client.Client')
|
||||
@requests_mock.Mocker()
|
||||
def test_os_endpoint_type(self, mock_client, m_requests):
|
||||
self.make_env(exclude='NOVA_ENDPOINT_TYPE', fake_env=FAKE_ENV3)
|
||||
self.register_keystone_discovery_fixture(m_requests)
|
||||
self.shell('list')
|
||||
client_kwargs = mock_client.call_args_list[0][1]
|
||||
self.assertEqual(client_kwargs['endpoint_type'], 'osURL')
|
||||
|
||||
@mock.patch('novaclient.client.Client')
|
||||
def test_default_endpoint_type(self, mock_client):
|
||||
self.make_env()
|
||||
self.shell('list')
|
||||
client_kwargs = mock_client.call_args_list[0][1]
|
||||
self.assertEqual(client_kwargs['endpoint_type'], 'publicURL')
|
||||
|
||||
@mock.patch('sys.stdin', side_effect=mock.MagicMock)
|
||||
@mock.patch('getpass.getpass', return_value='password')
|
||||
@requests_mock.Mocker()
|
||||
def test_password(self, mock_getpass, mock_stdin, m_requests):
|
||||
mock_stdin.encoding = "utf-8"
|
||||
|
||||
# default output of empty tables differs depending between prettytable
|
||||
# versions
|
||||
if (hasattr(prettytable, '__version__') and
|
||||
dist_version.StrictVersion(prettytable.__version__) <
|
||||
dist_version.StrictVersion('0.7.2')):
|
||||
ex = '\n'
|
||||
else:
|
||||
ex = '\n'.join([
|
||||
'+----+------+--------+------------+-------------+----------+',
|
||||
'| ID | Name | Status | Task State | Power State | Networks |',
|
||||
'+----+------+--------+------------+-------------+----------+',
|
||||
'+----+------+--------+------------+-------------+----------+',
|
||||
''
|
||||
])
|
||||
self.make_env(exclude='OS_PASSWORD')
|
||||
self.register_keystone_discovery_fixture(m_requests)
|
||||
stdout, stderr = self.shell('list')
|
||||
self.assertEqual((stdout + stderr), ex)
|
||||
|
||||
@mock.patch('sys.stdin', side_effect=mock.MagicMock)
|
||||
@mock.patch('getpass.getpass', side_effect=EOFError)
|
||||
def test_no_password(self, mock_getpass, mock_stdin):
|
||||
required = ('Expecting a password provided'
|
||||
' via either --os-password, env[OS_PASSWORD],'
|
||||
' or prompted response',)
|
||||
self.make_env(exclude='OS_PASSWORD')
|
||||
try:
|
||||
self.shell('list')
|
||||
except exceptions.CommandError as message:
|
||||
self.assertEqual(required, message.args)
|
||||
else:
|
||||
self.fail('CommandError not raised')
|
||||
|
||||
def _test_service_type(self, version, service_type, mock_client):
|
||||
if version is None:
|
||||
cmd = 'list'
|
||||
else:
|
||||
cmd = ('--service_type %s --os-compute-api-version %s list' %
|
||||
(service_type, version))
|
||||
self.make_env()
|
||||
self.shell(cmd)
|
||||
_, client_kwargs = mock_client.call_args_list[0]
|
||||
self.assertEqual(service_type, client_kwargs['service_type'])
|
||||
|
||||
@mock.patch('novaclient.client.Client')
|
||||
def test_default_service_type(self, mock_client):
|
||||
self._test_service_type(None, 'compute', mock_client)
|
||||
|
||||
@mock.patch('novaclient.client.Client')
|
||||
def test_v1_1_service_type(self, mock_client):
|
||||
self._test_service_type('1.1', 'compute', mock_client)
|
||||
|
||||
@mock.patch('novaclient.client.Client')
|
||||
def test_v2_service_type(self, mock_client):
|
||||
self._test_service_type('2', 'compute', mock_client)
|
||||
|
||||
@mock.patch('novaclient.client.Client')
|
||||
def test_v3_service_type(self, mock_client):
|
||||
self._test_service_type('3', 'computev3', mock_client)
|
||||
|
||||
@mock.patch('novaclient.client.Client')
|
||||
def test_v_unknown_service_type(self, mock_client):
|
||||
self._test_service_type('unknown', 'compute', mock_client)
|
||||
|
||||
@mock.patch('sys.argv', ['nova'])
|
||||
@mock.patch('sys.stdout', six.StringIO())
|
||||
@mock.patch('sys.stderr', six.StringIO())
|
||||
def test_main_noargs(self):
|
||||
# Ensure that main works with no command-line arguments
|
||||
try:
|
||||
novaclient.shell.main()
|
||||
except SystemExit:
|
||||
self.fail('Unexpected SystemExit')
|
||||
|
||||
# We expect the normal usage as a result
|
||||
self.assertIn('Command-line interface to the OpenStack Nova API',
|
||||
sys.stdout.getvalue())
|
||||
|
||||
@mock.patch.object(novaclient.shell.OpenStackComputeShell, 'main')
|
||||
def test_main_keyboard_interrupt(self, mock_compute_shell):
|
||||
# Ensure that exit code is 130 for KeyboardInterrupt
|
||||
mock_compute_shell.side_effect = KeyboardInterrupt()
|
||||
try:
|
||||
novaclient.shell.main()
|
||||
except SystemExit as ex:
|
||||
self.assertEqual(ex.code, 130)
|
||||
|
||||
|
||||
class ShellTestKeystoneV3(ShellTest):
|
||||
def make_env(self, exclude=None, fake_env=FAKE_ENV):
|
||||
if 'OS_AUTH_URL' in fake_env:
|
||||
fake_env.update({'OS_AUTH_URL': 'http://no.where/v3'})
|
||||
env = dict((k, v) for k, v in fake_env.items() if k != exclude)
|
||||
self.useFixture(fixtures.MonkeyPatch('os.environ', env))
|
||||
|
||||
def register_keystone_discovery_fixture(self, mreq):
|
||||
v3_url = "http://no.where/v3"
|
||||
v3_version = fixture.V3Discovery(v3_url)
|
||||
mreq.register_uri(
|
||||
'GET', v3_url, json=_create_ver_list([v3_version]),
|
||||
status_code=200)
|
||||
357
awx/lib/site-packages/novaclient/tests/unit/test_utils.py
Normal file
357
awx/lib/site-packages/novaclient/tests/unit/test_utils.py
Normal file
@ -0,0 +1,357 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import sys
|
||||
|
||||
import mock
|
||||
import six
|
||||
|
||||
from novaclient import base
|
||||
from novaclient import exceptions
|
||||
from novaclient.tests.unit import utils as test_utils
|
||||
from novaclient import utils
|
||||
|
||||
UUID = '8e8ec658-c7b0-4243-bdf8-6f7f2952c0d0'
|
||||
|
||||
|
||||
class FakeResource(object):
|
||||
NAME_ATTR = 'name'
|
||||
|
||||
def __init__(self, _id, properties):
|
||||
self.id = _id
|
||||
try:
|
||||
self.name = properties['name']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
class FakeManager(base.ManagerWithFind):
|
||||
|
||||
resource_class = FakeResource
|
||||
|
||||
resources = [
|
||||
FakeResource('1234', {'name': 'entity_one'}),
|
||||
FakeResource('12345', {'name': 'UPPER'}),
|
||||
FakeResource('123456', {'name': 'lower'}),
|
||||
FakeResource('1234567', {'name': 'Mixed'}),
|
||||
FakeResource('12345678', {'name': 'mixed'}),
|
||||
FakeResource(UUID, {'name': 'entity_two'}),
|
||||
FakeResource('5678', {'name': '9876'}),
|
||||
FakeResource('01234', {'name': 'entity_three'})
|
||||
]
|
||||
|
||||
is_alphanum_id_allowed = None
|
||||
|
||||
def __init__(self, alphanum_id_allowed=False):
|
||||
self.is_alphanum_id_allowed = alphanum_id_allowed
|
||||
|
||||
def get(self, resource_id):
|
||||
for resource in self.resources:
|
||||
if resource.id == str(resource_id):
|
||||
return resource
|
||||
raise exceptions.NotFound(resource_id)
|
||||
|
||||
def list(self):
|
||||
return self.resources
|
||||
|
||||
|
||||
class FakeDisplayResource(object):
|
||||
NAME_ATTR = 'display_name'
|
||||
|
||||
def __init__(self, _id, properties):
|
||||
self.id = _id
|
||||
try:
|
||||
self.display_name = properties['display_name']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
class FakeDisplayManager(FakeManager):
|
||||
|
||||
resource_class = FakeDisplayResource
|
||||
|
||||
resources = [
|
||||
FakeDisplayResource('4242', {'display_name': 'entity_three'}),
|
||||
]
|
||||
|
||||
|
||||
class FindResourceTestCase(test_utils.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(FindResourceTestCase, self).setUp()
|
||||
self.manager = FakeManager(None)
|
||||
|
||||
def test_find_none(self):
|
||||
"""Test a few non-valid inputs."""
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
utils.find_resource,
|
||||
self.manager,
|
||||
'asdf')
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
utils.find_resource,
|
||||
self.manager,
|
||||
None)
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
utils.find_resource,
|
||||
self.manager,
|
||||
{})
|
||||
|
||||
def test_find_by_integer_id(self):
|
||||
output = utils.find_resource(self.manager, 1234)
|
||||
self.assertEqual(output, self.manager.get('1234'))
|
||||
|
||||
def test_find_by_str_id(self):
|
||||
output = utils.find_resource(self.manager, '1234')
|
||||
self.assertEqual(output, self.manager.get('1234'))
|
||||
|
||||
def test_find_by_uuid(self):
|
||||
output = utils.find_resource(self.manager, UUID)
|
||||
self.assertEqual(output, self.manager.get(UUID))
|
||||
|
||||
def test_find_by_str_name(self):
|
||||
output = utils.find_resource(self.manager, 'entity_one')
|
||||
self.assertEqual(output, self.manager.get('1234'))
|
||||
|
||||
def test_find_by_str_upper_name(self):
|
||||
output = utils.find_resource(self.manager, 'UPPER')
|
||||
self.assertEqual(output, self.manager.get('12345'))
|
||||
|
||||
def test_find_by_str_lower_name(self):
|
||||
output = utils.find_resource(self.manager, 'lower')
|
||||
self.assertEqual(output, self.manager.get('123456'))
|
||||
|
||||
def test_find_by_str_mix_name(self):
|
||||
output = utils.find_resource(self.manager, 'Mixed')
|
||||
self.assertEqual(output, self.manager.get('1234567'))
|
||||
|
||||
def test_find_by_str_lower_name_mixed(self):
|
||||
output = utils.find_resource(self.manager, 'mixed')
|
||||
self.assertEqual(output, self.manager.get('12345678'))
|
||||
|
||||
def test_find_by_str_display_name(self):
|
||||
display_manager = FakeDisplayManager(None)
|
||||
output = utils.find_resource(display_manager, 'entity_three')
|
||||
self.assertEqual(output, display_manager.get('4242'))
|
||||
|
||||
def test_find_in_alphanum_allowed_manager_by_str_id_(self):
|
||||
alphanum_manager = FakeManager(True)
|
||||
output = utils.find_resource(alphanum_manager, '01234')
|
||||
self.assertEqual(output, alphanum_manager.get('01234'))
|
||||
|
||||
|
||||
class _FakeResult(object):
|
||||
def __init__(self, name, value):
|
||||
self.name = name
|
||||
self.value = value
|
||||
|
||||
|
||||
class PrintResultTestCase(test_utils.TestCase):
|
||||
@mock.patch('sys.stdout', six.StringIO())
|
||||
def test_print_dict(self):
|
||||
dict = {'key': 'value'}
|
||||
utils.print_dict(dict)
|
||||
self.assertEqual('+----------+-------+\n'
|
||||
'| Property | Value |\n'
|
||||
'+----------+-------+\n'
|
||||
'| key | value |\n'
|
||||
'+----------+-------+\n',
|
||||
sys.stdout.getvalue())
|
||||
|
||||
@mock.patch('sys.stdout', six.StringIO())
|
||||
def test_print_dict_wrap(self):
|
||||
dict = {'key1': 'not wrapped',
|
||||
'key2': 'this will be wrapped'}
|
||||
utils.print_dict(dict, wrap=16)
|
||||
self.assertEqual('+----------+--------------+\n'
|
||||
'| Property | Value |\n'
|
||||
'+----------+--------------+\n'
|
||||
'| key1 | not wrapped |\n'
|
||||
'| key2 | this will be |\n'
|
||||
'| | wrapped |\n'
|
||||
'+----------+--------------+\n',
|
||||
sys.stdout.getvalue())
|
||||
|
||||
@mock.patch('sys.stdout', six.StringIO())
|
||||
def test_print_list_sort_by_str(self):
|
||||
objs = [_FakeResult("k1", 1),
|
||||
_FakeResult("k3", 2),
|
||||
_FakeResult("k2", 3)]
|
||||
|
||||
utils.print_list(objs, ["Name", "Value"], sortby_index=0)
|
||||
|
||||
self.assertEqual('+------+-------+\n'
|
||||
'| Name | Value |\n'
|
||||
'+------+-------+\n'
|
||||
'| k1 | 1 |\n'
|
||||
'| k2 | 3 |\n'
|
||||
'| k3 | 2 |\n'
|
||||
'+------+-------+\n',
|
||||
sys.stdout.getvalue())
|
||||
|
||||
@mock.patch('sys.stdout', six.StringIO())
|
||||
def test_print_list_sort_by_integer(self):
|
||||
objs = [_FakeResult("k1", 1),
|
||||
_FakeResult("k3", 2),
|
||||
_FakeResult("k2", 3)]
|
||||
|
||||
utils.print_list(objs, ["Name", "Value"], sortby_index=1)
|
||||
|
||||
self.assertEqual('+------+-------+\n'
|
||||
'| Name | Value |\n'
|
||||
'+------+-------+\n'
|
||||
'| k1 | 1 |\n'
|
||||
'| k3 | 2 |\n'
|
||||
'| k2 | 3 |\n'
|
||||
'+------+-------+\n',
|
||||
sys.stdout.getvalue())
|
||||
|
||||
# without sorting
|
||||
@mock.patch('sys.stdout', six.StringIO())
|
||||
def test_print_list_sort_by_none(self):
|
||||
objs = [_FakeResult("k1", 1),
|
||||
_FakeResult("k3", 3),
|
||||
_FakeResult("k2", 2)]
|
||||
|
||||
utils.print_list(objs, ["Name", "Value"], sortby_index=None)
|
||||
|
||||
self.assertEqual('+------+-------+\n'
|
||||
'| Name | Value |\n'
|
||||
'+------+-------+\n'
|
||||
'| k1 | 1 |\n'
|
||||
'| k3 | 3 |\n'
|
||||
'| k2 | 2 |\n'
|
||||
'+------+-------+\n',
|
||||
sys.stdout.getvalue())
|
||||
|
||||
@mock.patch('sys.stdout', six.StringIO())
|
||||
def test_print_dict_dictionary(self):
|
||||
dict = {'k': {'foo': 'bar'}}
|
||||
utils.print_dict(dict)
|
||||
self.assertEqual('+----------+----------------+\n'
|
||||
'| Property | Value |\n'
|
||||
'+----------+----------------+\n'
|
||||
'| k | {"foo": "bar"} |\n'
|
||||
'+----------+----------------+\n',
|
||||
sys.stdout.getvalue())
|
||||
|
||||
@mock.patch('sys.stdout', six.StringIO())
|
||||
def test_print_dict_list_dictionary(self):
|
||||
dict = {'k': [{'foo': 'bar'}]}
|
||||
utils.print_dict(dict)
|
||||
self.assertEqual('+----------+------------------+\n'
|
||||
'| Property | Value |\n'
|
||||
'+----------+------------------+\n'
|
||||
'| k | [{"foo": "bar"}] |\n'
|
||||
'+----------+------------------+\n',
|
||||
sys.stdout.getvalue())
|
||||
|
||||
@mock.patch('sys.stdout', six.StringIO())
|
||||
def test_print_dict_list(self):
|
||||
dict = {'k': ['foo', 'bar']}
|
||||
utils.print_dict(dict)
|
||||
self.assertEqual('+----------+----------------+\n'
|
||||
'| Property | Value |\n'
|
||||
'+----------+----------------+\n'
|
||||
'| k | ["foo", "bar"] |\n'
|
||||
'+----------+----------------+\n',
|
||||
sys.stdout.getvalue())
|
||||
|
||||
|
||||
class FlattenTestCase(test_utils.TestCase):
|
||||
def test_flattening(self):
|
||||
squashed = utils.flatten_dict(
|
||||
{'a1': {'b1': 1234,
|
||||
'b2': 'string',
|
||||
'b3': set((1, 2, 3)),
|
||||
'b4': {'c1': ['l', 'l', ['l']],
|
||||
'c2': 'string'}},
|
||||
'a2': ['l'],
|
||||
'a3': ('t',)})
|
||||
|
||||
self.assertEqual({'a1_b1': 1234,
|
||||
'a1_b2': 'string',
|
||||
'a1_b3': set([1, 2, 3]),
|
||||
'a1_b4_c1': ['l', 'l', ['l']],
|
||||
'a1_b4_c2': 'string',
|
||||
'a2': ['l'],
|
||||
'a3': ('t',)},
|
||||
squashed)
|
||||
|
||||
def test_pretty_choice_dict(self):
|
||||
d = {}
|
||||
r = utils.pretty_choice_dict(d)
|
||||
self.assertEqual("", r)
|
||||
|
||||
d = {"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3"}
|
||||
r = utils.pretty_choice_dict(d)
|
||||
self.assertEqual("'k1=v1', 'k2=v2', 'k3=v3'", r)
|
||||
|
||||
|
||||
class ValidationsTestCase(test_utils.TestCase):
|
||||
def test_validate_flavor_metadata_keys_with_valid_keys(self):
|
||||
valid_keys = ['key1', 'month.price', 'I-Am:AK-ey.01-', 'spaces and _']
|
||||
utils.validate_flavor_metadata_keys(valid_keys)
|
||||
|
||||
def test_validate_flavor_metadata_keys_with_invalid_keys(self):
|
||||
invalid_keys = ['/1', '?1', '%1', '<', '>', '\1']
|
||||
for key in invalid_keys:
|
||||
try:
|
||||
utils.validate_flavor_metadata_keys([key])
|
||||
self.fail("Invalid key passed validation: %s" % key)
|
||||
except exceptions.CommandError as ce:
|
||||
self.assertIn(key, str(ce))
|
||||
|
||||
|
||||
class ResourceManagerExtraKwargsHookTestCase(test_utils.TestCase):
|
||||
def test_get_resource_manager_extra_kwargs_hook_test(self):
|
||||
do_foo = mock.MagicMock()
|
||||
|
||||
def hook1(args):
|
||||
return {'kwarg1': 'v_hook1'}
|
||||
|
||||
def hook2(args):
|
||||
return {'kwarg1': 'v_hook2'}
|
||||
do_foo.resource_manager_kwargs_hooks = [hook1, hook2]
|
||||
args = {}
|
||||
exc = self.assertRaises(exceptions.NoUniqueMatch,
|
||||
utils.get_resource_manager_extra_kwargs,
|
||||
do_foo,
|
||||
args)
|
||||
except_error = ("Hook 'hook2' is attempting to redefine "
|
||||
"attributes")
|
||||
self.assertIn(except_error, six.text_type(exc))
|
||||
|
||||
|
||||
class DoActionOnManyTestCase(test_utils.TestCase):
|
||||
|
||||
def _test_do_action_on_many(self, side_effect, fail):
|
||||
action = mock.Mock(side_effect=side_effect)
|
||||
|
||||
if fail:
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
utils.do_action_on_many,
|
||||
action, [1, 2], 'success with %s', 'error')
|
||||
else:
|
||||
utils.do_action_on_many(action, [1, 2], 'success with %s', 'error')
|
||||
action.assert_has_calls([mock.call(1), mock.call(2)])
|
||||
|
||||
def test_do_action_on_many_success(self):
|
||||
self._test_do_action_on_many([None, None], fail=False)
|
||||
|
||||
def test_do_action_on_many_first_fails(self):
|
||||
self._test_do_action_on_many([Exception(), None], fail=True)
|
||||
|
||||
def test_do_action_on_many_last_fails(self):
|
||||
self._test_do_action_on_many([None, Exception()], fail=True)
|
||||
128
awx/lib/site-packages/novaclient/tests/unit/utils.py
Normal file
128
awx/lib/site-packages/novaclient/tests/unit/utils.py
Normal file
@ -0,0 +1,128 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
|
||||
import fixtures
|
||||
import mock
|
||||
from oslo.serialization import jsonutils
|
||||
import requests
|
||||
from requests_mock.contrib import fixture as requests_mock_fixture
|
||||
import six
|
||||
import testscenarios
|
||||
import testtools
|
||||
|
||||
AUTH_URL = "http://localhost:5002/auth_url"
|
||||
AUTH_URL_V1 = "http://localhost:5002/auth_url/v1.0"
|
||||
AUTH_URL_V2 = "http://localhost:5002/auth_url/v2.0"
|
||||
|
||||
|
||||
def _patch_mock_to_raise_for_invalid_assert_calls():
|
||||
def raise_for_invalid_assert_calls(wrapped):
|
||||
def wrapper(_self, name):
|
||||
valid_asserts = [
|
||||
'assert_called_with',
|
||||
'assert_called_once_with',
|
||||
'assert_has_calls',
|
||||
'assert_any_calls']
|
||||
|
||||
if name.startswith('assert') and name not in valid_asserts:
|
||||
raise AttributeError('%s is not a valid mock assert method'
|
||||
% name)
|
||||
|
||||
return wrapped(_self, name)
|
||||
return wrapper
|
||||
mock.Mock.__getattr__ = raise_for_invalid_assert_calls(
|
||||
mock.Mock.__getattr__)
|
||||
|
||||
# NOTE(gibi): needs to be called only once at import time
|
||||
# to patch the mock lib
|
||||
_patch_mock_to_raise_for_invalid_assert_calls()
|
||||
|
||||
|
||||
class TestCase(testtools.TestCase):
|
||||
TEST_REQUEST_BASE = {
|
||||
'verify': True,
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(TestCase, self).setUp()
|
||||
if (os.environ.get('OS_STDOUT_CAPTURE') == 'True' or
|
||||
os.environ.get('OS_STDOUT_CAPTURE') == '1'):
|
||||
stdout = self.useFixture(fixtures.StringStream('stdout')).stream
|
||||
self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
|
||||
if (os.environ.get('OS_STDERR_CAPTURE') == 'True' or
|
||||
os.environ.get('OS_STDERR_CAPTURE') == '1'):
|
||||
stderr = self.useFixture(fixtures.StringStream('stderr')).stream
|
||||
self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
|
||||
|
||||
|
||||
class FixturedTestCase(testscenarios.TestWithScenarios, TestCase):
|
||||
|
||||
client_fixture_class = None
|
||||
data_fixture_class = None
|
||||
|
||||
def setUp(self):
|
||||
super(FixturedTestCase, self).setUp()
|
||||
|
||||
self.requests = self.useFixture(requests_mock_fixture.Fixture())
|
||||
self.data_fixture = None
|
||||
self.client_fixture = None
|
||||
self.cs = None
|
||||
|
||||
if 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:
|
||||
fix = self.data_fixture_class(self.requests)
|
||||
self.data_fixture = self.useFixture(fix)
|
||||
|
||||
def assert_called(self, method, path, body=None):
|
||||
self.assertEqual(self.requests.last_request.method, method)
|
||||
self.assertEqual(self.requests.last_request.path_url, path)
|
||||
|
||||
if 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):
|
||||
# json load if the input body to match against is not a string
|
||||
req_data = jsonutils.loads(req_data)
|
||||
self.assertEqual(req_data, body)
|
||||
|
||||
|
||||
class TestResponse(requests.Response):
|
||||
"""
|
||||
Class used to wrap requests.Response and provide some
|
||||
convenience to initialize with a dict
|
||||
"""
|
||||
|
||||
def __init__(self, data):
|
||||
super(TestResponse, self).__init__()
|
||||
self._text = None
|
||||
if isinstance(data, dict):
|
||||
self.status_code = data.get('status_code')
|
||||
self.headers = data.get('headers')
|
||||
# Fake the text attribute to streamline Response creation
|
||||
self._text = data.get('text')
|
||||
else:
|
||||
self.status_code = data
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__dict__ == other.__dict__
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
return self._text
|
||||
151
awx/lib/site-packages/novaclient/tests/unit/v2/contrib/fakes.py
Normal file
151
awx/lib/site-packages/novaclient/tests/unit/v2/contrib/fakes.py
Normal file
@ -0,0 +1,151 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from novaclient.tests.unit.v2 import fakes
|
||||
from novaclient.v2 import client
|
||||
|
||||
|
||||
class FakeClient(fakes.FakeClient):
|
||||
def __init__(self, *args, **kwargs):
|
||||
client.Client.__init__(self, 'username', 'password',
|
||||
'project_id', 'auth_url',
|
||||
extensions=kwargs.get('extensions'))
|
||||
self.client = FakeHTTPClient(**kwargs)
|
||||
|
||||
|
||||
class FakeHTTPClient(fakes.FakeHTTPClient):
|
||||
def get_os_tenant_networks(self):
|
||||
return (200, {}, {
|
||||
'networks': [{"label": "1", "cidr": "10.0.0.0/24",
|
||||
'project_id': '4ffc664c198e435e9853f2538fbcd7a7',
|
||||
'id': '1'}]})
|
||||
|
||||
def get_os_tenant_networks_1(self, **kw):
|
||||
return (200, {}, {
|
||||
'network': {"label": "1", "cidr": "10.0.0.0/24",
|
||||
'project_id': '4ffc664c198e435e9853f2538fbcd7a7',
|
||||
'id': '1'}})
|
||||
|
||||
def post_os_tenant_networks(self, **kw):
|
||||
return (201, {}, {
|
||||
'network': {"label": "1", "cidr": "10.0.0.0/24",
|
||||
'project_id': '4ffc664c198e435e9853f2538fbcd7a7',
|
||||
'id': '1'}})
|
||||
|
||||
def delete_os_tenant_networks_1(self, **kw):
|
||||
return (204, {}, None)
|
||||
|
||||
def get_os_baremetal_nodes(self, **kw):
|
||||
return (
|
||||
200, {}, {
|
||||
'nodes': [
|
||||
{
|
||||
"id": 1,
|
||||
"instance_uuid": None,
|
||||
"interfaces": [],
|
||||
"cpus": 2,
|
||||
"local_gb": 10,
|
||||
"memory_mb": 5,
|
||||
"pm_address": "2.3.4.5",
|
||||
"pm_user": "pmuser",
|
||||
"pm_password": "pmpass",
|
||||
"prov_mac_address": "aa:bb:cc:dd:ee:ff",
|
||||
"prov_vlan_id": 1,
|
||||
"service_host": "somehost",
|
||||
"terminal_port": 8080,
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
def get_os_baremetal_nodes_1(self, **kw):
|
||||
return (
|
||||
200, {}, {
|
||||
'node': {
|
||||
"id": 1,
|
||||
"instance_uuid": None,
|
||||
"pm_address": "1.2.3.4",
|
||||
"interfaces": [],
|
||||
"cpus": 2,
|
||||
"local_gb": 10,
|
||||
"memory_mb": 5,
|
||||
"pm_user": "pmuser",
|
||||
"pm_password": "pmpass",
|
||||
"prov_mac_address": "aa:bb:cc:dd:ee:ff",
|
||||
"prov_vlan_id": 1,
|
||||
"service_host": "somehost",
|
||||
"terminal_port": 8080,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def post_os_baremetal_nodes(self, **kw):
|
||||
return (
|
||||
200, {}, {
|
||||
'node': {
|
||||
"id": 1,
|
||||
"instance_uuid": None,
|
||||
"cpus": 2,
|
||||
"local_gb": 10,
|
||||
"memory_mb": 5,
|
||||
"pm_address": "2.3.4.5",
|
||||
"pm_user": "pmuser",
|
||||
"pm_password": "pmpass",
|
||||
"prov_mac_address": "aa:bb:cc:dd:ee:ff",
|
||||
"prov_vlan_id": 1,
|
||||
"service_host": "somehost",
|
||||
"terminal_port": 8080,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def delete_os_baremetal_nodes_1(self, **kw):
|
||||
return (202, {}, {})
|
||||
|
||||
def post_os_baremetal_nodes_1_action(self, **kw):
|
||||
body = kw['body']
|
||||
action = list(body)[0]
|
||||
if action == "add_interface":
|
||||
return (
|
||||
200, {}, {
|
||||
'interface': {
|
||||
"id": 2,
|
||||
"address": "bb:cc:dd:ee:ff:aa",
|
||||
"datapath_id": 1,
|
||||
"port_no": 2,
|
||||
}
|
||||
}
|
||||
)
|
||||
elif action == "remove_interface":
|
||||
return (202, {}, {})
|
||||
else:
|
||||
return (500, {}, {})
|
||||
|
||||
def post_os_assisted_volume_snapshots(self, **kw):
|
||||
return (202, {}, {'snapshot': {'id': 'blah', 'volumeId': '1'}})
|
||||
|
||||
def delete_os_assisted_volume_snapshots_x(self, **kw):
|
||||
return (202, {}, {})
|
||||
|
||||
def post_os_server_external_events(self, **kw):
|
||||
return (200, {}, {
|
||||
'events': [
|
||||
{'name': 'test-event',
|
||||
'status': 'completed',
|
||||
'tag': 'tag',
|
||||
'server_uuid': 'fake-uuid1'},
|
||||
{'name': 'test-event',
|
||||
'status': 'completed',
|
||||
'tag': 'tag',
|
||||
'server_uuid': 'fake-uuid2'}]})
|
||||
@ -0,0 +1,42 @@
|
||||
# Copyright (C) 2013, Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Assisted volume snapshots - to be used by Cinder and not end users.
|
||||
"""
|
||||
|
||||
from novaclient import extension
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.tests.unit.v2.contrib import fakes
|
||||
from novaclient.v2.contrib import assisted_volume_snapshots as assisted_snaps
|
||||
|
||||
|
||||
extensions = [
|
||||
extension.Extension(assisted_snaps.__name__.split(".")[-1],
|
||||
assisted_snaps),
|
||||
]
|
||||
cs = fakes.FakeClient(extensions=extensions)
|
||||
|
||||
|
||||
class AssistedVolumeSnapshotsTestCase(utils.TestCase):
|
||||
|
||||
def test_create_snap(self):
|
||||
cs.assisted_volume_snapshots.create('1', {})
|
||||
cs.assert_called('POST', '/os-assisted-volume-snapshots')
|
||||
|
||||
def test_delete_snap(self):
|
||||
cs.assisted_volume_snapshots.delete('x', {})
|
||||
cs.assert_called(
|
||||
'DELETE',
|
||||
'/os-assisted-volume-snapshots/x?delete_info={}')
|
||||
@ -0,0 +1,64 @@
|
||||
# Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from novaclient import extension
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.tests.unit.v2.contrib import fakes
|
||||
from novaclient.v2.contrib import baremetal
|
||||
|
||||
|
||||
extensions = [
|
||||
extension.Extension(baremetal.__name__.split(".")[-1], baremetal),
|
||||
]
|
||||
cs = fakes.FakeClient(extensions=extensions)
|
||||
|
||||
|
||||
class BaremetalExtensionTest(utils.TestCase):
|
||||
|
||||
def test_list_nodes(self):
|
||||
nl = cs.baremetal.list()
|
||||
cs.assert_called('GET', '/os-baremetal-nodes')
|
||||
for n in nl:
|
||||
self.assertIsInstance(n, baremetal.BareMetalNode)
|
||||
|
||||
def test_get_node(self):
|
||||
n = cs.baremetal.get(1)
|
||||
cs.assert_called('GET', '/os-baremetal-nodes/1')
|
||||
self.assertIsInstance(n, baremetal.BareMetalNode)
|
||||
|
||||
def test_create_node(self):
|
||||
n = cs.baremetal.create("service_host", 1, 1024, 2048,
|
||||
"aa:bb:cc:dd:ee:ff")
|
||||
cs.assert_called('POST', '/os-baremetal-nodes')
|
||||
self.assertIsInstance(n, baremetal.BareMetalNode)
|
||||
|
||||
def test_delete_node(self):
|
||||
n = cs.baremetal.get(1)
|
||||
cs.baremetal.delete(n)
|
||||
cs.assert_called('DELETE', '/os-baremetal-nodes/1')
|
||||
|
||||
def test_node_add_interface(self):
|
||||
i = cs.baremetal.add_interface(1, "bb:cc:dd:ee:ff:aa", 1, 2)
|
||||
cs.assert_called('POST', '/os-baremetal-nodes/1/action')
|
||||
self.assertIsInstance(i, baremetal.BareMetalNodeInterface)
|
||||
|
||||
def test_node_remove_interface(self):
|
||||
cs.baremetal.remove_interface(1, "bb:cc:dd:ee:ff:aa")
|
||||
cs.assert_called('POST', '/os-baremetal-nodes/1/action')
|
||||
|
||||
def test_node_list_interfaces(self):
|
||||
cs.baremetal.list_interfaces(1)
|
||||
cs.assert_called('GET', '/os-baremetal-nodes/1')
|
||||
@ -0,0 +1,42 @@
|
||||
# Copyright 2013 Rackspace Hosting
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient import extension
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.tests.unit.v2.contrib import fakes
|
||||
from novaclient.v2.contrib import cells
|
||||
|
||||
|
||||
extensions = [
|
||||
extension.Extension(cells.__name__.split(".")[-1],
|
||||
cells),
|
||||
]
|
||||
cs = fakes.FakeClient(extensions=extensions)
|
||||
|
||||
|
||||
class CellsExtensionTests(utils.TestCase):
|
||||
def test_get_cells(self):
|
||||
cell_name = 'child_cell'
|
||||
cs.cells.get(cell_name)
|
||||
cs.assert_called('GET', '/os-cells/%s' % cell_name)
|
||||
|
||||
def test_get_capacities_for_a_given_cell(self):
|
||||
cell_name = 'child_cell'
|
||||
cs.cells.capacities(cell_name)
|
||||
cs.assert_called('GET', '/os-cells/%s/capacities' % cell_name)
|
||||
|
||||
def test_get_capacities_for_all_cells(self):
|
||||
cs.cells.capacities()
|
||||
cs.assert_called('GET', '/os-cells/capacities')
|
||||
@ -0,0 +1,43 @@
|
||||
# Copyright 2013 Rackspace Hosting
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient import extension
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.tests.unit.v2.contrib import fakes
|
||||
from novaclient.v2.contrib import instance_action
|
||||
|
||||
|
||||
extensions = [
|
||||
extension.Extension(instance_action.__name__.split(".")[-1],
|
||||
instance_action),
|
||||
]
|
||||
cs = fakes.FakeClient(extensions=extensions)
|
||||
|
||||
|
||||
class InstanceActionExtensionTests(utils.TestCase):
|
||||
def test_list_instance_actions(self):
|
||||
server_uuid = '1234'
|
||||
cs.instance_action.list(server_uuid)
|
||||
cs.assert_called(
|
||||
'GET', '/servers/%s/os-instance-actions' %
|
||||
server_uuid)
|
||||
|
||||
def test_get_instance_action(self):
|
||||
server_uuid = '1234'
|
||||
request_id = 'req-abcde12345'
|
||||
cs.instance_action.get(server_uuid, request_id)
|
||||
cs.assert_called(
|
||||
'GET', '/servers/%s/os-instance-actions/%s'
|
||||
% (server_uuid, request_id))
|
||||
@ -0,0 +1,33 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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.tests.unit import utils
|
||||
from novaclient.tests.unit.v2 import fakes
|
||||
from novaclient.v2.contrib import list_extensions
|
||||
|
||||
|
||||
extensions = [
|
||||
extension.Extension(list_extensions.__name__.split(".")[-1],
|
||||
list_extensions),
|
||||
]
|
||||
cs = fakes.FakeClient(extensions=extensions)
|
||||
|
||||
|
||||
class ListExtensionsTests(utils.TestCase):
|
||||
def test_list_extensions(self):
|
||||
all_exts = cs.list_extensions.show_all()
|
||||
cs.assert_called('GET', '/extensions')
|
||||
self.assertTrue(len(all_exts) > 0)
|
||||
for r in all_exts:
|
||||
self.assertTrue(len(r.summary) > 0)
|
||||
@ -0,0 +1,40 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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.tests.unit import utils
|
||||
from novaclient.tests.unit.v2 import fakes
|
||||
from novaclient.v2.contrib import migrations
|
||||
|
||||
extensions = [
|
||||
extension.Extension(migrations.__name__.split(".")[-1],
|
||||
migrations),
|
||||
]
|
||||
cs = fakes.FakeClient(extensions=extensions)
|
||||
|
||||
|
||||
class MigrationsTest(utils.TestCase):
|
||||
|
||||
def test_list_migrations(self):
|
||||
ml = cs.migrations.list()
|
||||
cs.assert_called('GET', '/os-migrations')
|
||||
for m in ml:
|
||||
self.assertIsInstance(m, migrations.Migration)
|
||||
|
||||
def test_list_migrations_with_filters(self):
|
||||
ml = cs.migrations.list('host1', 'finished', 'child1')
|
||||
|
||||
cs.assert_called('GET',
|
||||
'/os-migrations?cell_name=child1&host=host1'
|
||||
'&status=finished')
|
||||
for m in ml:
|
||||
self.assertIsInstance(m, migrations.Migration)
|
||||
@ -0,0 +1,44 @@
|
||||
# Copyright (C) 2014, Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
External event triggering for servers, not to be used by users.
|
||||
"""
|
||||
|
||||
from novaclient import extension
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.tests.unit.v2.contrib import fakes
|
||||
from novaclient.v2.contrib import server_external_events as ext_events
|
||||
|
||||
|
||||
extensions = [
|
||||
extension.Extension(ext_events.__name__.split(".")[-1],
|
||||
ext_events),
|
||||
]
|
||||
cs = fakes.FakeClient(extensions=extensions)
|
||||
|
||||
|
||||
class ServerExternalEventsTestCase(utils.TestCase):
|
||||
def test_external_event(self):
|
||||
events = [{'server_uuid': 'fake-uuid1',
|
||||
'name': 'test-event',
|
||||
'status': 'completed',
|
||||
'tag': 'tag'},
|
||||
{'server_uuid': 'fake-uuid2',
|
||||
'name': 'test-event',
|
||||
'status': 'completed',
|
||||
'tag': 'tag'}]
|
||||
result = cs.server_external_events.create(events)
|
||||
self.assertEqual(events, result)
|
||||
cs.assert_called('POST', '/os-server-external-events')
|
||||
@ -0,0 +1,46 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient import extension
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.tests.unit.v2.contrib import fakes
|
||||
from novaclient.v2.contrib import tenant_networks
|
||||
|
||||
|
||||
extensions = [
|
||||
extension.Extension(tenant_networks.__name__.split(".")[-1],
|
||||
tenant_networks),
|
||||
]
|
||||
cs = fakes.FakeClient(extensions=extensions)
|
||||
|
||||
|
||||
class TenantNetworkExtensionTests(utils.TestCase):
|
||||
def test_list_tenant_networks(self):
|
||||
nets = cs.tenant_networks.list()
|
||||
cs.assert_called('GET', '/os-tenant-networks')
|
||||
self.assertTrue(len(nets) > 0)
|
||||
|
||||
def test_get_tenant_network(self):
|
||||
cs.tenant_networks.get(1)
|
||||
cs.assert_called('GET', '/os-tenant-networks/1')
|
||||
|
||||
def test_create_tenant_networks(self):
|
||||
cs.tenant_networks.create(label="net",
|
||||
cidr="10.0.0.0/24")
|
||||
cs.assert_called('POST', '/os-tenant-networks')
|
||||
|
||||
def test_delete_tenant_networks(self):
|
||||
cs.tenant_networks.delete(1)
|
||||
cs.assert_called('DELETE', '/os-tenant-networks/1')
|
||||
2204
awx/lib/site-packages/novaclient/tests/unit/v2/fakes.py
Normal file
2204
awx/lib/site-packages/novaclient/tests/unit/v2/fakes.py
Normal file
File diff suppressed because it is too large
Load Diff
104
awx/lib/site-packages/novaclient/tests/unit/v2/test_agents.py
Normal file
104
awx/lib/site-packages/novaclient/tests/unit/v2/test_agents.py
Normal file
@ -0,0 +1,104 @@
|
||||
# Copyright 2012 IBM Corp.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import agents as data
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import agents
|
||||
|
||||
|
||||
class AgentsTest(utils.FixturedTestCase):
|
||||
|
||||
data_fixture_class = data.Fixture
|
||||
|
||||
scenarios = [('original', {'client_fixture_class': client.V1}),
|
||||
('session', {'client_fixture_class': client.SessionV1})]
|
||||
|
||||
def stub_hypervisors(self, hypervisor='kvm'):
|
||||
get_os_agents = {
|
||||
'agents': [
|
||||
{
|
||||
'hypervisor': hypervisor,
|
||||
'os': 'win',
|
||||
'architecture': 'x86',
|
||||
'version': '7.0',
|
||||
'url': 'xxx://xxxx/xxx/xxx',
|
||||
'md5hash': 'add6bb58e139be103324d04d82d8f545',
|
||||
'id': 1
|
||||
},
|
||||
{
|
||||
'hypervisor': hypervisor,
|
||||
'os': 'linux',
|
||||
'architecture': 'x86',
|
||||
'version': '16.0',
|
||||
'url': 'xxx://xxxx/xxx/xxx1',
|
||||
'md5hash': 'add6bb58e139be103324d04d82d8f546',
|
||||
'id': 2
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
headers = {'Content-Type': 'application/json'}
|
||||
self.requests.register_uri('GET', self.data_fixture.url(),
|
||||
json=get_os_agents,
|
||||
headers=headers)
|
||||
|
||||
def test_list_agents(self):
|
||||
self.stub_hypervisors()
|
||||
ags = self.cs.agents.list()
|
||||
self.assert_called('GET', '/os-agents')
|
||||
for a in ags:
|
||||
self.assertIsInstance(a, agents.Agent)
|
||||
self.assertEqual('kvm', a.hypervisor)
|
||||
|
||||
def test_list_agents_with_hypervisor(self):
|
||||
self.stub_hypervisors('xen')
|
||||
ags = self.cs.agents.list('xen')
|
||||
self.assert_called('GET', '/os-agents?hypervisor=xen')
|
||||
for a in ags:
|
||||
self.assertIsInstance(a, agents.Agent)
|
||||
self.assertEqual('xen', a.hypervisor)
|
||||
|
||||
def test_agents_create(self):
|
||||
ag = self.cs.agents.create('win', 'x86', '7.0',
|
||||
'/xxx/xxx/xxx',
|
||||
'add6bb58e139be103324d04d82d8f546',
|
||||
'xen')
|
||||
body = {'agent': {'url': '/xxx/xxx/xxx',
|
||||
'hypervisor': 'xen',
|
||||
'md5hash': 'add6bb58e139be103324d04d82d8f546',
|
||||
'version': '7.0',
|
||||
'architecture': 'x86',
|
||||
'os': 'win'}}
|
||||
self.assert_called('POST', '/os-agents', body)
|
||||
self.assertEqual(1, ag._info.copy()['id'])
|
||||
|
||||
def test_agents_delete(self):
|
||||
self.cs.agents.delete('1')
|
||||
self.assert_called('DELETE', '/os-agents/1')
|
||||
|
||||
def _build_example_update_body(self):
|
||||
return {"para": {
|
||||
"url": "/yyy/yyyy/yyyy",
|
||||
"version": "8.0",
|
||||
"md5hash": "add6bb58e139be103324d04d82d8f546"}}
|
||||
|
||||
def test_agents_modify(self):
|
||||
ag = self.cs.agents.update('1', '8.0',
|
||||
'/yyy/yyyy/yyyy',
|
||||
'add6bb58e139be103324d04d82d8f546')
|
||||
body = self._build_example_update_body()
|
||||
self.assert_called('PUT', '/os-agents/1', body)
|
||||
self.assertEqual(1, ag.id)
|
||||
@ -0,0 +1,141 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import aggregates as data
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import aggregates
|
||||
|
||||
|
||||
class AggregatesTest(utils.FixturedTestCase):
|
||||
|
||||
data_fixture_class = data.Fixture
|
||||
|
||||
scenarios = [('original', {'client_fixture_class': client.V1}),
|
||||
('session', {'client_fixture_class': client.SessionV1})]
|
||||
|
||||
def test_list_aggregates(self):
|
||||
result = self.cs.aggregates.list()
|
||||
self.assert_called('GET', '/os-aggregates')
|
||||
for aggregate in result:
|
||||
self.assertIsInstance(aggregate, aggregates.Aggregate)
|
||||
|
||||
def test_create_aggregate(self):
|
||||
body = {"aggregate": {"name": "test", "availability_zone": "nova1"}}
|
||||
aggregate = self.cs.aggregates.create("test", "nova1")
|
||||
self.assert_called('POST', '/os-aggregates', body)
|
||||
self.assertIsInstance(aggregate, aggregates.Aggregate)
|
||||
|
||||
def test_get(self):
|
||||
aggregate = self.cs.aggregates.get("1")
|
||||
self.assert_called('GET', '/os-aggregates/1')
|
||||
self.assertIsInstance(aggregate, aggregates.Aggregate)
|
||||
|
||||
aggregate2 = self.cs.aggregates.get(aggregate)
|
||||
self.assert_called('GET', '/os-aggregates/1')
|
||||
self.assertIsInstance(aggregate2, aggregates.Aggregate)
|
||||
|
||||
def test_get_details(self):
|
||||
aggregate = self.cs.aggregates.get_details("1")
|
||||
self.assert_called('GET', '/os-aggregates/1')
|
||||
self.assertIsInstance(aggregate, aggregates.Aggregate)
|
||||
|
||||
aggregate2 = self.cs.aggregates.get_details(aggregate)
|
||||
self.assert_called('GET', '/os-aggregates/1')
|
||||
self.assertIsInstance(aggregate2, aggregates.Aggregate)
|
||||
|
||||
def test_update(self):
|
||||
aggregate = self.cs.aggregates.get("1")
|
||||
values = {"name": "foo"}
|
||||
body = {"aggregate": values}
|
||||
|
||||
result1 = aggregate.update(values)
|
||||
self.assert_called('PUT', '/os-aggregates/1', body)
|
||||
self.assertIsInstance(result1, aggregates.Aggregate)
|
||||
|
||||
result2 = self.cs.aggregates.update(2, values)
|
||||
self.assert_called('PUT', '/os-aggregates/2', body)
|
||||
self.assertIsInstance(result2, aggregates.Aggregate)
|
||||
|
||||
def test_update_with_availability_zone(self):
|
||||
aggregate = self.cs.aggregates.get("1")
|
||||
values = {"name": "foo", "availability_zone": "new_zone"}
|
||||
body = {"aggregate": values}
|
||||
|
||||
result3 = self.cs.aggregates.update(aggregate, values)
|
||||
self.assert_called('PUT', '/os-aggregates/1', body)
|
||||
self.assertIsInstance(result3, aggregates.Aggregate)
|
||||
|
||||
def test_add_host(self):
|
||||
aggregate = self.cs.aggregates.get("1")
|
||||
host = "host1"
|
||||
body = {"add_host": {"host": "host1"}}
|
||||
|
||||
result1 = aggregate.add_host(host)
|
||||
self.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assertIsInstance(result1, aggregates.Aggregate)
|
||||
|
||||
result2 = self.cs.aggregates.add_host("2", host)
|
||||
self.assert_called('POST', '/os-aggregates/2/action', body)
|
||||
self.assertIsInstance(result2, aggregates.Aggregate)
|
||||
|
||||
result3 = self.cs.aggregates.add_host(aggregate, host)
|
||||
self.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assertIsInstance(result3, aggregates.Aggregate)
|
||||
|
||||
def test_remove_host(self):
|
||||
aggregate = self.cs.aggregates.get("1")
|
||||
host = "host1"
|
||||
body = {"remove_host": {"host": "host1"}}
|
||||
|
||||
result1 = aggregate.remove_host(host)
|
||||
self.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assertIsInstance(result1, aggregates.Aggregate)
|
||||
|
||||
result2 = self.cs.aggregates.remove_host("2", host)
|
||||
self.assert_called('POST', '/os-aggregates/2/action', body)
|
||||
self.assertIsInstance(result2, aggregates.Aggregate)
|
||||
|
||||
result3 = self.cs.aggregates.remove_host(aggregate, host)
|
||||
self.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assertIsInstance(result3, aggregates.Aggregate)
|
||||
|
||||
def test_set_metadata(self):
|
||||
aggregate = self.cs.aggregates.get("1")
|
||||
metadata = {"foo": "bar"}
|
||||
body = {"set_metadata": {"metadata": metadata}}
|
||||
|
||||
result1 = aggregate.set_metadata(metadata)
|
||||
self.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assertIsInstance(result1, aggregates.Aggregate)
|
||||
|
||||
result2 = self.cs.aggregates.set_metadata(2, metadata)
|
||||
self.assert_called('POST', '/os-aggregates/2/action', body)
|
||||
self.assertIsInstance(result2, aggregates.Aggregate)
|
||||
|
||||
result3 = self.cs.aggregates.set_metadata(aggregate, metadata)
|
||||
self.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assertIsInstance(result3, aggregates.Aggregate)
|
||||
|
||||
def test_delete_aggregate(self):
|
||||
aggregate = self.cs.aggregates.list()[0]
|
||||
aggregate.delete()
|
||||
self.assert_called('DELETE', '/os-aggregates/1')
|
||||
|
||||
self.cs.aggregates.delete('1')
|
||||
self.assert_called('DELETE', '/os-aggregates/1')
|
||||
|
||||
self.cs.aggregates.delete(aggregate)
|
||||
self.assert_called('DELETE', '/os-aggregates/1')
|
||||
387
awx/lib/site-packages/novaclient/tests/unit/v2/test_auth.py
Normal file
387
awx/lib/site-packages/novaclient/tests/unit/v2/test_auth.py
Normal file
@ -0,0 +1,387 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import json
|
||||
|
||||
from keystoneclient import fixture
|
||||
import mock
|
||||
import requests
|
||||
|
||||
from novaclient import exceptions
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 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 = self.get_token()
|
||||
|
||||
auth_response = utils.TestResponse({
|
||||
"status_code": 200,
|
||||
"text": json.dumps(resp),
|
||||
})
|
||||
|
||||
mock_request = mock.Mock(return_value=(auth_response))
|
||||
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_auth_call():
|
||||
cs.client.authenticate()
|
||||
headers = {
|
||||
'User-Agent': cs.client.USER_AGENT,
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
body = {
|
||||
'auth': {
|
||||
'passwordCredentials': {
|
||||
'username': cs.client.user,
|
||||
'password': cs.client.password,
|
||||
},
|
||||
'tenantName': cs.client.projectid,
|
||||
},
|
||||
}
|
||||
|
||||
token_url = cs.client.auth_url + "/tokens"
|
||||
mock_request.assert_called_with(
|
||||
"POST",
|
||||
token_url,
|
||||
headers=headers,
|
||||
data=json.dumps(body),
|
||||
allow_redirects=True,
|
||||
**self.TEST_REQUEST_BASE)
|
||||
|
||||
endpoints = resp["access"]["serviceCatalog"][0]['endpoints']
|
||||
public_url = endpoints[0]["publicURL"].rstrip('/')
|
||||
self.assertEqual(cs.client.management_url, public_url)
|
||||
token_id = resp["access"]["token"]["id"]
|
||||
self.assertEqual(cs.client.auth_token, token_id)
|
||||
|
||||
test_auth_call()
|
||||
|
||||
def test_authenticate_failure(self):
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
utils.AUTH_URL_V2)
|
||||
resp = {"unauthorized": {"message": "Unauthorized", "code": "401"}}
|
||||
auth_response = utils.TestResponse({
|
||||
"status_code": 401,
|
||||
"text": json.dumps(resp),
|
||||
})
|
||||
|
||||
mock_request = mock.Mock(return_value=(auth_response))
|
||||
|
||||
@mock.patch.object(requests.Session, "request", mock_request)
|
||||
def test_auth_call():
|
||||
self.assertRaises(exceptions.Unauthorized, cs.client.authenticate)
|
||||
|
||||
test_auth_call()
|
||||
|
||||
def test_v1_auth_redirect(self):
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
utils.AUTH_URL_V1, service_type='compute')
|
||||
dict_correct_response = self.get_token()
|
||||
correct_response = json.dumps(dict_correct_response)
|
||||
dict_responses = [
|
||||
{"headers": {'location': 'http://127.0.0.1:5001'},
|
||||
"status_code": 305,
|
||||
"text": "Use proxy"},
|
||||
# Configured on admin port, nova redirects to v2.0 port.
|
||||
# When trying to connect on it, keystone auth succeed by v1.0
|
||||
# protocol (through headers) but tokens are being returned in
|
||||
# body (looks like keystone bug). Leaved for compatibility.
|
||||
{"headers": {},
|
||||
"status_code": 200,
|
||||
"text": correct_response},
|
||||
{"headers": {},
|
||||
"status_code": 200,
|
||||
"text": correct_response}
|
||||
]
|
||||
|
||||
responses = [(utils.TestResponse(resp)) for resp in dict_responses]
|
||||
|
||||
def side_effect(*args, **kwargs):
|
||||
return responses.pop(0)
|
||||
|
||||
mock_request = mock.Mock(side_effect=side_effect)
|
||||
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_auth_call():
|
||||
cs.client.authenticate()
|
||||
headers = {
|
||||
'User-Agent': cs.client.USER_AGENT,
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
body = {
|
||||
'auth': {
|
||||
'passwordCredentials': {
|
||||
'username': cs.client.user,
|
||||
'password': cs.client.password,
|
||||
},
|
||||
'tenantName': cs.client.projectid,
|
||||
},
|
||||
}
|
||||
|
||||
token_url = cs.client.auth_url + "/tokens"
|
||||
kwargs = copy.copy(self.TEST_REQUEST_BASE)
|
||||
kwargs['headers'] = headers
|
||||
kwargs['data'] = json.dumps(body)
|
||||
mock_request.assert_called_with(
|
||||
"POST",
|
||||
token_url,
|
||||
allow_redirects=True,
|
||||
**kwargs)
|
||||
|
||||
resp = dict_correct_response
|
||||
endpoints = resp["access"]["serviceCatalog"][0]['endpoints']
|
||||
public_url = endpoints[0]["publicURL"].rstrip('/')
|
||||
self.assertEqual(cs.client.management_url, public_url)
|
||||
token_id = resp["access"]["token"]["id"]
|
||||
self.assertEqual(cs.client.auth_token, token_id)
|
||||
|
||||
test_auth_call()
|
||||
|
||||
def test_v2_auth_redirect(self):
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
utils.AUTH_URL_V2, service_type='compute')
|
||||
dict_correct_response = self.get_token()
|
||||
correct_response = json.dumps(dict_correct_response)
|
||||
dict_responses = [
|
||||
{"headers": {'location': 'http://127.0.0.1:5001'},
|
||||
"status_code": 305,
|
||||
"text": "Use proxy"},
|
||||
# Configured on admin port, nova redirects to v2.0 port.
|
||||
# When trying to connect on it, keystone auth succeed by v1.0
|
||||
# protocol (through headers) but tokens are being returned in
|
||||
# body (looks like keystone bug). Leaved for compatibility.
|
||||
{"headers": {},
|
||||
"status_code": 200,
|
||||
"text": correct_response},
|
||||
{"headers": {},
|
||||
"status_code": 200,
|
||||
"text": correct_response}
|
||||
]
|
||||
|
||||
responses = [(utils.TestResponse(resp)) for resp in dict_responses]
|
||||
|
||||
def side_effect(*args, **kwargs):
|
||||
return responses.pop(0)
|
||||
|
||||
mock_request = mock.Mock(side_effect=side_effect)
|
||||
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_auth_call():
|
||||
cs.client.authenticate()
|
||||
headers = {
|
||||
'User-Agent': cs.client.USER_AGENT,
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
body = {
|
||||
'auth': {
|
||||
'passwordCredentials': {
|
||||
'username': cs.client.user,
|
||||
'password': cs.client.password,
|
||||
},
|
||||
'tenantName': cs.client.projectid,
|
||||
},
|
||||
}
|
||||
|
||||
token_url = cs.client.auth_url + "/tokens"
|
||||
kwargs = copy.copy(self.TEST_REQUEST_BASE)
|
||||
kwargs['headers'] = headers
|
||||
kwargs['data'] = json.dumps(body)
|
||||
mock_request.assert_called_with(
|
||||
"POST",
|
||||
token_url,
|
||||
allow_redirects=True,
|
||||
**kwargs)
|
||||
|
||||
resp = dict_correct_response
|
||||
endpoints = resp["access"]["serviceCatalog"][0]['endpoints']
|
||||
public_url = endpoints[0]["publicURL"].rstrip('/')
|
||||
self.assertEqual(cs.client.management_url, public_url)
|
||||
token_id = resp["access"]["token"]["id"]
|
||||
self.assertEqual(cs.client.auth_token, token_id)
|
||||
|
||||
test_auth_call()
|
||||
|
||||
def test_ambiguous_endpoints(self):
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
utils.AUTH_URL_V2, service_type='compute')
|
||||
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),
|
||||
})
|
||||
|
||||
mock_request = mock.Mock(return_value=(auth_response))
|
||||
|
||||
@mock.patch.object(requests.Session, "request", mock_request)
|
||||
def test_auth_call():
|
||||
self.assertRaises(exceptions.AmbiguousEndpoints,
|
||||
cs.client.authenticate)
|
||||
|
||||
test_auth_call()
|
||||
|
||||
def test_authenticate_with_token_success(self):
|
||||
cs = client.Client("username", None, "project_id",
|
||||
utils.AUTH_URL_V2, service_type='compute')
|
||||
cs.client.auth_token = "FAKE_ID"
|
||||
resp = self.get_token(token_id="FAKE_ID")
|
||||
auth_response = utils.TestResponse({
|
||||
"status_code": 200,
|
||||
"text": json.dumps(resp),
|
||||
})
|
||||
|
||||
mock_request = mock.Mock(return_value=(auth_response))
|
||||
|
||||
with mock.patch.object(requests, "request", mock_request):
|
||||
cs.client.authenticate()
|
||||
headers = {
|
||||
'User-Agent': cs.client.USER_AGENT,
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
body = {
|
||||
'auth': {
|
||||
'token': {
|
||||
'id': cs.client.auth_token,
|
||||
},
|
||||
'tenantName': cs.client.projectid,
|
||||
},
|
||||
}
|
||||
|
||||
token_url = cs.client.auth_url + "/tokens"
|
||||
mock_request.assert_called_with(
|
||||
"POST",
|
||||
token_url,
|
||||
headers=headers,
|
||||
data=json.dumps(body),
|
||||
allow_redirects=True,
|
||||
**self.TEST_REQUEST_BASE)
|
||||
|
||||
endpoints = resp["access"]["serviceCatalog"][0]['endpoints']
|
||||
public_url = endpoints[0]["publicURL"].rstrip('/')
|
||||
self.assertEqual(cs.client.management_url, public_url)
|
||||
token_id = resp["access"]["token"]["id"]
|
||||
self.assertEqual(cs.client.auth_token, token_id)
|
||||
|
||||
def test_authenticate_with_token_failure(self):
|
||||
cs = client.Client("username", None, "project_id", utils.AUTH_URL_V2)
|
||||
cs.client.auth_token = "FAKE_ID"
|
||||
resp = {"unauthorized": {"message": "Unauthorized", "code": "401"}}
|
||||
auth_response = utils.TestResponse({
|
||||
"status_code": 401,
|
||||
"text": json.dumps(resp),
|
||||
})
|
||||
|
||||
mock_request = mock.Mock(return_value=(auth_response))
|
||||
|
||||
with mock.patch.object(requests.Session, "request", mock_request):
|
||||
self.assertRaises(exceptions.Unauthorized, cs.client.authenticate)
|
||||
|
||||
|
||||
class AuthenticationTests(utils.TestCase):
|
||||
def test_authenticate_success(self):
|
||||
cs = client.Client("username", "password",
|
||||
"project_id", utils.AUTH_URL)
|
||||
management_url = 'https://localhost/v1.1/443470'
|
||||
auth_response = utils.TestResponse({
|
||||
'status_code': 204,
|
||||
'headers': {
|
||||
'x-server-management-url': management_url,
|
||||
'x-auth-token': '1b751d74-de0c-46ae-84f0-915744b582d1',
|
||||
},
|
||||
})
|
||||
mock_request = mock.Mock(return_value=(auth_response))
|
||||
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_auth_call():
|
||||
cs.client.authenticate()
|
||||
headers = {
|
||||
'Accept': 'application/json',
|
||||
'X-Auth-User': 'username',
|
||||
'X-Auth-Key': 'password',
|
||||
'X-Auth-Project-Id': 'project_id',
|
||||
'User-Agent': cs.client.USER_AGENT
|
||||
}
|
||||
mock_request.assert_called_with(
|
||||
"GET",
|
||||
cs.client.auth_url,
|
||||
headers=headers,
|
||||
**self.TEST_REQUEST_BASE)
|
||||
|
||||
self.assertEqual(cs.client.management_url,
|
||||
auth_response.headers['x-server-management-url'])
|
||||
self.assertEqual(cs.client.auth_token,
|
||||
auth_response.headers['x-auth-token'])
|
||||
|
||||
test_auth_call()
|
||||
|
||||
def test_authenticate_failure(self):
|
||||
cs = client.Client("username", "password",
|
||||
"project_id", utils.AUTH_URL)
|
||||
auth_response = utils.TestResponse({'status_code': 401})
|
||||
mock_request = mock.Mock(return_value=(auth_response))
|
||||
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_auth_call():
|
||||
self.assertRaises(exceptions.Unauthorized, cs.client.authenticate)
|
||||
|
||||
test_auth_call()
|
||||
|
||||
def test_auth_automatic(self):
|
||||
cs = client.Client("username", "password",
|
||||
"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)
|
||||
@mock.patch.object(http_client, 'authenticate')
|
||||
def test_auth_call(m):
|
||||
http_client.get('/')
|
||||
self.assertTrue(m.called)
|
||||
self.assertTrue(mock_request.called)
|
||||
|
||||
test_auth_call()
|
||||
|
||||
def test_auth_manual(self):
|
||||
cs = client.Client("username", "password",
|
||||
"project_id", utils.AUTH_URL)
|
||||
|
||||
@mock.patch.object(cs.client, 'authenticate')
|
||||
def test_auth_call(m):
|
||||
cs.authenticate()
|
||||
self.assertTrue(m.called)
|
||||
|
||||
test_auth_call()
|
||||
@ -0,0 +1,102 @@
|
||||
# Copyright 2011 OpenStack Foundation
|
||||
# Copyright 2013 IBM Corp.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import six
|
||||
|
||||
from novaclient.tests.unit.fixture_data import availability_zones as data
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import availability_zones
|
||||
|
||||
|
||||
class AvailabilityZoneTest(utils.FixturedTestCase):
|
||||
# NOTE(cyeoh): import shell here so the V3 version of
|
||||
# this class can inherit off the v3 version of shell
|
||||
from novaclient.v2 import shell # noqa
|
||||
|
||||
data_fixture_class = data.V1
|
||||
|
||||
scenarios = [('original', {'client_fixture_class': client.V1}),
|
||||
('session', {'client_fixture_class': client.SessionV1})]
|
||||
|
||||
def setUp(self):
|
||||
super(AvailabilityZoneTest, self).setUp()
|
||||
self.availability_zone_type = self._get_availability_zone_type()
|
||||
|
||||
def _get_availability_zone_type(self):
|
||||
return availability_zones.AvailabilityZone
|
||||
|
||||
def _assertZone(self, zone, name, status):
|
||||
self.assertEqual(zone.zoneName, name)
|
||||
self.assertEqual(zone.zoneState, status)
|
||||
|
||||
def test_list_availability_zone(self):
|
||||
zones = self.cs.availability_zones.list(detailed=False)
|
||||
self.assert_called('GET', '/os-availability-zone')
|
||||
|
||||
for zone in zones:
|
||||
self.assertIsInstance(zone, self.availability_zone_type)
|
||||
|
||||
self.assertEqual(2, len(zones))
|
||||
|
||||
l0 = [six.u('zone-1'), six.u('available')]
|
||||
l1 = [six.u('zone-2'), six.u('not available')]
|
||||
|
||||
z0 = self.shell._treeizeAvailabilityZone(zones[0])
|
||||
z1 = self.shell._treeizeAvailabilityZone(zones[1])
|
||||
|
||||
self.assertEqual((1, 1), (len(z0), len(z1)))
|
||||
|
||||
self._assertZone(z0[0], l0[0], l0[1])
|
||||
self._assertZone(z1[0], l1[0], l1[1])
|
||||
|
||||
def test_detail_availability_zone(self):
|
||||
zones = self.cs.availability_zones.list(detailed=True)
|
||||
self.assert_called('GET', '/os-availability-zone/detail')
|
||||
|
||||
for zone in zones:
|
||||
self.assertIsInstance(zone, self.availability_zone_type)
|
||||
|
||||
self.assertEqual(3, len(zones))
|
||||
|
||||
l0 = [six.u('zone-1'), six.u('available')]
|
||||
l1 = [six.u('|- fake_host-1'), six.u('')]
|
||||
l2 = [six.u('| |- nova-compute'),
|
||||
six.u('enabled :-) 2012-12-26 14:45:25')]
|
||||
l3 = [six.u('internal'), six.u('available')]
|
||||
l4 = [six.u('|- fake_host-1'), six.u('')]
|
||||
l5 = [six.u('| |- nova-sched'),
|
||||
six.u('enabled :-) 2012-12-26 14:45:25')]
|
||||
l6 = [six.u('|- fake_host-2'), six.u('')]
|
||||
l7 = [six.u('| |- nova-network'),
|
||||
six.u('enabled XXX 2012-12-26 14:45:24')]
|
||||
l8 = [six.u('zone-2'), six.u('not available')]
|
||||
|
||||
z0 = self.shell._treeizeAvailabilityZone(zones[0])
|
||||
z1 = self.shell._treeizeAvailabilityZone(zones[1])
|
||||
z2 = self.shell._treeizeAvailabilityZone(zones[2])
|
||||
|
||||
self.assertEqual((3, 5, 1), (len(z0), len(z1), len(z2)))
|
||||
|
||||
self._assertZone(z0[0], l0[0], l0[1])
|
||||
self._assertZone(z0[1], l1[0], l1[1])
|
||||
self._assertZone(z0[2], l2[0], l2[1])
|
||||
self._assertZone(z1[0], l3[0], l3[1])
|
||||
self._assertZone(z1[1], l4[0], l4[1])
|
||||
self._assertZone(z1[2], l5[0], l5[1])
|
||||
self._assertZone(z1[3], l6[0], l6[1])
|
||||
self._assertZone(z1[4], l7[0], l7[1])
|
||||
self._assertZone(z2[0], l8[0], l8[1])
|
||||
36
awx/lib/site-packages/novaclient/tests/unit/v2/test_certs.py
Normal file
36
awx/lib/site-packages/novaclient/tests/unit/v2/test_certs.py
Normal file
@ -0,0 +1,36 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import certs as data
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import certs
|
||||
|
||||
|
||||
class CertsTest(utils.FixturedTestCase):
|
||||
|
||||
data_fixture_class = data.Fixture
|
||||
cert_type = certs.Certificate
|
||||
|
||||
scenarios = [('original', {'client_fixture_class': client.V1}),
|
||||
('session', {'client_fixture_class': client.SessionV1})]
|
||||
|
||||
def test_create_cert(self):
|
||||
cert = self.cs.certs.create()
|
||||
self.assert_called('POST', '/os-certificates')
|
||||
self.assertIsInstance(cert, self.cert_type)
|
||||
|
||||
def test_get_root_cert(self):
|
||||
cert = self.cs.certs.get()
|
||||
self.assert_called('GET', '/os-certificates/root')
|
||||
self.assertIsInstance(cert, self.cert_type)
|
||||
@ -0,0 +1,45 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import uuid
|
||||
|
||||
from keystoneclient import session
|
||||
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import client
|
||||
|
||||
|
||||
class ClientTest(utils.TestCase):
|
||||
|
||||
def test_adapter_properties(self):
|
||||
# sample of properties, there are many more
|
||||
user_agent = uuid.uuid4().hex
|
||||
endpoint_override = uuid.uuid4().hex
|
||||
|
||||
s = session.Session()
|
||||
c = client.Client(session=s,
|
||||
user_agent=user_agent,
|
||||
endpoint_override=endpoint_override)
|
||||
|
||||
self.assertEqual(user_agent, c.client.user_agent)
|
||||
self.assertEqual(endpoint_override, c.client.endpoint_override)
|
||||
|
||||
def test_passing_interface(self):
|
||||
endpoint_type = uuid.uuid4().hex
|
||||
interface = uuid.uuid4().hex
|
||||
|
||||
s = session.Session()
|
||||
c = client.Client(session=s,
|
||||
interface=interface,
|
||||
endpoint_type=endpoint_type)
|
||||
|
||||
self.assertEqual(interface, c.client.interface)
|
||||
@ -0,0 +1,45 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import six
|
||||
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import cloudpipe as data
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import cloudpipe
|
||||
|
||||
|
||||
class CloudpipeTest(utils.FixturedTestCase):
|
||||
|
||||
data_fixture_class = data.Fixture
|
||||
|
||||
scenarios = [('original', {'client_fixture_class': client.V1}),
|
||||
('session', {'client_fixture_class': client.SessionV1})]
|
||||
|
||||
def test_list_cloudpipes(self):
|
||||
cp = self.cs.cloudpipe.list()
|
||||
self.assert_called('GET', '/os-cloudpipe')
|
||||
[self.assertIsInstance(c, cloudpipe.Cloudpipe) for c in cp]
|
||||
|
||||
def test_create(self):
|
||||
project = "test"
|
||||
cp = self.cs.cloudpipe.create(project)
|
||||
body = {'cloudpipe': {'project_id': project}}
|
||||
self.assert_called('POST', '/os-cloudpipe', body)
|
||||
self.assertIsInstance(cp, six.string_types)
|
||||
|
||||
def test_update(self):
|
||||
self.cs.cloudpipe.update("192.168.1.1", 2345)
|
||||
body = {'configure_project': {'vpn_ip': "192.168.1.1",
|
||||
'vpn_port': 2345}}
|
||||
self.assert_called('PUT', '/os-cloudpipe/configure-project', body)
|
||||
@ -0,0 +1,44 @@
|
||||
# Copyright 2012 IBM Corp.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import fixedips as data
|
||||
from novaclient.tests.unit import utils
|
||||
|
||||
|
||||
class FixedIpsTest(utils.FixturedTestCase):
|
||||
|
||||
data_fixture_class = data.Fixture
|
||||
|
||||
scenarios = [('original', {'client_fixture_class': client.V1}),
|
||||
('session', {'client_fixture_class': client.SessionV1})]
|
||||
|
||||
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('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}
|
||||
self.cs.fixed_ips.reserve(fixed_ip='192.168.1.1')
|
||||
self.assert_called('POST', '/os-fixed-ips/192.168.1.1/action', body)
|
||||
|
||||
def test_unreserve_fixed_ip(self):
|
||||
body = {"unreserve": None}
|
||||
self.cs.fixed_ips.unreserve(fixed_ip='192.168.1.1')
|
||||
self.assert_called('POST', '/os-fixed-ips/192.168.1.1/action', body)
|
||||
@ -0,0 +1,70 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.tests.unit.v2 import fakes
|
||||
from novaclient.v2 import flavor_access
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
|
||||
|
||||
class FlavorAccessTest(utils.TestCase):
|
||||
|
||||
def test_list_access_by_flavor_private(self):
|
||||
kwargs = {'flavor': cs.flavors.get(2)}
|
||||
r = cs.flavor_access.list(**kwargs)
|
||||
cs.assert_called('GET', '/flavors/2/os-flavor-access')
|
||||
[self.assertIsInstance(a, flavor_access.FlavorAccess) for a in r]
|
||||
|
||||
def test_add_tenant_access(self):
|
||||
flavor = cs.flavors.get(2)
|
||||
tenant = 'proj2'
|
||||
r = cs.flavor_access.add_tenant_access(flavor, tenant)
|
||||
|
||||
body = {
|
||||
"addTenantAccess": {
|
||||
"tenant": "proj2"
|
||||
}
|
||||
}
|
||||
|
||||
cs.assert_called('POST', '/flavors/2/action', body)
|
||||
[self.assertIsInstance(a, flavor_access.FlavorAccess) for a in r]
|
||||
|
||||
def test_remove_tenant_access(self):
|
||||
flavor = cs.flavors.get(2)
|
||||
tenant = 'proj2'
|
||||
r = cs.flavor_access.remove_tenant_access(flavor, tenant)
|
||||
|
||||
body = {
|
||||
"removeTenantAccess": {
|
||||
"tenant": "proj2"
|
||||
}
|
||||
}
|
||||
|
||||
cs.assert_called('POST', '/flavors/2/action', body)
|
||||
[self.assertIsInstance(a, flavor_access.FlavorAccess) for a in r]
|
||||
|
||||
def test_repr_flavor_access(self):
|
||||
flavor = cs.flavors.get(2)
|
||||
tenant = 'proj3'
|
||||
r = cs.flavor_access.add_tenant_access(flavor, tenant)
|
||||
|
||||
def get_expected(flavor_access):
|
||||
return ("<FlavorAccess flavor id: %s, tenant id: %s>" %
|
||||
(flavor_access.flavor_id, flavor_access.tenant_id))
|
||||
|
||||
for a in r:
|
||||
self.assertEqual(get_expected(a), repr(a))
|
||||
219
awx/lib/site-packages/novaclient/tests/unit/v2/test_flavors.py
Normal file
219
awx/lib/site-packages/novaclient/tests/unit/v2/test_flavors.py
Normal file
@ -0,0 +1,219 @@
|
||||
# Copyright (c) 2013, OpenStack
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
|
||||
from novaclient import exceptions
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.tests.unit.v2 import fakes
|
||||
from novaclient.v2 import flavors
|
||||
|
||||
|
||||
class FlavorsTest(utils.TestCase):
|
||||
def setUp(self):
|
||||
super(FlavorsTest, self).setUp()
|
||||
self.cs = self._get_fake_client()
|
||||
self.flavor_type = self._get_flavor_type()
|
||||
|
||||
def _get_fake_client(self):
|
||||
return fakes.FakeClient()
|
||||
|
||||
def _get_flavor_type(self):
|
||||
return flavors.Flavor
|
||||
|
||||
def test_list_flavors(self):
|
||||
fl = self.cs.flavors.list()
|
||||
self.cs.assert_called('GET', '/flavors/detail')
|
||||
for flavor in fl:
|
||||
self.assertIsInstance(flavor, self.flavor_type)
|
||||
|
||||
def test_list_flavors_undetailed(self):
|
||||
fl = self.cs.flavors.list(detailed=False)
|
||||
self.cs.assert_called('GET', '/flavors')
|
||||
for flavor in fl:
|
||||
self.assertIsInstance(flavor, self.flavor_type)
|
||||
|
||||
def test_list_flavors_is_public_none(self):
|
||||
fl = self.cs.flavors.list(is_public=None)
|
||||
self.cs.assert_called('GET', '/flavors/detail?is_public=None')
|
||||
for flavor in fl:
|
||||
self.assertIsInstance(flavor, self.flavor_type)
|
||||
|
||||
def test_list_flavors_is_public_false(self):
|
||||
fl = self.cs.flavors.list(is_public=False)
|
||||
self.cs.assert_called('GET', '/flavors/detail?is_public=False')
|
||||
for flavor in fl:
|
||||
self.assertIsInstance(flavor, self.flavor_type)
|
||||
|
||||
def test_list_flavors_is_public_true(self):
|
||||
fl = self.cs.flavors.list(is_public=True)
|
||||
self.cs.assert_called('GET', '/flavors/detail')
|
||||
for flavor in fl:
|
||||
self.assertIsInstance(flavor, self.flavor_type)
|
||||
|
||||
def test_get_flavor_details(self):
|
||||
f = self.cs.flavors.get(1)
|
||||
self.cs.assert_called('GET', '/flavors/1')
|
||||
self.assertIsInstance(f, self.flavor_type)
|
||||
self.assertEqual(256, f.ram)
|
||||
self.assertEqual(10, f.disk)
|
||||
self.assertEqual(10, f.ephemeral)
|
||||
self.assertTrue(f.is_public)
|
||||
|
||||
def test_get_flavor_details_alphanum_id(self):
|
||||
f = self.cs.flavors.get('aa1')
|
||||
self.cs.assert_called('GET', '/flavors/aa1')
|
||||
self.assertIsInstance(f, self.flavor_type)
|
||||
self.assertEqual(128, f.ram)
|
||||
self.assertEqual(0, f.disk)
|
||||
self.assertEqual(0, f.ephemeral)
|
||||
self.assertTrue(f.is_public)
|
||||
|
||||
def test_get_flavor_details_diablo(self):
|
||||
f = self.cs.flavors.get(3)
|
||||
self.cs.assert_called('GET', '/flavors/3')
|
||||
self.assertIsInstance(f, self.flavor_type)
|
||||
self.assertEqual(256, f.ram)
|
||||
self.assertEqual(10, f.disk)
|
||||
self.assertEqual('N/A', f.ephemeral)
|
||||
self.assertEqual('N/A', f.is_public)
|
||||
|
||||
def test_find(self):
|
||||
f = self.cs.flavors.find(ram=256)
|
||||
self.cs.assert_called('GET', '/flavors/detail')
|
||||
self.assertEqual('256 mb server', f.name)
|
||||
|
||||
f = self.cs.flavors.find(disk=0)
|
||||
self.assertEqual('128 mb server', f.name)
|
||||
|
||||
self.assertRaises(exceptions.NotFound, self.cs.flavors.find,
|
||||
disk=12345)
|
||||
|
||||
def _create_body(self, name, ram, vcpus, disk, ephemeral, id, swap,
|
||||
rxtx_factor, is_public):
|
||||
return {
|
||||
"flavor": {
|
||||
"name": name,
|
||||
"ram": ram,
|
||||
"vcpus": vcpus,
|
||||
"disk": disk,
|
||||
"OS-FLV-EXT-DATA:ephemeral": ephemeral,
|
||||
"id": id,
|
||||
"swap": swap,
|
||||
"rxtx_factor": rxtx_factor,
|
||||
"os-flavor-access:is_public": is_public,
|
||||
}
|
||||
}
|
||||
|
||||
def test_create(self):
|
||||
f = self.cs.flavors.create("flavorcreate", 512, 1, 10, 1234,
|
||||
ephemeral=10, is_public=False)
|
||||
|
||||
body = self._create_body("flavorcreate", 512, 1, 10, 10, 1234, 0, 1.0,
|
||||
False)
|
||||
|
||||
self.cs.assert_called('POST', '/flavors', body)
|
||||
self.assertIsInstance(f, self.flavor_type)
|
||||
|
||||
def test_create_with_id_as_string(self):
|
||||
flavor_id = 'foobar'
|
||||
f = self.cs.flavors.create("flavorcreate", 512,
|
||||
1, 10, flavor_id, ephemeral=10,
|
||||
is_public=False)
|
||||
|
||||
body = self._create_body("flavorcreate", 512, 1, 10, 10, flavor_id, 0,
|
||||
1.0, False)
|
||||
|
||||
self.cs.assert_called('POST', '/flavors', body)
|
||||
self.assertIsInstance(f, self.flavor_type)
|
||||
|
||||
def test_create_ephemeral_ispublic_defaults(self):
|
||||
f = self.cs.flavors.create("flavorcreate", 512, 1, 10, 1234)
|
||||
|
||||
body = self._create_body("flavorcreate", 512, 1, 10, 0, 1234, 0,
|
||||
1.0, True)
|
||||
|
||||
self.cs.assert_called('POST', '/flavors', body)
|
||||
self.assertIsInstance(f, self.flavor_type)
|
||||
|
||||
def test_invalid_parameters_create(self):
|
||||
self.assertRaises(exceptions.CommandError, self.cs.flavors.create,
|
||||
"flavorcreate", "invalid", 1, 10, 1234, swap=0,
|
||||
ephemeral=0, rxtx_factor=1.0, is_public=True)
|
||||
self.assertRaises(exceptions.CommandError, self.cs.flavors.create,
|
||||
"flavorcreate", 512, "invalid", 10, 1234, swap=0,
|
||||
ephemeral=0, rxtx_factor=1.0, is_public=True)
|
||||
self.assertRaises(exceptions.CommandError, self.cs.flavors.create,
|
||||
"flavorcreate", 512, 1, "invalid", 1234, swap=0,
|
||||
ephemeral=0, rxtx_factor=1.0, is_public=True)
|
||||
self.assertRaises(exceptions.CommandError, self.cs.flavors.create,
|
||||
"flavorcreate", 512, 1, 10, 1234, swap="invalid",
|
||||
ephemeral=0, rxtx_factor=1.0, is_public=True)
|
||||
self.assertRaises(exceptions.CommandError, self.cs.flavors.create,
|
||||
"flavorcreate", 512, 1, 10, 1234, swap=0,
|
||||
ephemeral="invalid", rxtx_factor=1.0, is_public=True)
|
||||
self.assertRaises(exceptions.CommandError, self.cs.flavors.create,
|
||||
"flavorcreate", 512, 1, 10, 1234, swap=0,
|
||||
ephemeral=0, rxtx_factor="invalid", is_public=True)
|
||||
self.assertRaises(exceptions.CommandError, self.cs.flavors.create,
|
||||
"flavorcreate", 512, 1, 10, 1234, swap=0,
|
||||
ephemeral=0, rxtx_factor=1.0, is_public='invalid')
|
||||
|
||||
def test_delete(self):
|
||||
self.cs.flavors.delete("flavordelete")
|
||||
self.cs.assert_called('DELETE', '/flavors/flavordelete')
|
||||
|
||||
def test_delete_with_flavor_instance(self):
|
||||
f = self.cs.flavors.get(2)
|
||||
self.cs.flavors.delete(f)
|
||||
self.cs.assert_called('DELETE', '/flavors/2')
|
||||
|
||||
def test_delete_with_flavor_instance_method(self):
|
||||
f = self.cs.flavors.get(2)
|
||||
f.delete()
|
||||
self.cs.assert_called('DELETE', '/flavors/2')
|
||||
|
||||
def test_set_keys(self):
|
||||
f = self.cs.flavors.get(1)
|
||||
f.set_keys({'k1': 'v1'})
|
||||
self.cs.assert_called('POST', '/flavors/1/os-extra_specs',
|
||||
{"extra_specs": {'k1': 'v1'}})
|
||||
|
||||
def test_set_with_valid_keys(self):
|
||||
valid_keys = ['key4', 'month.price', 'I-Am:AK-ey.44-',
|
||||
'key with spaces and _']
|
||||
|
||||
f = self.cs.flavors.get(4)
|
||||
for key in valid_keys:
|
||||
f.set_keys({key: 'v4'})
|
||||
self.cs.assert_called('POST', '/flavors/4/os-extra_specs',
|
||||
{"extra_specs": {key: 'v4'}})
|
||||
|
||||
def test_set_with_invalid_keys(self):
|
||||
invalid_keys = ['/1', '?1', '%1', '<', '>']
|
||||
|
||||
f = self.cs.flavors.get(1)
|
||||
for key in invalid_keys:
|
||||
self.assertRaises(exceptions.CommandError, f.set_keys, {key: 'v1'})
|
||||
|
||||
@mock.patch.object(flavors.FlavorManager, '_delete')
|
||||
def test_unset_keys(self, mock_delete):
|
||||
f = self.cs.flavors.get(1)
|
||||
keys = ['k1', 'k2']
|
||||
f.unset_keys(keys)
|
||||
mock_delete.assert_has_calls([
|
||||
mock.call("/flavors/1/os-extra_specs/k1"),
|
||||
mock.call("/flavors/1/os-extra_specs/k2")
|
||||
])
|
||||
@ -0,0 +1,91 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import floatingips as data
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import floating_ip_dns
|
||||
|
||||
|
||||
class FloatingIPDNSDomainTest(utils.FixturedTestCase):
|
||||
|
||||
testdomain = "testdomain"
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.DNSFixture
|
||||
|
||||
def test_dns_domains(self):
|
||||
domainlist = self.cs.dns_domains.domains()
|
||||
self.assertEqual(2, len(domainlist))
|
||||
|
||||
for entry in domainlist:
|
||||
self.assertIsInstance(entry,
|
||||
floating_ip_dns.FloatingIPDNSDomain)
|
||||
|
||||
self.assertEqual('example.com', domainlist[1].domain)
|
||||
|
||||
def test_create_private_domain(self):
|
||||
self.cs.dns_domains.create_private(self.testdomain, 'test_avzone')
|
||||
self.assert_called('PUT', '/os-floating-ip-dns/%s' %
|
||||
self.testdomain)
|
||||
|
||||
def test_create_public_domain(self):
|
||||
self.cs.dns_domains.create_public(self.testdomain, 'test_project')
|
||||
self.assert_called('PUT', '/os-floating-ip-dns/%s' %
|
||||
self.testdomain)
|
||||
|
||||
def test_delete_domain(self):
|
||||
self.cs.dns_domains.delete(self.testdomain)
|
||||
self.assert_called('DELETE', '/os-floating-ip-dns/%s' %
|
||||
self.testdomain)
|
||||
|
||||
|
||||
class FloatingIPDNSEntryTest(utils.FixturedTestCase):
|
||||
|
||||
testname = "testname"
|
||||
testip = "1.2.3.4"
|
||||
testdomain = "testdomain"
|
||||
testtype = "A"
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.DNSFixture
|
||||
|
||||
def test_get_dns_entries_by_ip(self):
|
||||
entries = self.cs.dns_entries.get_for_ip(self.testdomain,
|
||||
ip=self.testip)
|
||||
self.assertEqual(2, len(entries))
|
||||
|
||||
for entry in entries:
|
||||
self.assertIsInstance(entry,
|
||||
floating_ip_dns.FloatingIPDNSEntry)
|
||||
|
||||
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):
|
||||
entry = self.cs.dns_entries.get(self.testdomain,
|
||||
self.testname)
|
||||
self.assertIsInstance(entry, floating_ip_dns.FloatingIPDNSEntry)
|
||||
self.assertEqual(entry.name, self.testname)
|
||||
|
||||
def test_create_entry(self):
|
||||
self.cs.dns_entries.create(self.testdomain,
|
||||
self.testname,
|
||||
self.testip,
|
||||
self.testtype)
|
||||
|
||||
self.assert_called('PUT', '/os-floating-ip-dns/%s/entries/%s' %
|
||||
(self.testdomain, self.testname))
|
||||
|
||||
def test_delete_entry(self):
|
||||
self.cs.dns_entries.delete(self.testdomain, self.testname)
|
||||
self.assert_called('DELETE', '/os-floating-ip-dns/%s/entries/%s' %
|
||||
(self.testdomain, self.testname))
|
||||
@ -0,0 +1,32 @@
|
||||
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
|
||||
#
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import floatingips as data
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import floating_ip_pools
|
||||
|
||||
|
||||
class TestFloatingIPPools(utils.FixturedTestCase):
|
||||
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.PoolsFixture
|
||||
|
||||
def test_list_floating_ips(self):
|
||||
fl = self.cs.floating_ip_pools.list()
|
||||
self.assert_called('GET', '/os-floating-ip-pools')
|
||||
for f in fl:
|
||||
self.assertIsInstance(f, floating_ip_pools.FloatingIPPool)
|
||||
@ -0,0 +1,59 @@
|
||||
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
|
||||
#
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import floatingips as data
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import floating_ips
|
||||
|
||||
|
||||
class FloatingIPsTest(utils.FixturedTestCase):
|
||||
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.FloatingFixture
|
||||
|
||||
def test_list_floating_ips(self):
|
||||
fips = self.cs.floating_ips.list()
|
||||
self.assert_called('GET', '/os-floating-ips')
|
||||
for fip in fips:
|
||||
self.assertIsInstance(fip, floating_ips.FloatingIP)
|
||||
|
||||
def test_list_floating_ips_all_tenants(self):
|
||||
fips = self.cs.floating_ips.list(all_tenants=True)
|
||||
self.assert_called('GET', '/os-floating-ips?all_tenants=1')
|
||||
for fip in fips:
|
||||
self.assertIsInstance(fip, floating_ips.FloatingIP)
|
||||
|
||||
def test_delete_floating_ip(self):
|
||||
fl = self.cs.floating_ips.list()[0]
|
||||
fl.delete()
|
||||
self.assert_called('DELETE', '/os-floating-ips/1')
|
||||
self.cs.floating_ips.delete(1)
|
||||
self.assert_called('DELETE', '/os-floating-ips/1')
|
||||
self.cs.floating_ips.delete(fl)
|
||||
self.assert_called('DELETE', '/os-floating-ips/1')
|
||||
|
||||
def test_create_floating_ip(self):
|
||||
fl = self.cs.floating_ips.create()
|
||||
self.assert_called('POST', '/os-floating-ips')
|
||||
self.assertIsNone(fl.pool)
|
||||
self.assertIsInstance(fl, floating_ips.FloatingIP)
|
||||
|
||||
def test_create_floating_ip_with_pool(self):
|
||||
fl = self.cs.floating_ips.create('nova')
|
||||
self.assert_called('POST', '/os-floating-ips')
|
||||
self.assertEqual('nova', fl.pool)
|
||||
self.assertIsInstance(fl, floating_ips.FloatingIP)
|
||||
@ -0,0 +1,64 @@
|
||||
# Copyright 2012 IBM Corp.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import floatingips as data
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import floating_ips_bulk
|
||||
|
||||
|
||||
class FloatingIPsBulkTest(utils.FixturedTestCase):
|
||||
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.BulkFixture
|
||||
|
||||
def test_list_floating_ips_bulk(self):
|
||||
fl = self.cs.floating_ips_bulk.list()
|
||||
self.assert_called('GET', '/os-floating-ips-bulk')
|
||||
[self.assertIsInstance(f, floating_ips_bulk.FloatingIP)
|
||||
for f in fl]
|
||||
|
||||
def test_list_floating_ips_bulk_host_filter(self):
|
||||
fl = self.cs.floating_ips_bulk.list('testHost')
|
||||
self.assert_called('GET', '/os-floating-ips-bulk/testHost')
|
||||
[self.assertIsInstance(f, floating_ips_bulk.FloatingIP)
|
||||
for f in fl]
|
||||
|
||||
def test_create_floating_ips_bulk(self):
|
||||
fl = self.cs.floating_ips_bulk.create('192.168.1.0/30')
|
||||
body = {'floating_ips_bulk_create': {'ip_range': '192.168.1.0/30'}}
|
||||
self.assert_called('POST', '/os-floating-ips-bulk', body)
|
||||
self.assertEqual(fl.ip_range,
|
||||
body['floating_ips_bulk_create']['ip_range'])
|
||||
|
||||
def test_create_floating_ips_bulk_with_pool_and_host(self):
|
||||
fl = self.cs.floating_ips_bulk.create('192.168.1.0/30', 'poolTest',
|
||||
'interfaceTest')
|
||||
body = {'floating_ips_bulk_create': {
|
||||
'ip_range': '192.168.1.0/30', 'pool': 'poolTest',
|
||||
'interface': 'interfaceTest'}}
|
||||
self.assert_called('POST', '/os-floating-ips-bulk', body)
|
||||
self.assertEqual(fl.ip_range,
|
||||
body['floating_ips_bulk_create']['ip_range'])
|
||||
self.assertEqual(fl.pool,
|
||||
body['floating_ips_bulk_create']['pool'])
|
||||
self.assertEqual(fl.interface,
|
||||
body['floating_ips_bulk_create']['interface'])
|
||||
|
||||
def test_delete_floating_ips_bulk(self):
|
||||
fl = self.cs.floating_ips_bulk.delete('192.168.1.0/30')
|
||||
body = {'ip_range': '192.168.1.0/30'}
|
||||
self.assert_called('PUT', '/os-floating-ips-bulk/delete', body)
|
||||
self.assertEqual(fl.floating_ips_bulk_delete, body['ip_range'])
|
||||
62
awx/lib/site-packages/novaclient/tests/unit/v2/test_fping.py
Normal file
62
awx/lib/site-packages/novaclient/tests/unit/v2/test_fping.py
Normal file
@ -0,0 +1,62 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import fping as data
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import fping
|
||||
|
||||
|
||||
class FpingTest(utils.FixturedTestCase):
|
||||
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.Fixture
|
||||
|
||||
def test_fping_repr(self):
|
||||
r = self.cs.fping.get(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("fake-project", f.project_id)
|
||||
self.assertTrue(f.alive)
|
||||
|
||||
def test_list_fpings_all_tenants(self):
|
||||
fl = self.cs.fping.list(all_tenants=True)
|
||||
for f in fl:
|
||||
self.assertIsInstance(f, fping.Fping)
|
||||
self.assert_called('GET', '/os-fping?all_tenants=1')
|
||||
|
||||
def test_list_fpings_exclude(self):
|
||||
fl = self.cs.fping.list(exclude=['1'])
|
||||
for f in fl:
|
||||
self.assertIsInstance(f, fping.Fping)
|
||||
self.assert_called('GET', '/os-fping?exclude=1')
|
||||
|
||||
def test_list_fpings_include(self):
|
||||
fl = self.cs.fping.list(include=['1'])
|
||||
for f in fl:
|
||||
self.assertIsInstance(f, fping.Fping)
|
||||
self.assert_called('GET', '/os-fping?include=1')
|
||||
|
||||
def test_get_fping(self):
|
||||
f = self.cs.fping.get(1)
|
||||
self.assert_called('GET', '/os-fping/1')
|
||||
self.assertIsInstance(f, fping.Fping)
|
||||
self.assertEqual("fake-project", f.project_id)
|
||||
self.assertTrue(f.alive)
|
||||
84
awx/lib/site-packages/novaclient/tests/unit/v2/test_hosts.py
Normal file
84
awx/lib/site-packages/novaclient/tests/unit/v2/test_hosts.py
Normal file
@ -0,0 +1,84 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import hosts as data
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import hosts
|
||||
|
||||
|
||||
class HostsTest(utils.FixturedTestCase):
|
||||
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.V1
|
||||
|
||||
def test_describe_resource(self):
|
||||
hs = self.cs.hosts.get('host')
|
||||
self.assert_called('GET', '/os-hosts/host')
|
||||
[self.assertIsInstance(h, hosts.Host) for h in hs]
|
||||
|
||||
def test_list_host(self):
|
||||
hs = self.cs.hosts.list()
|
||||
self.assert_called('GET', '/os-hosts')
|
||||
[self.assertIsInstance(h, hosts.Host) for h in hs]
|
||||
[self.assertEqual(h.zone, 'nova1') for h in hs]
|
||||
|
||||
def test_list_host_with_zone(self):
|
||||
hs = self.cs.hosts.list('nova')
|
||||
self.assert_called('GET', '/os-hosts?zone=nova')
|
||||
[self.assertIsInstance(h, hosts.Host) for h in hs]
|
||||
[self.assertEqual(h.zone, 'nova') for h in hs]
|
||||
|
||||
def test_update_enable(self):
|
||||
host = self.cs.hosts.get('sample_host')[0]
|
||||
values = {"status": "enabled"}
|
||||
result = host.update(values)
|
||||
self.assert_called('PUT', '/os-hosts/sample_host', values)
|
||||
self.assertIsInstance(result, hosts.Host)
|
||||
|
||||
def test_update_maintenance(self):
|
||||
host = self.cs.hosts.get('sample_host')[0]
|
||||
values = {"maintenance_mode": "enable"}
|
||||
result = host.update(values)
|
||||
self.assert_called('PUT', '/os-hosts/sample_host', values)
|
||||
self.assertIsInstance(result, hosts.Host)
|
||||
|
||||
def test_update_both(self):
|
||||
host = self.cs.hosts.get('sample_host')[0]
|
||||
values = {"status": "enabled",
|
||||
"maintenance_mode": "enable"}
|
||||
result = host.update(values)
|
||||
self.assert_called('PUT', '/os-hosts/sample_host', values)
|
||||
self.assertIsInstance(result, hosts.Host)
|
||||
|
||||
def test_host_startup(self):
|
||||
host = self.cs.hosts.get('sample_host')[0]
|
||||
host.startup()
|
||||
self.assert_called(
|
||||
'GET', '/os-hosts/sample_host/startup')
|
||||
|
||||
def test_host_reboot(self):
|
||||
host = self.cs.hosts.get('sample_host')[0]
|
||||
host.reboot()
|
||||
self.assert_called(
|
||||
'GET', '/os-hosts/sample_host/reboot')
|
||||
|
||||
def test_host_shutdown(self):
|
||||
host = self.cs.hosts.get('sample_host')[0]
|
||||
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]))
|
||||
@ -0,0 +1,178 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import hypervisors as data
|
||||
from novaclient.tests.unit import utils
|
||||
|
||||
|
||||
class HypervisorsTest(utils.FixturedTestCase):
|
||||
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.V1
|
||||
|
||||
def compare_to_expected(self, expected, hyper):
|
||||
for key, value in expected.items():
|
||||
self.assertEqual(getattr(hyper, key), value)
|
||||
|
||||
def test_hypervisor_index(self):
|
||||
expected = [
|
||||
dict(id=1234, hypervisor_hostname='hyper1'),
|
||||
dict(id=5678, hypervisor_hostname='hyper2')]
|
||||
|
||||
result = self.cs.hypervisors.list(False)
|
||||
self.assert_called('GET', '/os-hypervisors')
|
||||
|
||||
for idx, hyper in enumerate(result):
|
||||
self.compare_to_expected(expected[idx], hyper)
|
||||
|
||||
def test_hypervisor_detail(self):
|
||||
expected = [
|
||||
dict(id=1234,
|
||||
service=dict(id=1, host='compute1'),
|
||||
vcpus=4,
|
||||
memory_mb=10 * 1024,
|
||||
local_gb=250,
|
||||
vcpus_used=2,
|
||||
memory_mb_used=5 * 1024,
|
||||
local_gb_used=125,
|
||||
hypervisor_type="xen",
|
||||
hypervisor_version=3,
|
||||
hypervisor_hostname="hyper1",
|
||||
free_ram_mb=5 * 1024,
|
||||
free_disk_gb=125,
|
||||
current_workload=2,
|
||||
running_vms=2,
|
||||
cpu_info='cpu_info',
|
||||
disk_available_least=100),
|
||||
dict(id=2,
|
||||
service=dict(id=2, host="compute2"),
|
||||
vcpus=4,
|
||||
memory_mb=10 * 1024,
|
||||
local_gb=250,
|
||||
vcpus_used=2,
|
||||
memory_mb_used=5 * 1024,
|
||||
local_gb_used=125,
|
||||
hypervisor_type="xen",
|
||||
hypervisor_version=3,
|
||||
hypervisor_hostname="hyper2",
|
||||
free_ram_mb=5 * 1024,
|
||||
free_disk_gb=125,
|
||||
current_workload=2,
|
||||
running_vms=2,
|
||||
cpu_info='cpu_info',
|
||||
disk_available_least=100)]
|
||||
|
||||
result = self.cs.hypervisors.list()
|
||||
self.assert_called('GET', '/os-hypervisors/detail')
|
||||
|
||||
for idx, hyper in enumerate(result):
|
||||
self.compare_to_expected(expected[idx], hyper)
|
||||
|
||||
def test_hypervisor_search(self):
|
||||
expected = [
|
||||
dict(id=1234, hypervisor_hostname='hyper1'),
|
||||
dict(id=5678, hypervisor_hostname='hyper2')]
|
||||
|
||||
result = self.cs.hypervisors.search('hyper')
|
||||
self.assert_called('GET', '/os-hypervisors/hyper/search')
|
||||
|
||||
for idx, hyper in enumerate(result):
|
||||
self.compare_to_expected(expected[idx], hyper)
|
||||
|
||||
def test_hypervisor_servers(self):
|
||||
expected = [
|
||||
dict(id=1234,
|
||||
hypervisor_hostname='hyper1',
|
||||
servers=[
|
||||
dict(name='inst1', uuid='uuid1'),
|
||||
dict(name='inst2', uuid='uuid2')]),
|
||||
dict(id=5678,
|
||||
hypervisor_hostname='hyper2',
|
||||
servers=[
|
||||
dict(name='inst3', uuid='uuid3'),
|
||||
dict(name='inst4', uuid='uuid4')]),
|
||||
]
|
||||
|
||||
result = self.cs.hypervisors.search('hyper', True)
|
||||
self.assert_called('GET', '/os-hypervisors/hyper/servers')
|
||||
|
||||
for idx, hyper in enumerate(result):
|
||||
self.compare_to_expected(expected[idx], hyper)
|
||||
|
||||
def test_hypervisor_get(self):
|
||||
expected = dict(
|
||||
id=1234,
|
||||
service=dict(id=1, host='compute1'),
|
||||
vcpus=4,
|
||||
memory_mb=10 * 1024,
|
||||
local_gb=250,
|
||||
vcpus_used=2,
|
||||
memory_mb_used=5 * 1024,
|
||||
local_gb_used=125,
|
||||
hypervisor_type="xen",
|
||||
hypervisor_version=3,
|
||||
hypervisor_hostname="hyper1",
|
||||
free_ram_mb=5 * 1024,
|
||||
free_disk_gb=125,
|
||||
current_workload=2,
|
||||
running_vms=2,
|
||||
cpu_info='cpu_info',
|
||||
disk_available_least=100)
|
||||
|
||||
result = self.cs.hypervisors.get(1234)
|
||||
self.assert_called('GET', '/os-hypervisors/1234')
|
||||
|
||||
self.compare_to_expected(expected, result)
|
||||
|
||||
def test_hypervisor_uptime(self):
|
||||
expected = dict(
|
||||
id=1234,
|
||||
hypervisor_hostname="hyper1",
|
||||
uptime="fake uptime")
|
||||
|
||||
result = self.cs.hypervisors.uptime(1234)
|
||||
self.assert_called('GET', '/os-hypervisors/1234/uptime')
|
||||
|
||||
self.compare_to_expected(expected, result)
|
||||
|
||||
def test_hypervisor_statistics(self):
|
||||
expected = dict(
|
||||
count=2,
|
||||
vcpus=8,
|
||||
memory_mb=20 * 1024,
|
||||
local_gb=500,
|
||||
vcpus_used=4,
|
||||
memory_mb_used=10 * 1024,
|
||||
local_gb_used=250,
|
||||
free_ram_mb=10 * 1024,
|
||||
free_disk_gb=250,
|
||||
current_workload=4,
|
||||
running_vms=4,
|
||||
disk_available_least=200,
|
||||
)
|
||||
|
||||
result = self.cs.hypervisors.statistics()
|
||||
self.assert_called('GET', '/os-hypervisors/statistics')
|
||||
|
||||
self.compare_to_expected(expected, result)
|
||||
|
||||
def test_hypervisor_statistics_data_model(self):
|
||||
result = self.cs.hypervisor_stats.statistics()
|
||||
self.assert_called('GET', '/os-hypervisors/statistics')
|
||||
|
||||
# Test for Bug #1370415, the line below used to raise AttributeError
|
||||
self.assertEqual("<HypervisorStats: 2 Hypervisors>",
|
||||
result.__repr__())
|
||||
@ -0,0 +1,67 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import images as data
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import images
|
||||
|
||||
|
||||
class ImagesTest(utils.FixturedTestCase):
|
||||
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.V1
|
||||
|
||||
def test_list_images(self):
|
||||
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)
|
||||
self.assert_called('GET', '/images')
|
||||
[self.assertIsInstance(i, images.Image) for i in il]
|
||||
|
||||
def test_list_images_with_limit(self):
|
||||
self.cs.images.list(limit=4)
|
||||
self.assert_called('GET', '/images/detail?limit=4')
|
||||
|
||||
def test_get_image_details(self):
|
||||
i = self.cs.images.get(1)
|
||||
self.assert_called('GET', '/images/1')
|
||||
self.assertIsInstance(i, images.Image)
|
||||
self.assertEqual(1, i.id)
|
||||
self.assertEqual('CentOS 5.2', i.name)
|
||||
|
||||
def test_delete_image(self):
|
||||
self.cs.images.delete(1)
|
||||
self.assert_called('DELETE', '/images/1')
|
||||
|
||||
def test_delete_meta(self):
|
||||
self.cs.images.delete_meta(1, {'test_key': 'test_value'})
|
||||
self.assert_called('DELETE', '/images/1/metadata/test_key')
|
||||
|
||||
def test_set_meta(self):
|
||||
self.cs.images.set_meta(1, {'test_key': 'test_value'})
|
||||
self.assert_called('POST', '/images/1/metadata',
|
||||
{"metadata": {'test_key': 'test_value'}})
|
||||
|
||||
def test_find(self):
|
||||
i = self.cs.images.find(name="CentOS 5.2")
|
||||
self.assertEqual(1, i.id)
|
||||
self.assert_called('GET', '/images/1')
|
||||
|
||||
iml = self.cs.images.findall(status='SAVING')
|
||||
self.assertEqual(1, len(iml))
|
||||
self.assertEqual('My Server Backup', iml[0].name)
|
||||
@ -0,0 +1,64 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import keypairs as data
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import keypairs
|
||||
|
||||
|
||||
class KeypairsTest(utils.FixturedTestCase):
|
||||
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.V1
|
||||
|
||||
def setUp(self):
|
||||
super(KeypairsTest, self).setUp()
|
||||
self.keypair_type = self._get_keypair_type()
|
||||
self.keypair_prefix = self._get_keypair_prefix()
|
||||
|
||||
def _get_keypair_type(self):
|
||||
return keypairs.Keypair
|
||||
|
||||
def _get_keypair_prefix(self):
|
||||
return keypairs.KeypairManager.keypair_prefix
|
||||
|
||||
def test_get_keypair(self):
|
||||
kp = self.cs.keypairs.get('test')
|
||||
self.assert_called('GET', '/%s/test' % self.keypair_prefix)
|
||||
self.assertIsInstance(kp, keypairs.Keypair)
|
||||
self.assertEqual('test', kp.name)
|
||||
|
||||
def test_list_keypairs(self):
|
||||
kps = self.cs.keypairs.list()
|
||||
self.assert_called('GET', '/%s' % self.keypair_prefix)
|
||||
[self.assertIsInstance(kp, keypairs.Keypair) for kp in kps]
|
||||
|
||||
def test_delete_keypair(self):
|
||||
kp = self.cs.keypairs.list()[0]
|
||||
kp.delete()
|
||||
self.assert_called('DELETE', '/%s/test' % self.keypair_prefix)
|
||||
self.cs.keypairs.delete('test')
|
||||
self.assert_called('DELETE', '/%s/test' % self.keypair_prefix)
|
||||
self.cs.keypairs.delete(kp)
|
||||
self.assert_called('DELETE', '/%s/test' % self.keypair_prefix)
|
||||
|
||||
def test_create_keypair(self):
|
||||
kp = self.cs.keypairs.create("foo")
|
||||
self.assert_called('POST', '/%s' % self.keypair_prefix)
|
||||
self.assertIsInstance(kp, keypairs.Keypair)
|
||||
|
||||
def test_import_keypair(self):
|
||||
kp = self.cs.keypairs.create("foo", "fake-public-key")
|
||||
self.assert_called('POST', '/%s' % self.keypair_prefix)
|
||||
self.assertIsInstance(kp, keypairs.Keypair)
|
||||
@ -0,0 +1,88 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import limits as data
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import limits
|
||||
|
||||
|
||||
class LimitsTest(utils.FixturedTestCase):
|
||||
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.Fixture
|
||||
|
||||
def test_get_limits(self):
|
||||
obj = self.cs.limits.get()
|
||||
self.assert_called('GET', '/limits')
|
||||
self.assertIsInstance(obj, limits.Limits)
|
||||
|
||||
def test_get_limits_for_a_tenant(self):
|
||||
obj = self.cs.limits.get(tenant_id=1234)
|
||||
self.assert_called('GET', '/limits?tenant_id=1234')
|
||||
self.assertIsInstance(obj, limits.Limits)
|
||||
|
||||
def test_absolute_limits(self):
|
||||
obj = self.cs.limits.get()
|
||||
|
||||
expected = (
|
||||
limits.AbsoluteLimit("maxTotalRAMSize", 51200),
|
||||
limits.AbsoluteLimit("maxServerMeta", 5),
|
||||
limits.AbsoluteLimit("maxImageMeta", 5),
|
||||
limits.AbsoluteLimit("maxPersonality", 5),
|
||||
limits.AbsoluteLimit("maxPersonalitySize", 10240),
|
||||
)
|
||||
|
||||
abs_limits = list(obj.absolute)
|
||||
self.assertEqual(len(abs_limits), len(expected))
|
||||
|
||||
for limit in abs_limits:
|
||||
self.assertIn(limit, expected)
|
||||
|
||||
def test_absolute_limits_reserved(self):
|
||||
obj = self.cs.limits.get(reserved=True)
|
||||
|
||||
expected = (
|
||||
limits.AbsoluteLimit("maxTotalRAMSize", 51200),
|
||||
limits.AbsoluteLimit("maxServerMeta", 5),
|
||||
limits.AbsoluteLimit("maxImageMeta", 5),
|
||||
limits.AbsoluteLimit("maxPersonality", 5),
|
||||
limits.AbsoluteLimit("maxPersonalitySize", 10240),
|
||||
)
|
||||
|
||||
self.assert_called('GET', '/limits?reserved=1')
|
||||
abs_limits = list(obj.absolute)
|
||||
self.assertEqual(len(abs_limits), len(expected))
|
||||
|
||||
for limit in abs_limits:
|
||||
self.assertIn(limit, expected)
|
||||
|
||||
def test_rate_limits(self):
|
||||
obj = self.cs.limits.get()
|
||||
|
||||
expected = (
|
||||
limits.RateLimit('POST', '*', '.*', 10, 2, 'MINUTE',
|
||||
'2011-12-15T22:42:45Z'),
|
||||
limits.RateLimit('PUT', '*', '.*', 10, 2, 'MINUTE',
|
||||
'2011-12-15T22:42:45Z'),
|
||||
limits.RateLimit('DELETE', '*', '.*', 100, 100, 'MINUTE',
|
||||
'2011-12-15T22:42:45Z'),
|
||||
limits.RateLimit('POST', '*/servers', '^/servers', 25, 24, 'DAY',
|
||||
'2011-12-15T22:42:45Z'),
|
||||
)
|
||||
|
||||
rate_limits = list(obj.rate)
|
||||
self.assertEqual(len(rate_limits), len(expected))
|
||||
|
||||
for limit in rate_limits:
|
||||
self.assertIn(limit, expected)
|
||||
106
awx/lib/site-packages/novaclient/tests/unit/v2/test_networks.py
Normal file
106
awx/lib/site-packages/novaclient/tests/unit/v2/test_networks.py
Normal file
@ -0,0 +1,106 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import networks as data
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import networks
|
||||
|
||||
|
||||
class NetworksTest(utils.FixturedTestCase):
|
||||
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.Fixture
|
||||
|
||||
def test_list_networks(self):
|
||||
fl = self.cs.networks.list()
|
||||
self.assert_called('GET', '/os-networks')
|
||||
[self.assertIsInstance(f, networks.Network) for f in fl]
|
||||
|
||||
def test_get_network(self):
|
||||
f = self.cs.networks.get(1)
|
||||
self.assert_called('GET', '/os-networks/1')
|
||||
self.assertIsInstance(f, networks.Network)
|
||||
|
||||
def test_delete(self):
|
||||
self.cs.networks.delete('networkdelete')
|
||||
self.assert_called('DELETE', '/os-networks/networkdelete')
|
||||
|
||||
def test_create(self):
|
||||
f = self.cs.networks.create(label='foo')
|
||||
self.assert_called('POST', '/os-networks',
|
||||
{'network': {'label': 'foo'}})
|
||||
self.assertIsInstance(f, networks.Network)
|
||||
|
||||
def test_create_allparams(self):
|
||||
params = {
|
||||
'label': 'bar',
|
||||
'bridge': 'br0',
|
||||
'bridge_interface': 'int0',
|
||||
'cidr': '192.0.2.0/24',
|
||||
'cidr_v6': '2001:DB8::/32',
|
||||
'dns1': '1.1.1.1',
|
||||
'dns2': '1.1.1.2',
|
||||
'fixed_cidr': '198.51.100.0/24',
|
||||
'gateway': '192.0.2.1',
|
||||
'gateway_v6': '2001:DB8::1',
|
||||
'multi_host': 'T',
|
||||
'priority': '1',
|
||||
'project_id': '1',
|
||||
'vlan': 5,
|
||||
'vlan_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)
|
||||
self.assert_called('POST', '/os-networks', {'network': params})
|
||||
self.assertIsInstance(f, networks.Network)
|
||||
|
||||
def test_associate_project(self):
|
||||
self.cs.networks.associate_project('networktest')
|
||||
self.assert_called('POST', '/os-networks/add',
|
||||
{'id': 'networktest'})
|
||||
|
||||
def test_associate_host(self):
|
||||
self.cs.networks.associate_host('networktest', 'testHost')
|
||||
self.assert_called('POST', '/os-networks/networktest/action',
|
||||
{'associate_host': 'testHost'})
|
||||
|
||||
def test_disassociate(self):
|
||||
self.cs.networks.disassociate('networkdisassociate')
|
||||
self.assert_called('POST',
|
||||
'/os-networks/networkdisassociate/action',
|
||||
{'disassociate': None})
|
||||
|
||||
def test_disassociate_host_only(self):
|
||||
self.cs.networks.disassociate('networkdisassociate', True, False)
|
||||
self.assert_called('POST',
|
||||
'/os-networks/networkdisassociate/action',
|
||||
{'disassociate_host': None})
|
||||
|
||||
def test_disassociate_project(self):
|
||||
self.cs.networks.disassociate('networkdisassociate', False, True)
|
||||
self.assert_called('POST',
|
||||
'/os-networks/networkdisassociate/action',
|
||||
{'disassociate_project': None})
|
||||
|
||||
def test_add(self):
|
||||
self.cs.networks.add('networkadd')
|
||||
self.assert_called('POST', '/os-networks/add',
|
||||
{'id': 'networkadd'})
|
||||
@ -0,0 +1,42 @@
|
||||
# Copyright 2011 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.tests.unit.v2 import fakes
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
|
||||
|
||||
class QuotaClassSetsTest(utils.TestCase):
|
||||
|
||||
def test_class_quotas_get(self):
|
||||
class_name = 'test'
|
||||
cs.quota_classes.get(class_name)
|
||||
cs.assert_called('GET', '/os-quota-class-sets/%s' % class_name)
|
||||
|
||||
def test_update_quota(self):
|
||||
q = cs.quota_classes.get('test')
|
||||
q.update(cores=2)
|
||||
cs.assert_called('PUT', '/os-quota-class-sets/test')
|
||||
|
||||
def test_refresh_quota(self):
|
||||
q = cs.quota_classes.get('test')
|
||||
q2 = cs.quota_classes.get('test')
|
||||
self.assertEqual(q.cores, q2.cores)
|
||||
q2.cores = 0
|
||||
self.assertNotEqual(q.cores, q2.cores)
|
||||
q2.get()
|
||||
self.assertEqual(q.cores, q2.cores)
|
||||
@ -0,0 +1,62 @@
|
||||
# Copyright 2011 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import quotas as data
|
||||
from novaclient.tests.unit import utils
|
||||
|
||||
|
||||
class QuotaSetsTest(utils.FixturedTestCase):
|
||||
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.V1
|
||||
|
||||
def test_tenant_quotas_get(self):
|
||||
tenant_id = 'test'
|
||||
self.cs.quotas.get(tenant_id)
|
||||
self.assert_called('GET', '/os-quota-sets/%s' % tenant_id)
|
||||
|
||||
def test_user_quotas_get(self):
|
||||
tenant_id = 'test'
|
||||
user_id = 'fake_user'
|
||||
self.cs.quotas.get(tenant_id, user_id=user_id)
|
||||
url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id)
|
||||
self.assert_called('GET', url)
|
||||
|
||||
def test_tenant_quotas_defaults(self):
|
||||
tenant_id = '97f4c221bff44578b0300df4ef119353'
|
||||
self.cs.quotas.defaults(tenant_id)
|
||||
self.assert_called('GET', '/os-quota-sets/%s/defaults' % tenant_id)
|
||||
|
||||
def test_force_update_quota(self):
|
||||
q = self.cs.quotas.get('97f4c221bff44578b0300df4ef119353')
|
||||
q.update(cores=2, force=True)
|
||||
self.assert_called(
|
||||
'PUT', '/os-quota-sets/97f4c221bff44578b0300df4ef119353',
|
||||
{'quota_set': {'force': True,
|
||||
'cores': 2,
|
||||
'tenant_id': '97f4c221bff44578b0300df4ef119353'}})
|
||||
|
||||
def test_quotas_delete(self):
|
||||
tenant_id = 'test'
|
||||
self.cs.quotas.delete(tenant_id)
|
||||
self.assert_called('DELETE', '/os-quota-sets/%s' % tenant_id)
|
||||
|
||||
def test_user_quotas_delete(self):
|
||||
tenant_id = 'test'
|
||||
user_id = 'fake_user'
|
||||
self.cs.quotas.delete(tenant_id, user_id=user_id)
|
||||
url = '/os-quota-sets/%s?user_id=%s' % (tenant_id, user_id)
|
||||
self.assert_called('DELETE', url)
|
||||
@ -0,0 +1,89 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient import exceptions
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import security_group_rules as data
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import security_group_rules
|
||||
|
||||
|
||||
class SecurityGroupRulesTest(utils.FixturedTestCase):
|
||||
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.Fixture
|
||||
|
||||
def test_delete_security_group_rule(self):
|
||||
self.cs.security_group_rules.delete(1)
|
||||
self.assert_called('DELETE', '/os-security-group-rules/1')
|
||||
|
||||
def test_create_security_group_rule(self):
|
||||
sg = self.cs.security_group_rules.create(1, "tcp", 1, 65535,
|
||||
"10.0.0.0/16")
|
||||
|
||||
body = {
|
||||
"security_group_rule": {
|
||||
"ip_protocol": "tcp",
|
||||
"from_port": 1,
|
||||
"to_port": 65535,
|
||||
"cidr": "10.0.0.0/16",
|
||||
"group_id": None,
|
||||
"parent_group_id": 1,
|
||||
}
|
||||
}
|
||||
|
||||
self.assert_called('POST', '/os-security-group-rules', body)
|
||||
self.assertTrue(isinstance(sg, security_group_rules.SecurityGroupRule))
|
||||
|
||||
def test_create_security_group_group_rule(self):
|
||||
sg = self.cs.security_group_rules.create(1, "tcp", 1, 65535,
|
||||
"10.0.0.0/16", 101)
|
||||
|
||||
body = {
|
||||
"security_group_rule": {
|
||||
"ip_protocol": "tcp",
|
||||
"from_port": 1,
|
||||
"to_port": 65535,
|
||||
"cidr": "10.0.0.0/16",
|
||||
"group_id": 101,
|
||||
"parent_group_id": 1,
|
||||
}
|
||||
}
|
||||
|
||||
self.assert_called('POST', '/os-security-group-rules', body)
|
||||
self.assertTrue(isinstance(sg, security_group_rules.SecurityGroupRule))
|
||||
|
||||
def test_invalid_parameters_create(self):
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
self.cs.security_group_rules.create,
|
||||
1, "invalid_ip_protocol", 1, 65535,
|
||||
"10.0.0.0/16", 101)
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
self.cs.security_group_rules.create,
|
||||
1, "tcp", "invalid_from_port", 65535,
|
||||
"10.0.0.0/16", 101)
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
self.cs.security_group_rules.create,
|
||||
1, "tcp", 1, "invalid_to_port",
|
||||
"10.0.0.0/16", 101)
|
||||
|
||||
def test_security_group_rule_str(self):
|
||||
sg = self.cs.security_group_rules.create(1, "tcp", 1, 65535,
|
||||
"10.0.0.0/16")
|
||||
self.assertEqual('1', str(sg))
|
||||
|
||||
def test_security_group_rule_del(self):
|
||||
sg = self.cs.security_group_rules.create(1, "tcp", 1, 65535,
|
||||
"10.0.0.0/16")
|
||||
sg.delete()
|
||||
self.assert_called('DELETE', '/os-security-group-rules/1')
|
||||
@ -0,0 +1,76 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import security_groups as data
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import security_groups
|
||||
|
||||
|
||||
class SecurityGroupsTest(utils.FixturedTestCase):
|
||||
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.Fixture
|
||||
|
||||
def _do_test_list_security_groups(self, search_opts, path):
|
||||
sgs = self.cs.security_groups.list(search_opts=search_opts)
|
||||
self.assert_called('GET', path)
|
||||
for sg in sgs:
|
||||
self.assertIsInstance(sg, security_groups.SecurityGroup)
|
||||
|
||||
def test_list_security_groups_all_tenants_on(self):
|
||||
self._do_test_list_security_groups(
|
||||
None, '/os-security-groups')
|
||||
|
||||
def test_list_security_groups_all_tenants_on_with_search_opts(self):
|
||||
self._do_test_list_security_groups(
|
||||
{'all_tenants': 1}, '/os-security-groups?all_tenants=1')
|
||||
|
||||
def test_list_security_groups_all_tenants_off(self):
|
||||
self._do_test_list_security_groups(
|
||||
{'all_tenants': 0}, '/os-security-groups')
|
||||
|
||||
def test_get_security_groups(self):
|
||||
sg = self.cs.security_groups.get(1)
|
||||
self.assert_called('GET', '/os-security-groups/1')
|
||||
self.assertIsInstance(sg, security_groups.SecurityGroup)
|
||||
self.assertEqual('1', str(sg))
|
||||
|
||||
def test_delete_security_group(self):
|
||||
sg = self.cs.security_groups.list()[0]
|
||||
sg.delete()
|
||||
self.assert_called('DELETE', '/os-security-groups/1')
|
||||
self.cs.security_groups.delete(1)
|
||||
self.assert_called('DELETE', '/os-security-groups/1')
|
||||
self.cs.security_groups.delete(sg)
|
||||
self.assert_called('DELETE', '/os-security-groups/1')
|
||||
|
||||
def test_create_security_group(self):
|
||||
sg = self.cs.security_groups.create("foo", "foo barr")
|
||||
self.assert_called('POST', '/os-security-groups')
|
||||
self.assertIsInstance(sg, security_groups.SecurityGroup)
|
||||
|
||||
def test_update_security_group(self):
|
||||
sg = self.cs.security_groups.list()[0]
|
||||
secgroup = self.cs.security_groups.update(sg, "update", "update")
|
||||
self.assert_called('PUT', '/os-security-groups/1')
|
||||
self.assertIsInstance(secgroup, security_groups.SecurityGroup)
|
||||
|
||||
def test_refresh_security_group(self):
|
||||
sg = self.cs.security_groups.get(1)
|
||||
sg2 = self.cs.security_groups.get(1)
|
||||
self.assertEqual(sg.name, sg2.name)
|
||||
sg2.name = "should be test"
|
||||
self.assertNotEqual(sg.name, sg2.name)
|
||||
sg2.get()
|
||||
self.assertEqual(sg.name, sg2.name)
|
||||
@ -0,0 +1,59 @@
|
||||
# Copyright (c) 2014 VMware, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import server_groups as data
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import server_groups
|
||||
|
||||
|
||||
class ServerGroupsTest(utils.FixturedTestCase):
|
||||
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.Fixture
|
||||
|
||||
def test_list_server_groups(self):
|
||||
result = self.cs.server_groups.list()
|
||||
self.assert_called('GET', '/os-server-groups')
|
||||
for server_group in result:
|
||||
self.assertTrue(isinstance(server_group,
|
||||
server_groups.ServerGroup))
|
||||
|
||||
def test_create_server_group(self):
|
||||
kwargs = {'name': 'ig1',
|
||||
'policies': ['anti-affinity']}
|
||||
server_group = self.cs.server_groups.create(**kwargs)
|
||||
body = {'server_group': kwargs}
|
||||
self.assert_called('POST', '/os-server-groups', body)
|
||||
self.assertTrue(isinstance(server_group,
|
||||
server_groups.ServerGroup))
|
||||
|
||||
def test_get_server_group(self):
|
||||
id = '2cbd51f4-fafe-4cdb-801b-cf913a6f288b'
|
||||
server_group = self.cs.server_groups.get(id)
|
||||
self.assert_called('GET', '/os-server-groups/%s' % id)
|
||||
self.assertTrue(isinstance(server_group,
|
||||
server_groups.ServerGroup))
|
||||
|
||||
def test_delete_server_group(self):
|
||||
id = '2cbd51f4-fafe-4cdb-801b-cf913a6f288b'
|
||||
self.cs.server_groups.delete(id)
|
||||
self.assert_called('DELETE', '/os-server-groups/%s' % id)
|
||||
|
||||
def test_delete_server_group_object(self):
|
||||
id = '2cbd51f4-fafe-4cdb-801b-cf913a6f288b'
|
||||
server_group = self.cs.server_groups.get(id)
|
||||
server_group.delete()
|
||||
self.assert_called('DELETE', '/os-server-groups/%s' % id)
|
||||
704
awx/lib/site-packages/novaclient/tests/unit/v2/test_servers.py
Normal file
704
awx/lib/site-packages/novaclient/tests/unit/v2/test_servers.py
Normal file
@ -0,0 +1,704 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
from oslo.serialization import jsonutils
|
||||
import six
|
||||
|
||||
from novaclient import exceptions
|
||||
from novaclient.tests.unit.fixture_data import client
|
||||
from novaclient.tests.unit.fixture_data import floatingips
|
||||
from novaclient.tests.unit.fixture_data import servers as data
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.v2 import servers
|
||||
|
||||
|
||||
class ServersTest(utils.FixturedTestCase):
|
||||
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.V1
|
||||
|
||||
def setUp(self):
|
||||
super(ServersTest, self).setUp()
|
||||
self.useFixture(floatingips.FloatingFixture(self.requests))
|
||||
|
||||
def test_list_servers(self):
|
||||
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 = 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 = 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_list_servers_sort_single(self):
|
||||
sl = self.cs.servers.list(sort_keys=['display_name'],
|
||||
sort_dirs=['asc'])
|
||||
self.assert_called(
|
||||
'GET',
|
||||
'/servers/detail?sort_dir=asc&sort_key=display_name')
|
||||
for s in sl:
|
||||
self.assertIsInstance(s, servers.Server)
|
||||
|
||||
def test_list_servers_sort_multiple(self):
|
||||
sl = self.cs.servers.list(sort_keys=['display_name', 'id'],
|
||||
sort_dirs=['asc', 'desc'])
|
||||
self.assert_called(
|
||||
'GET',
|
||||
('/servers/detail?sort_dir=asc&sort_dir=desc&'
|
||||
'sort_key=display_name&sort_key=id'))
|
||||
for s in sl:
|
||||
self.assertIsInstance(s, servers.Server)
|
||||
|
||||
def test_get_server_details(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
self.assert_called('GET', '/servers/1234')
|
||||
self.assertIsInstance(s, servers.Server)
|
||||
self.assertEqual(1234, s.id)
|
||||
self.assertEqual('BUILD', s.status)
|
||||
|
||||
def test_get_server_promote_details(self):
|
||||
s1 = self.cs.servers.list(detailed=False)[0]
|
||||
s2 = self.cs.servers.list(detailed=True)[0]
|
||||
self.assertNotEqual(s1._info, s2._info)
|
||||
s1.get()
|
||||
self.assertEqual(s1._info, s2._info)
|
||||
|
||||
def test_create_server(self):
|
||||
s = self.cs.servers.create(
|
||||
name="My server",
|
||||
image=1,
|
||||
flavor=1,
|
||||
meta={'foo': 'bar'},
|
||||
userdata="hello moto",
|
||||
key_name="fakekey",
|
||||
files={
|
||||
'/etc/passwd': 'some data', # a file
|
||||
'/tmp/foo.txt': six.StringIO('data'), # a stream
|
||||
}
|
||||
)
|
||||
self.assert_called('POST', '/servers')
|
||||
self.assertIsInstance(s, servers.Server)
|
||||
|
||||
def test_create_server_boot_from_volume_with_nics(self):
|
||||
old_boot = self.cs.servers._boot
|
||||
|
||||
nics = [{'net-id': '11111111-1111-1111-1111-111111111111',
|
||||
'v4-fixed-ip': '10.0.0.7'}]
|
||||
bdm = {"volume_size": "1",
|
||||
"volume_id": "11111111-1111-1111-1111-111111111111",
|
||||
"delete_on_termination": "0",
|
||||
"device_name": "vda"}
|
||||
|
||||
def wrapped_boot(url, key, *boot_args, **boot_kwargs):
|
||||
self.assertEqual(boot_kwargs['block_device_mapping'], bdm)
|
||||
self.assertEqual(boot_kwargs['nics'], nics)
|
||||
return old_boot(url, key, *boot_args, **boot_kwargs)
|
||||
|
||||
@mock.patch.object(self.cs.servers, '_boot', wrapped_boot)
|
||||
def test_create_server_from_volume():
|
||||
s = self.cs.servers.create(
|
||||
name="My server",
|
||||
image=1,
|
||||
flavor=1,
|
||||
meta={'foo': 'bar'},
|
||||
userdata="hello moto",
|
||||
key_name="fakekey",
|
||||
block_device_mapping=bdm,
|
||||
nics=nics
|
||||
)
|
||||
self.assert_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 = self.cs.servers._boot
|
||||
nics = [{'net-id': '11111111-1111-1111-1111-111111111111',
|
||||
'v6-fixed-ip': '2001:db9:0:1::10'}]
|
||||
|
||||
def wrapped_boot(url, key, *boot_args, **boot_kwargs):
|
||||
self.assertEqual(boot_kwargs['nics'], nics)
|
||||
return old_boot(url, key, *boot_args, **boot_kwargs)
|
||||
|
||||
with mock.patch.object(self.cs.servers, '_boot', wrapped_boot):
|
||||
s = self.cs.servers.create(
|
||||
name="My server",
|
||||
image=1,
|
||||
flavor=1,
|
||||
meta={'foo': 'bar'},
|
||||
userdata="hello moto",
|
||||
key_name="fakekey",
|
||||
nics=nics
|
||||
)
|
||||
self.assert_called('POST', '/servers')
|
||||
self.assertIsInstance(s, servers.Server)
|
||||
|
||||
def test_create_server_userdata_file_object(self):
|
||||
s = self.cs.servers.create(
|
||||
name="My server",
|
||||
image=1,
|
||||
flavor=1,
|
||||
meta={'foo': 'bar'},
|
||||
userdata=six.StringIO('hello moto'),
|
||||
files={
|
||||
'/etc/passwd': 'some data', # a file
|
||||
'/tmp/foo.txt': six.StringIO('data'), # a stream
|
||||
},
|
||||
)
|
||||
self.assert_called('POST', '/servers')
|
||||
self.assertIsInstance(s, servers.Server)
|
||||
|
||||
def test_create_server_userdata_unicode(self):
|
||||
s = self.cs.servers.create(
|
||||
name="My server",
|
||||
image=1,
|
||||
flavor=1,
|
||||
meta={'foo': 'bar'},
|
||||
userdata=six.u('こんにちは'),
|
||||
key_name="fakekey",
|
||||
files={
|
||||
'/etc/passwd': 'some data', # a file
|
||||
'/tmp/foo.txt': six.StringIO('data'), # a stream
|
||||
},
|
||||
)
|
||||
self.assert_called('POST', '/servers')
|
||||
self.assertIsInstance(s, servers.Server)
|
||||
|
||||
def test_create_server_userdata_utf8(self):
|
||||
s = self.cs.servers.create(
|
||||
name="My server",
|
||||
image=1,
|
||||
flavor=1,
|
||||
meta={'foo': 'bar'},
|
||||
userdata='こんにちは',
|
||||
key_name="fakekey",
|
||||
files={
|
||||
'/etc/passwd': 'some data', # a file
|
||||
'/tmp/foo.txt': six.StringIO('data'), # a stream
|
||||
},
|
||||
)
|
||||
self.assert_called('POST', '/servers')
|
||||
self.assertIsInstance(s, servers.Server)
|
||||
|
||||
def _create_disk_config(self, disk_config):
|
||||
s = self.cs.servers.create(
|
||||
name="My server",
|
||||
image=1,
|
||||
flavor=1,
|
||||
disk_config=disk_config
|
||||
)
|
||||
self.assert_called('POST', '/servers')
|
||||
self.assertIsInstance(s, servers.Server)
|
||||
|
||||
# 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.assertEqual(disk_config, server['OS-DCF:diskConfig'])
|
||||
|
||||
def test_create_server_disk_config_auto(self):
|
||||
self._create_disk_config('AUTO')
|
||||
|
||||
def test_create_server_disk_config_manual(self):
|
||||
self._create_disk_config('MANUAL')
|
||||
|
||||
def test_update_server(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
|
||||
# Update via instance
|
||||
s.update(name='hi')
|
||||
self.assert_called('PUT', '/servers/1234')
|
||||
s.update(name='hi')
|
||||
self.assert_called('PUT', '/servers/1234')
|
||||
|
||||
# Silly, but not an error
|
||||
s.update()
|
||||
|
||||
# Update via manager
|
||||
self.cs.servers.update(s, name='hi')
|
||||
self.assert_called('PUT', '/servers/1234')
|
||||
|
||||
def test_delete_server(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.delete()
|
||||
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):
|
||||
self.cs.servers.delete_meta(1234, ['test_key'])
|
||||
self.assert_called('DELETE', '/servers/1234/metadata/test_key')
|
||||
|
||||
def test_set_server_meta(self):
|
||||
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):
|
||||
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 = self.cs.servers.find(name='sample-server')
|
||||
self.assert_called('GET', '/servers/1234')
|
||||
self.assertEqual('sample-server', server.name)
|
||||
|
||||
self.assertRaises(exceptions.NoUniqueMatch, self.cs.servers.find,
|
||||
flavor={"id": 1, "name": "256 MB Server"})
|
||||
|
||||
sl = self.cs.servers.findall(flavor={"id": 1, "name": "256 MB Server"})
|
||||
self.assertEqual([1234, 5678, 9012], [s.id for s in sl])
|
||||
|
||||
def test_reboot_server(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.reboot()
|
||||
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 = self.cs.servers.get(1234)
|
||||
s.rebuild(image=1)
|
||||
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')
|
||||
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 = 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)
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
|
||||
# verify disk config param was used in the request:
|
||||
body = jsonutils.loads(self.requests.last_request.body)
|
||||
|
||||
d = body[operation]
|
||||
self.assertIn('OS-DCF:diskConfig', d)
|
||||
self.assertEqual(disk_config, d['OS-DCF:diskConfig'])
|
||||
|
||||
def test_rebuild_server_disk_config_auto(self):
|
||||
self._rebuild_resize_disk_config('AUTO')
|
||||
|
||||
def test_rebuild_server_disk_config_manual(self):
|
||||
self._rebuild_resize_disk_config('MANUAL')
|
||||
|
||||
def test_rebuild_server_preserve_ephemeral(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.rebuild(image=1, preserve_ephemeral=True)
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
body = jsonutils.loads(self.requests.last_request.body)
|
||||
d = body['rebuild']
|
||||
self.assertIn('preserve_ephemeral', d)
|
||||
self.assertTrue(d['preserve_ephemeral'])
|
||||
|
||||
def test_rebuild_server_name_meta_files(self):
|
||||
files = {'/etc/passwd': 'some data'}
|
||||
s = self.cs.servers.get(1234)
|
||||
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 = self.cs.servers.get(1234)
|
||||
s.resize(flavor=1)
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
self.cs.servers.resize(s, flavor=1)
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
|
||||
def test_resize_server_disk_config_auto(self):
|
||||
self._rebuild_resize_disk_config('AUTO', 'resize')
|
||||
|
||||
def test_resize_server_disk_config_manual(self):
|
||||
self._rebuild_resize_disk_config('MANUAL', 'resize')
|
||||
|
||||
def test_confirm_resized_server(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.confirm_resize()
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
self.cs.servers.confirm_resize(s)
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
|
||||
def test_revert_resized_server(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.revert_resize()
|
||||
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 = self.cs.servers.get(1234)
|
||||
s.migrate()
|
||||
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 = self.cs.servers.get(1234)
|
||||
s.add_fixed_ip(1)
|
||||
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 = self.cs.servers.get(1234)
|
||||
s.remove_fixed_ip('10.0.0.1')
|
||||
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 = self.cs.servers.get(1234)
|
||||
s.add_floating_ip('11.0.0.1')
|
||||
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)
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
|
||||
def test_add_floating_ip_to_fixed(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.add_floating_ip('11.0.0.1', fixed_address='12.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')
|
||||
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)
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
|
||||
def test_remove_floating_ip(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.remove_floating_ip('11.0.0.1')
|
||||
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)
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
|
||||
def test_stop(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.stop()
|
||||
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 = self.cs.servers.get(1234)
|
||||
s.force_delete()
|
||||
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 = self.cs.servers.get(1234)
|
||||
s.restore()
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
self.cs.servers.restore(s)
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
|
||||
def test_start(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.start()
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
self.cs.servers.start(s)
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
|
||||
def test_rescue(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.rescue()
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
self.cs.servers.rescue(s)
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
|
||||
def test_rescue_password(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.rescue(password='asdf')
|
||||
self.assert_called('POST', '/servers/1234/action',
|
||||
{'rescue': {'adminPass': 'asdf'}})
|
||||
self.cs.servers.rescue(s, password='asdf')
|
||||
self.assert_called('POST', '/servers/1234/action',
|
||||
{'rescue': {'adminPass': 'asdf'}})
|
||||
|
||||
def test_rescue_image(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.rescue(image=1)
|
||||
self.assert_called('POST', '/servers/1234/action',
|
||||
{'rescue': {'rescue_image_ref': 1}})
|
||||
self.cs.servers.rescue(s, image=1)
|
||||
self.assert_called('POST', '/servers/1234/action',
|
||||
{'rescue': {'rescue_image_ref': 1}})
|
||||
|
||||
def test_unrescue(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.unrescue()
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
self.cs.servers.unrescue(s)
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
|
||||
def test_lock(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.lock()
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
self.cs.servers.lock(s)
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
|
||||
def test_unlock(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.unlock()
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
self.cs.servers.unlock(s)
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
|
||||
def test_backup(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.backup('back1', 'daily', 1)
|
||||
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 = self.cs.servers.get(1234)
|
||||
s.get_console_output()
|
||||
self.assertEqual(success, s.get_console_output())
|
||||
self.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 = self.cs.servers.get(1234)
|
||||
s.get_console_output(length=50)
|
||||
self.assertEqual(success, s.get_console_output(length=50))
|
||||
self.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
|
||||
#
|
||||
# Clear password: FooBar123
|
||||
#
|
||||
# RSA Private Key: novaclient/tests/unit/idfake.pem
|
||||
#
|
||||
# Encrypted password
|
||||
# OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r
|
||||
# qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho
|
||||
# QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw
|
||||
# /y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N
|
||||
# tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk
|
||||
# Hi/fmZZNQQqj1Ijq0caOIw==
|
||||
|
||||
def test_get_password(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
self.assertEqual(b'FooBar123',
|
||||
s.get_password('novaclient/tests/unit/idfake.pem'))
|
||||
self.assert_called('GET', '/servers/1234/os-server-password')
|
||||
|
||||
def test_get_password_without_key(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
self.assertEqual(
|
||||
'OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r'
|
||||
'qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho'
|
||||
'QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw'
|
||||
'/y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N'
|
||||
'tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk'
|
||||
'Hi/fmZZNQQqj1Ijq0caOIw==', s.get_password())
|
||||
self.assert_called('GET', '/servers/1234/os-server-password')
|
||||
|
||||
def test_clear_password(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.clear_password()
|
||||
self.assert_called('DELETE', '/servers/1234/os-server-password')
|
||||
|
||||
def test_get_server_diagnostics(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
diagnostics = s.diagnostics()
|
||||
self.assertIsNotNone(diagnostics)
|
||||
self.assert_called('GET', '/servers/1234/diagnostics')
|
||||
|
||||
diagnostics_from_manager = self.cs.servers.diagnostics(1234)
|
||||
self.assertIsNotNone(diagnostics_from_manager)
|
||||
self.assert_called('GET', '/servers/1234/diagnostics')
|
||||
|
||||
self.assertEqual(diagnostics[1], diagnostics_from_manager[1])
|
||||
|
||||
def test_get_vnc_console(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.get_vnc_console('fake')
|
||||
self.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 = self.cs.servers.get(1234)
|
||||
s.get_spice_console('fake')
|
||||
self.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 = self.cs.servers.get(1234)
|
||||
s.get_rdp_console('fake')
|
||||
self.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 = self.cs.servers.get(1234)
|
||||
s.create_image('123')
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
s.create_image('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 = self.cs.servers.get(1234)
|
||||
s.live_migrate(host='hostname', block_migration=False,
|
||||
disk_over_commit=False)
|
||||
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 = self.cs.servers.get(1234)
|
||||
s.reset_state('newstate')
|
||||
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 = self.cs.servers.get(1234)
|
||||
s.reset_network()
|
||||
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 = self.cs.servers.get(1234)
|
||||
s.add_security_group('newsg')
|
||||
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 = self.cs.servers.get(1234)
|
||||
s.remove_security_group('oldsg')
|
||||
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 = self.cs.servers.get(1234)
|
||||
s.list_security_group()
|
||||
self.assert_called('GET', '/servers/1234/os-security-groups')
|
||||
|
||||
def test_evacuate(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.evacuate('fake_target_host', 'True')
|
||||
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 = self.cs.servers.get(1234)
|
||||
s.interface_list()
|
||||
self.assert_called('GET', '/servers/1234/os-interface')
|
||||
|
||||
def test_interface_list_result_string_representable(self):
|
||||
"""Test for bugs.launchpad.net/python-novaclient/+bug/1280453."""
|
||||
# According to https://github.com/openstack/nova/blob/master/
|
||||
# nova/api/openstack/compute/contrib/attach_interfaces.py#L33,
|
||||
# the attach_interface extension get method will return a json
|
||||
# object partly like this:
|
||||
interface_list = [{
|
||||
'net_id': 'd7745cf5-63f9-4883-b0ae-983f061e4f23',
|
||||
'port_id': 'f35079da-36d5-4513-8ec1-0298d703f70e',
|
||||
'mac_addr': 'fa:16:3e:4c:37:c8',
|
||||
'port_state': 'ACTIVE',
|
||||
'fixed_ips': [
|
||||
{
|
||||
'subnet_id': 'f1ad93ad-2967-46ba-b403-e8cbbe65f7fa',
|
||||
'ip_address': '10.2.0.96'
|
||||
}]
|
||||
}]
|
||||
# If server is not string representable, it will raise an exception,
|
||||
# because attribute named 'name' cannot be found.
|
||||
# Parameter 'loaded' must be True or it will try to get attribute
|
||||
# 'id' then fails (lazy load detail), this is exactly same as
|
||||
# novaclient.base.Manager._list()
|
||||
s = servers.Server(servers.ServerManager, interface_list[0],
|
||||
loaded=True)
|
||||
# Trigger the __repr__ magic method
|
||||
self.assertEqual('<Server: unknown-name>', '%r' % s)
|
||||
|
||||
def test_interface_attach(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.interface_attach(None, None, None)
|
||||
self.assert_called('POST', '/servers/1234/os-interface')
|
||||
|
||||
def test_interface_detach(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
s.interface_detach('port-id')
|
||||
self.assert_called('DELETE', '/servers/1234/os-interface/port-id')
|
||||
@ -0,0 +1,99 @@
|
||||
# Copyright 2012 IBM Corp.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.tests.unit.v2 import fakes
|
||||
from novaclient.v2 import services
|
||||
|
||||
|
||||
class ServicesTest(utils.TestCase):
|
||||
def setUp(self):
|
||||
super(ServicesTest, self).setUp()
|
||||
self.cs = self._get_fake_client()
|
||||
self.service_type = self._get_service_type()
|
||||
|
||||
def _get_fake_client(self):
|
||||
return fakes.FakeClient()
|
||||
|
||||
def _get_service_type(self):
|
||||
return services.Service
|
||||
|
||||
def test_list_services(self):
|
||||
svs = self.cs.services.list()
|
||||
self.cs.assert_called('GET', '/os-services')
|
||||
for s in svs:
|
||||
self.assertIsInstance(s, self._get_service_type())
|
||||
self.assertEqual('nova-compute', s.binary)
|
||||
self.assertEqual('host1', s.host)
|
||||
self.assertTrue(str(s).startswith('<Service: '))
|
||||
|
||||
def test_list_services_with_hostname(self):
|
||||
svs = self.cs.services.list(host='host2')
|
||||
self.cs.assert_called('GET', '/os-services?host=host2')
|
||||
for s in svs:
|
||||
self.assertIsInstance(s, self._get_service_type())
|
||||
self.assertEqual('nova-compute', s.binary)
|
||||
self.assertEqual('host2', s.host)
|
||||
|
||||
def test_list_services_with_binary(self):
|
||||
svs = self.cs.services.list(binary='nova-cert')
|
||||
self.cs.assert_called('GET', '/os-services?binary=nova-cert')
|
||||
for s in svs:
|
||||
self.assertIsInstance(s, self._get_service_type())
|
||||
self.assertEqual('nova-cert', s.binary)
|
||||
self.assertEqual('host1', s.host)
|
||||
|
||||
def test_list_services_with_host_binary(self):
|
||||
svs = self.cs.services.list(host='host2', binary='nova-cert')
|
||||
self.cs.assert_called('GET',
|
||||
'/os-services?host=host2&binary=nova-cert')
|
||||
for s in svs:
|
||||
self.assertIsInstance(s, self._get_service_type())
|
||||
self.assertEqual('nova-cert', s.binary)
|
||||
self.assertEqual('host2', s.host)
|
||||
|
||||
def _update_body(self, host, binary, disabled_reason=None):
|
||||
body = {"host": host,
|
||||
"binary": binary}
|
||||
if disabled_reason is not None:
|
||||
body["disabled_reason"] = disabled_reason
|
||||
return body
|
||||
|
||||
def test_services_enable(self):
|
||||
service = self.cs.services.enable('host1', 'nova-cert')
|
||||
values = self._update_body("host1", "nova-cert")
|
||||
self.cs.assert_called('PUT', '/os-services/enable', values)
|
||||
self.assertIsInstance(service, self._get_service_type())
|
||||
self.assertEqual('enabled', service.status)
|
||||
|
||||
def test_services_delete(self):
|
||||
self.cs.services.delete('1')
|
||||
self.cs.assert_called('DELETE', '/os-services/1')
|
||||
|
||||
def test_services_disable(self):
|
||||
service = self.cs.services.disable('host1', 'nova-cert')
|
||||
values = self._update_body("host1", "nova-cert")
|
||||
self.cs.assert_called('PUT', '/os-services/disable', values)
|
||||
self.assertIsInstance(service, self._get_service_type())
|
||||
self.assertEqual('disabled', service.status)
|
||||
|
||||
def test_services_disable_log_reason(self):
|
||||
service = self.cs.services.disable_log_reason(
|
||||
'compute1', 'nova-compute', 'disable bad host')
|
||||
values = self._update_body("compute1", "nova-compute",
|
||||
"disable bad host")
|
||||
self.cs.assert_called('PUT', '/os-services/disable-log-reason', values)
|
||||
self.assertIsInstance(service, self._get_service_type())
|
||||
self.assertEqual('disabled', service.status)
|
||||
2473
awx/lib/site-packages/novaclient/tests/unit/v2/test_shell.py
Normal file
2473
awx/lib/site-packages/novaclient/tests/unit/v2/test_shell.py
Normal file
File diff suppressed because it is too large
Load Diff
57
awx/lib/site-packages/novaclient/tests/unit/v2/test_usage.py
Normal file
57
awx/lib/site-packages/novaclient/tests/unit/v2/test_usage.py
Normal file
@ -0,0 +1,57 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
|
||||
from novaclient.tests.unit import utils
|
||||
from novaclient.tests.unit.v2 import fakes
|
||||
from novaclient.v2 import usage
|
||||
|
||||
|
||||
class UsageTest(utils.TestCase):
|
||||
def setUp(self):
|
||||
super(UsageTest, self).setUp()
|
||||
self.cs = self._get_fake_client()
|
||||
self.usage_type = self._get_usage_type()
|
||||
|
||||
def _get_fake_client(self):
|
||||
return fakes.FakeClient()
|
||||
|
||||
def _get_usage_type(self):
|
||||
return usage.Usage
|
||||
|
||||
def test_usage_list(self, detailed=False):
|
||||
now = datetime.datetime.now()
|
||||
usages = self.cs.usage.list(now, now, detailed)
|
||||
|
||||
self.cs.assert_called(
|
||||
'GET',
|
||||
"/os-simple-tenant-usage?" +
|
||||
("start=%s&" % now.isoformat()) +
|
||||
("end=%s&" % now.isoformat()) +
|
||||
("detailed=%s" % int(bool(detailed))))
|
||||
[self.assertIsInstance(u, usage.Usage) for u in usages]
|
||||
|
||||
def test_usage_list_detailed(self):
|
||||
self.test_usage_list(True)
|
||||
|
||||
def test_usage_get(self):
|
||||
now = datetime.datetime.now()
|
||||
u = self.cs.usage.get("tenantfoo", now, now)
|
||||
|
||||
self.cs.assert_called(
|
||||
'GET',
|
||||
"/os-simple-tenant-usage/tenantfoo?" +
|
||||
("start=%s&" % now.isoformat()) +
|
||||
("end=%s" % now.isoformat()))
|
||||
self.assertIsInstance(u, usage.Usage)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user