mirror of
https://github.com/ansible/awx.git
synced 2026-01-16 12:20:45 -03:30
Upgrade novaclient to 2.18.1
This commit is contained in:
parent
7cf1df23ab
commit
07bfe7c275
@ -45,7 +45,7 @@ pip==1.5.4 (pip/*, excluded bin/pip*)
|
||||
prettytable==0.7.2 (prettytable.py)
|
||||
pyrax==1.9.0 (pyrax/*)
|
||||
python-dateutil==2.2 (dateutil/*)
|
||||
python-novaclient==2.17.0 (novaclient/*, excluded bin/nova)
|
||||
python-novaclient==2.18.1 (novaclient/*, excluded bin/nova)
|
||||
python-swiftclient==2.0.3 (swiftclient/*, excluded bin/swift)
|
||||
pytz==2014.4 (pytz/*)
|
||||
rackspace-auth-openstack==1.3 (rackspace_auth_openstack/*)
|
||||
|
||||
@ -20,7 +20,6 @@ import pkg_resources
|
||||
import six
|
||||
|
||||
from novaclient import exceptions
|
||||
from novaclient.openstack.common.gettextutils import _
|
||||
from novaclient import utils
|
||||
|
||||
|
||||
@ -40,7 +39,7 @@ def discover_auth_systems():
|
||||
try:
|
||||
auth_plugin = ep.load()
|
||||
except (ImportError, pkg_resources.UnknownExtra, AttributeError) as e:
|
||||
logger.debug(_("ERROR: Cannot load auth plugin %s") % ep.name)
|
||||
logger.debug("ERROR: Cannot load auth plugin %s" % ep.name)
|
||||
logger.debug(e, exc_info=1)
|
||||
else:
|
||||
_discovered_plugins[ep.name] = auth_plugin
|
||||
|
||||
@ -20,11 +20,7 @@ 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
|
||||
|
||||
@ -52,11 +48,18 @@ class Manager(utils.HookableMixin):
|
||||
etc.) and provide CRUD operations for them.
|
||||
"""
|
||||
resource_class = None
|
||||
cache_lock = threading.RLock()
|
||||
|
||||
def __init__(self, api):
|
||||
self.api = api
|
||||
|
||||
def _write_object_to_completion_cache(self, obj):
|
||||
if hasattr(self.api, 'write_object_to_completion_cache'):
|
||||
self.api.write_object_to_completion_cache(obj)
|
||||
|
||||
def _clear_completion_cache_for_class(self, obj_class):
|
||||
if hasattr(self.api, 'clear_completion_cache_for_class'):
|
||||
self.api.clear_completion_cache_for_class(obj_class)
|
||||
|
||||
def _list(self, url, response_key, obj_class=None, body=None):
|
||||
if body:
|
||||
_resp, body = self.api.client.post(url, body=body)
|
||||
@ -75,77 +78,22 @@ class Manager(utils.HookableMixin):
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
with self.completion_cache('human_id', obj_class, mode="w"):
|
||||
with self.completion_cache('uuid', obj_class, mode="w"):
|
||||
return [obj_class(self, res, loaded=True)
|
||||
for res in data if res]
|
||||
self._clear_completion_cache_for_class(obj_class)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def 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.
|
||||
objs = []
|
||||
for res in data:
|
||||
if res:
|
||||
obj = obj_class(self, res, loaded=True)
|
||||
self._write_object_to_completion_cache(obj)
|
||||
objs.append(obj)
|
||||
|
||||
A resource listing will clear and repopulate the cache.
|
||||
|
||||
A resource create will append to the cache.
|
||||
|
||||
Delete is not handled because listings are assumed to be performed
|
||||
often enough to keep the cache reasonably up-to-date.
|
||||
"""
|
||||
# NOTE(wryan): This lock protects read and write access to the
|
||||
# completion caches
|
||||
with self.cache_lock:
|
||||
base_dir = utils.env('NOVACLIENT_UUID_CACHE_DIR',
|
||||
default="~/.novaclient")
|
||||
|
||||
# NOTE(sirp): Keep separate UUID caches for each username +
|
||||
# endpoint pair
|
||||
username = utils.env('OS_USERNAME', 'NOVA_USERNAME')
|
||||
url = utils.env('OS_URL', 'NOVA_URL')
|
||||
uniqifier = hashlib.md5(username.encode('utf-8') +
|
||||
url.encode('utf-8')).hexdigest()
|
||||
|
||||
cache_dir = os.path.expanduser(os.path.join(base_dir, uniqifier))
|
||||
|
||||
try:
|
||||
os.makedirs(cache_dir, 0o755)
|
||||
except OSError:
|
||||
# NOTE(kiall): This is typically either permission denied while
|
||||
# attempting to create the directory, or the
|
||||
# directory already exists. Either way, don't
|
||||
# fail.
|
||||
pass
|
||||
|
||||
resource = obj_class.__name__.lower()
|
||||
filename = "%s-%s-cache" % (resource, cache_type.replace('_', '-'))
|
||||
path = os.path.join(cache_dir, filename)
|
||||
|
||||
cache_attr = "_%s_cache" % cache_type
|
||||
|
||||
try:
|
||||
setattr(self, cache_attr, open(path, mode))
|
||||
except IOError:
|
||||
# NOTE(kiall): This is typically a permission denied while
|
||||
# attempting to write the cache file.
|
||||
pass
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
cache = getattr(self, cache_attr, None)
|
||||
if cache:
|
||||
cache.close()
|
||||
delattr(self, cache_attr)
|
||||
|
||||
def write_to_completion_cache(self, cache_type, val):
|
||||
cache = getattr(self, "_%s_cache" % cache_type, None)
|
||||
if cache:
|
||||
cache.write("%s\n" % val)
|
||||
return objs
|
||||
|
||||
def _get(self, url, response_key):
|
||||
_resp, body = self.api.client.get(url)
|
||||
return self.resource_class(self, body[response_key], loaded=True)
|
||||
obj = self.resource_class(self, body[response_key], loaded=True)
|
||||
self._write_object_to_completion_cache(obj)
|
||||
return obj
|
||||
|
||||
def _create(self, url, body, response_key, return_raw=False, **kwargs):
|
||||
self.run_hooks('modify_body_for_create', body, **kwargs)
|
||||
@ -153,9 +101,9 @@ class Manager(utils.HookableMixin):
|
||||
if return_raw:
|
||||
return body[response_key]
|
||||
|
||||
with self.completion_cache('human_id', self.resource_class, mode="a"):
|
||||
with self.completion_cache('uuid', self.resource_class, mode="a"):
|
||||
return self.resource_class(self, body[response_key])
|
||||
obj = self.resource_class(self, body[response_key])
|
||||
self._write_object_to_completion_cache(obj)
|
||||
return obj
|
||||
|
||||
def _delete(self, url):
|
||||
_resp, _body = self.api.client.delete(url)
|
||||
|
||||
@ -20,7 +20,12 @@
|
||||
OpenStack Client interface. Handles the REST calls and responses.
|
||||
"""
|
||||
|
||||
import errno
|
||||
import functools
|
||||
import glob
|
||||
import hashlib
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
|
||||
import requests
|
||||
@ -35,21 +40,177 @@ 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 import service_catalog
|
||||
from novaclient import utils
|
||||
|
||||
|
||||
_ADAPTERS = {}
|
||||
SENSITIVE_HEADERS = ('X-Auth-Token',)
|
||||
|
||||
|
||||
def _adapter_pool(url):
|
||||
class _ClientConnectionPool(object):
|
||||
|
||||
def __init__(self):
|
||||
self._adapters = {}
|
||||
|
||||
def get(self, url):
|
||||
"""
|
||||
Store and reuse HTTP adapters per Service URL.
|
||||
"""
|
||||
if url not in self._adapters:
|
||||
self._adapters[url] = adapters.HTTPAdapter()
|
||||
|
||||
return self._adapters[url]
|
||||
|
||||
|
||||
class CompletionCache(object):
|
||||
"""The completion cache is how we support tab-completion with novaclient.
|
||||
|
||||
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
|
||||
"""
|
||||
Store and reuse HTTP adapters per Service URL.
|
||||
"""
|
||||
if url not in _ADAPTERS:
|
||||
_ADAPTERS[url] = adapters.HTTPAdapter()
|
||||
def __init__(self, username, auth_url, attributes=('id', 'human_id')):
|
||||
self.directory = self._make_directory_name(username, auth_url)
|
||||
self.attributes = attributes
|
||||
|
||||
return _ADAPTERS[url]
|
||||
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 request(self, url, method, **kwargs):
|
||||
kwargs.setdefault('user_agent', 'python-novaclient')
|
||||
kwargs.setdefault('auth', self.auth)
|
||||
kwargs.setdefault('authenticated', False)
|
||||
|
||||
try:
|
||||
kwargs['json'] = kwargs.pop('body')
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
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:
|
||||
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(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 _original_only(f):
|
||||
"""Indicates and enforces that this function can only be used if we are
|
||||
using the original HTTPClient object.
|
||||
|
||||
We use this to specify that if you use the newer Session HTTP client then
|
||||
you are aware that the way you use your client has been updated and certain
|
||||
functions are no longer allowed to be used.
|
||||
"""
|
||||
@functools.wraps(f)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
if isinstance(self.client, SessionClient):
|
||||
msg = ('This call is no longer available. The operation should '
|
||||
'be performed on the session object instead.')
|
||||
raise exceptions.InvalidUsage(msg)
|
||||
|
||||
return f(self, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
class HTTPClient(object):
|
||||
@ -64,12 +225,17 @@ class HTTPClient(object):
|
||||
os_cache=False, no_cache=True,
|
||||
http_log_debug=False, auth_system='keystone',
|
||||
auth_plugin=None, auth_token=None,
|
||||
cacert=None, tenant_id=None):
|
||||
cacert=None, tenant_id=None, user_id=None,
|
||||
connection_pool=False):
|
||||
self.user = user
|
||||
self.user_id = user_id
|
||||
self.password = password
|
||||
self.projectid = projectid
|
||||
self.tenant_id = tenant_id
|
||||
|
||||
self._connection_pool = (_ClientConnectionPool()
|
||||
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
|
||||
# token is available, you don't want to prompt until the token has
|
||||
@ -83,7 +249,7 @@ class HTTPClient(object):
|
||||
auth_url = auth_plugin.get_auth_url()
|
||||
if not auth_url:
|
||||
raise exceptions.EndpointNotFound()
|
||||
self.auth_url = auth_url.rstrip('/')
|
||||
self.auth_url = auth_url.rstrip('/') if auth_url else auth_url
|
||||
self.version = 'v1.1'
|
||||
self.region_name = region_name
|
||||
self.endpoint_type = endpoint_type
|
||||
@ -91,7 +257,7 @@ class HTTPClient(object):
|
||||
self.service_name = service_name
|
||||
self.volume_service_name = volume_service_name
|
||||
self.timings = timings
|
||||
self.bypass_url = bypass_url
|
||||
self.bypass_url = bypass_url.rstrip('/') if bypass_url else bypass_url
|
||||
self.os_cache = os_cache or not no_cache
|
||||
self.http_log_debug = http_log_debug
|
||||
if timeout is not None:
|
||||
@ -118,8 +284,8 @@ class HTTPClient(object):
|
||||
|
||||
self.auth_system = auth_system
|
||||
self.auth_plugin = auth_plugin
|
||||
self._session = None
|
||||
self._current_url = None
|
||||
self._http = None
|
||||
self._logger = logging.getLogger(__name__)
|
||||
|
||||
if self.http_log_debug and not self._logger.handlers:
|
||||
@ -152,6 +318,16 @@ 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 http_log_req(self, method, url, kwargs):
|
||||
if not self.http_log_debug:
|
||||
return
|
||||
@ -164,35 +340,52 @@ class HTTPClient(object):
|
||||
string_parts.append(" '%s'" % url)
|
||||
string_parts.append(' -X %s' % method)
|
||||
|
||||
for element in kwargs['headers']:
|
||||
header = ' -H "%s: %s"' % (element, kwargs['headers'][element])
|
||||
# because dict ordering changes from 2 to 3
|
||||
keys = sorted(kwargs['headers'].keys())
|
||||
for name in keys:
|
||||
value = kwargs['headers'][name]
|
||||
header = ' -H "%s: %s"' % self.safe_header(name, value)
|
||||
string_parts.append(header)
|
||||
|
||||
if 'data' in kwargs:
|
||||
string_parts.append(" -d '%s'" % (kwargs['data']))
|
||||
self._logger.debug("\nREQ: %s\n" % "".join(string_parts))
|
||||
self._logger.debug("REQ: %s" % "".join(string_parts))
|
||||
|
||||
def http_log_resp(self, resp):
|
||||
if not self.http_log_debug:
|
||||
return
|
||||
self._logger.debug(_("RESP: [%(status)s] %(headers)s\nRESP BODY: "
|
||||
"%(text)s\n"), {'status': resp.status_code,
|
||||
'headers': resp.headers,
|
||||
'text': resp.text})
|
||||
self._logger.debug("RESP: [%(status)s] %(headers)s\nRESP BODY: "
|
||||
"%(text)s\n", {'status': resp.status_code,
|
||||
'headers': resp.headers,
|
||||
'text': resp.text})
|
||||
|
||||
def http(self, url):
|
||||
magic_tuple = parse.urlsplit(url)
|
||||
scheme, netloc, path, query, frag = magic_tuple
|
||||
service_url = '%s://%s' % (scheme, netloc)
|
||||
if self._current_url != service_url:
|
||||
# Invalidate Session object in case the url is somehow changed
|
||||
if self._http:
|
||||
self._http.close()
|
||||
self._current_url = service_url
|
||||
self._logger.debug("New session created for: (%s)" % service_url)
|
||||
self._http = requests.Session()
|
||||
self._http.mount(service_url, _adapter_pool(service_url))
|
||||
return self._http
|
||||
def open_session(self):
|
||||
if not self._connection_pool:
|
||||
self._session = requests.Session()
|
||||
|
||||
def close_session(self):
|
||||
if self._session and not self._connection_pool:
|
||||
self._session.close()
|
||||
self._session = None
|
||||
|
||||
def _get_session(self, url):
|
||||
if self._connection_pool:
|
||||
magic_tuple = parse.urlsplit(url)
|
||||
scheme, netloc, path, query, frag = magic_tuple
|
||||
service_url = '%s://%s' % (scheme, netloc)
|
||||
if self._current_url != service_url:
|
||||
# Invalidate Session object in case the url is somehow changed
|
||||
if self._session:
|
||||
self._session.close()
|
||||
self._current_url = service_url
|
||||
self._logger.debug(
|
||||
"New session created for: (%s)" % service_url)
|
||||
self._session = requests.Session()
|
||||
self._session.mount(service_url,
|
||||
self._connection_pool.get(service_url))
|
||||
return self._session
|
||||
elif self._session:
|
||||
return self._session
|
||||
|
||||
def request(self, url, method, **kwargs):
|
||||
kwargs.setdefault('headers', kwargs.get('headers', {}))
|
||||
@ -207,7 +400,13 @@ class HTTPClient(object):
|
||||
kwargs['verify'] = self.verify_cert
|
||||
|
||||
self.http_log_req(method, url, kwargs)
|
||||
resp = self.http(url).request(
|
||||
|
||||
request_func = requests.request
|
||||
session = self._get_session(url)
|
||||
if session:
|
||||
request_func = session.request
|
||||
|
||||
resp = request_func(
|
||||
method,
|
||||
url,
|
||||
**kwargs)
|
||||
@ -347,14 +546,14 @@ class HTTPClient(object):
|
||||
# GET ...:5001/v2.0/tokens/#####/endpoints
|
||||
url = '/'.join([url, 'tokens', '%s?belongsTo=%s'
|
||||
% (self.proxy_token, self.proxy_tenant_id)])
|
||||
self._logger.debug(_("Using Endpoint URL: %s") % url)
|
||||
self._logger.debug("Using Endpoint URL: %s" % url)
|
||||
resp, body = self._time_request(
|
||||
url, "GET", headers={'X-Auth-Token': self.auth_token})
|
||||
return self._extract_service_catalog(url, resp, body,
|
||||
extract_token=False)
|
||||
|
||||
def authenticate(self):
|
||||
magic_tuple = parse.urlsplit(self.auth_url)
|
||||
magic_tuple = network_utils.urlsplit(self.auth_url)
|
||||
scheme, netloc, path, query, frag = magic_tuple
|
||||
port = magic_tuple.port
|
||||
if port is None:
|
||||
@ -456,6 +655,10 @@ class HTTPClient(object):
|
||||
if self.auth_token:
|
||||
body = {"auth": {
|
||||
"token": {"id": self.auth_token}}}
|
||||
elif self.user_id:
|
||||
body = {"auth": {
|
||||
"passwordCredentials": {"userId": self.user_id,
|
||||
"password": self._get_password()}}}
|
||||
else:
|
||||
body = {"auth": {
|
||||
"passwordCredentials": {"username": self.user,
|
||||
@ -484,6 +687,53 @@ class HTTPClient(object):
|
||||
return self._extract_service_catalog(url, resp, respbody)
|
||||
|
||||
|
||||
def _construct_http_client(username=None, password=None, project_id=None,
|
||||
auth_url=None, insecure=False, timeout=None,
|
||||
proxy_tenant_id=None, proxy_token=None,
|
||||
region_name=None, endpoint_type='publicURL',
|
||||
extensions=None, service_type='compute',
|
||||
service_name=None, volume_service_name=None,
|
||||
timings=False, bypass_url=None, os_cache=False,
|
||||
no_cache=True, http_log_debug=False,
|
||||
auth_system='keystone', auth_plugin=None,
|
||||
auth_token=None, cacert=None, tenant_id=None,
|
||||
user_id=None, connection_pool=False, session=None,
|
||||
auth=None):
|
||||
if session:
|
||||
return SessionClient(session=session,
|
||||
auth=auth,
|
||||
interface=endpoint_type,
|
||||
service_type=service_type,
|
||||
region_name=region_name)
|
||||
else:
|
||||
# FIXME(jamielennox): username and password are now optional. Need
|
||||
# to test that they were provided in this mode.
|
||||
return HTTPClient(username,
|
||||
password,
|
||||
user_id=user_id,
|
||||
projectid=project_id,
|
||||
tenant_id=tenant_id,
|
||||
auth_url=auth_url,
|
||||
auth_token=auth_token,
|
||||
insecure=insecure,
|
||||
timeout=timeout,
|
||||
auth_system=auth_system,
|
||||
auth_plugin=auth_plugin,
|
||||
proxy_token=proxy_token,
|
||||
proxy_tenant_id=proxy_tenant_id,
|
||||
region_name=region_name,
|
||||
endpoint_type=endpoint_type,
|
||||
service_type=service_type,
|
||||
service_name=service_name,
|
||||
volume_service_name=volume_service_name,
|
||||
timings=timings,
|
||||
bypass_url=bypass_url,
|
||||
os_cache=os_cache,
|
||||
http_log_debug=http_log_debug,
|
||||
cacert=cacert,
|
||||
connection_pool=connection_pool)
|
||||
|
||||
|
||||
def get_client_class(version):
|
||||
version_map = {
|
||||
'1.1': 'novaclient.v1_1.client.Client',
|
||||
|
||||
@ -77,10 +77,17 @@ class ConnectionRefused(Exception):
|
||||
return "ConnectionRefused: %s" % repr(self.response)
|
||||
|
||||
|
||||
class InstanceInErrorState(Exception):
|
||||
"""Instance is in the error state."""
|
||||
pass
|
||||
|
||||
|
||||
class ClientException(Exception):
|
||||
"""
|
||||
The base exception class for all exceptions this library raises.
|
||||
"""
|
||||
message = 'Unknown Error'
|
||||
|
||||
def __init__(self, code, message=None, details=None, request_id=None,
|
||||
url=None, method=None):
|
||||
self.code = code
|
||||
@ -192,6 +199,16 @@ _error_classes = [BadRequest, Unauthorized, Forbidden, NotFound,
|
||||
_code_map = dict((c.http_status, c) for c in _error_classes)
|
||||
|
||||
|
||||
class InvalidUsage(RuntimeError):
|
||||
"""This function call is invalid in the way you are using this client.
|
||||
|
||||
Due to the transition to using keystoneclient some function calls are no
|
||||
longer available. You should make a similar call to the session object
|
||||
instead.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
def from_response(response, body, url, method=None):
|
||||
"""
|
||||
Return an instance of an ClientException or subclass
|
||||
|
||||
@ -1,2 +1,17 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import six
|
||||
|
||||
|
||||
six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox'))
|
||||
|
||||
@ -213,8 +213,8 @@ class BaseAuthPlugin(object):
|
||||
:type service_type: string
|
||||
:param endpoint_type: Type of endpoint.
|
||||
Possible values: public or publicURL,
|
||||
internal or internalURL,
|
||||
admin or adminURL
|
||||
internal or internalURL,
|
||||
admin or adminURL
|
||||
:type endpoint_type: string
|
||||
:returns: tuple of token and endpoint strings
|
||||
:raises: EndpointException
|
||||
|
||||
@ -30,6 +30,7 @@ import six
|
||||
from six.moves.urllib import parse
|
||||
|
||||
from novaclient.openstack.common.apiclient import exceptions
|
||||
from novaclient.openstack.common.gettextutils import _
|
||||
from novaclient.openstack.common import strutils
|
||||
|
||||
|
||||
@ -74,8 +75,8 @@ class HookableMixin(object):
|
||||
|
||||
:param cls: class that registers hooks
|
||||
:param hook_type: hook type, e.g., '__pre_parse_args__'
|
||||
:param **args: args to be passed to every hook function
|
||||
:param **kwargs: kwargs to be passed to every hook function
|
||||
:param args: args to be passed to every hook function
|
||||
:param kwargs: kwargs to be passed to every hook function
|
||||
"""
|
||||
hook_funcs = cls._hooks_map.get(hook_type) or []
|
||||
for hook_func in hook_funcs:
|
||||
@ -219,7 +220,10 @@ class ManagerWithFind(BaseManager):
|
||||
matches = self.findall(**kwargs)
|
||||
num_matches = len(matches)
|
||||
if num_matches == 0:
|
||||
msg = "No %s matching %s." % (self.resource_class.__name__, kwargs)
|
||||
msg = _("No %(name)s matching %(args)s.") % {
|
||||
'name': self.resource_class.__name__,
|
||||
'args': kwargs
|
||||
}
|
||||
raise exceptions.NotFound(msg)
|
||||
elif num_matches > 1:
|
||||
raise exceptions.NoUniqueMatch()
|
||||
@ -373,7 +377,10 @@ class CrudManager(BaseManager):
|
||||
num = len(rl)
|
||||
|
||||
if num == 0:
|
||||
msg = "No %s matching %s." % (self.resource_class.__name__, kwargs)
|
||||
msg = _("No %(name)s matching %(args)s.") % {
|
||||
'name': self.resource_class.__name__,
|
||||
'args': kwargs
|
||||
}
|
||||
raise exceptions.NotFound(404, msg)
|
||||
elif num > 1:
|
||||
raise exceptions.NoUniqueMatch
|
||||
@ -441,8 +448,10 @@ class Resource(object):
|
||||
def human_id(self):
|
||||
"""Human-readable ID which can be used for bash completion.
|
||||
"""
|
||||
if self.NAME_ATTR in self.__dict__ and self.HUMAN_ID:
|
||||
return strutils.to_slug(getattr(self, self.NAME_ATTR))
|
||||
if self.HUMAN_ID:
|
||||
name = getattr(self, self.NAME_ATTR, None)
|
||||
if name is not None:
|
||||
return strutils.to_slug(name)
|
||||
return None
|
||||
|
||||
def _add_details(self, info):
|
||||
|
||||
@ -36,6 +36,7 @@ except ImportError:
|
||||
import requests
|
||||
|
||||
from novaclient.openstack.common.apiclient import exceptions
|
||||
from novaclient.openstack.common.gettextutils import _
|
||||
from novaclient.openstack.common import importutils
|
||||
|
||||
|
||||
@ -46,6 +47,7 @@ class HTTPClient(object):
|
||||
"""This client handles sending HTTP requests to OpenStack servers.
|
||||
|
||||
Features:
|
||||
|
||||
- share authentication information between several clients to different
|
||||
services (e.g., for compute and image clients);
|
||||
- reissue authentication request for expired tokens;
|
||||
@ -151,7 +153,7 @@ class HTTPClient(object):
|
||||
:param method: method of HTTP request
|
||||
:param url: URL of HTTP request
|
||||
:param kwargs: any other parameter that can be passed to
|
||||
' requests.Session.request (such as `headers`) or `json`
|
||||
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", {}))
|
||||
@ -206,7 +208,7 @@ class HTTPClient(object):
|
||||
:param method: method of HTTP request
|
||||
:param url: URL of HTTP request
|
||||
:param kwargs: any other parameter that can be passed to
|
||||
' `HTTPClient.request`
|
||||
`HTTPClient.request`
|
||||
"""
|
||||
|
||||
filter_args = {
|
||||
@ -228,7 +230,7 @@ class HTTPClient(object):
|
||||
**filter_args)
|
||||
if not (token and endpoint):
|
||||
raise exceptions.AuthorizationFailure(
|
||||
"Cannot find endpoint or token for request")
|
||||
_("Cannot find endpoint or token for request"))
|
||||
|
||||
old_token_endpoint = (token, endpoint)
|
||||
kwargs.setdefault("headers", {})["X-Auth-Token"] = token
|
||||
@ -351,8 +353,12 @@ class BaseClient(object):
|
||||
try:
|
||||
client_path = version_map[str(version)]
|
||||
except (KeyError, ValueError):
|
||||
msg = "Invalid %s client version '%s'. must be one of: %s" % (
|
||||
(api_name, version, ', '.join(version_map.keys())))
|
||||
msg = _("Invalid %(api_name)s client version '%(version)s'. "
|
||||
"Must be one of: %(version_map)s") % {
|
||||
'api_name': api_name,
|
||||
'version': version,
|
||||
'version_map': ', '.join(version_map.keys())
|
||||
}
|
||||
raise exceptions.UnsupportedVersion(msg)
|
||||
|
||||
return importutils.import_class(client_path)
|
||||
|
||||
@ -25,6 +25,8 @@ import sys
|
||||
|
||||
import six
|
||||
|
||||
from novaclient.openstack.common.gettextutils import _
|
||||
|
||||
|
||||
class ClientException(Exception):
|
||||
"""The base exception class for all exceptions this library raises.
|
||||
@ -36,7 +38,7 @@ class MissingArgs(ClientException):
|
||||
"""Supplied arguments are not sufficient for calling a function."""
|
||||
def __init__(self, missing):
|
||||
self.missing = missing
|
||||
msg = "Missing argument(s): %s" % ", ".join(missing)
|
||||
msg = _("Missing arguments: %s") % ", ".join(missing)
|
||||
super(MissingArgs, self).__init__(msg)
|
||||
|
||||
|
||||
@ -55,6 +57,11 @@ class CommandError(ClientException):
|
||||
pass
|
||||
|
||||
|
||||
class ResourceNotFound(ClientException):
|
||||
"""Error in getting the resource."""
|
||||
pass
|
||||
|
||||
|
||||
class AuthorizationFailure(ClientException):
|
||||
"""Cannot authorize API client."""
|
||||
pass
|
||||
@ -69,16 +76,16 @@ class AuthPluginOptionsMissing(AuthorizationFailure):
|
||||
"""Auth plugin misses some options."""
|
||||
def __init__(self, opt_names):
|
||||
super(AuthPluginOptionsMissing, self).__init__(
|
||||
"Authentication failed. Missing options: %s" %
|
||||
_("Authentication failed. Missing options: %s") %
|
||||
", ".join(opt_names))
|
||||
self.opt_names = opt_names
|
||||
|
||||
|
||||
class AuthSystemNotFound(AuthorizationFailure):
|
||||
"""User has specified a AuthSystem that is not installed."""
|
||||
"""User has specified an AuthSystem that is not installed."""
|
||||
def __init__(self, auth_system):
|
||||
super(AuthSystemNotFound, self).__init__(
|
||||
"AuthSystemNotFound: %s" % repr(auth_system))
|
||||
_("AuthSystemNotFound: %s") % repr(auth_system))
|
||||
self.auth_system = auth_system
|
||||
|
||||
|
||||
@ -101,7 +108,7 @@ class AmbiguousEndpoints(EndpointException):
|
||||
"""Found more than one matching endpoint in Service Catalog."""
|
||||
def __init__(self, endpoints=None):
|
||||
super(AmbiguousEndpoints, self).__init__(
|
||||
"AmbiguousEndpoints: %s" % repr(endpoints))
|
||||
_("AmbiguousEndpoints: %s") % repr(endpoints))
|
||||
self.endpoints = endpoints
|
||||
|
||||
|
||||
@ -109,7 +116,7 @@ class HttpError(ClientException):
|
||||
"""The base exception class for all HTTP exceptions.
|
||||
"""
|
||||
http_status = 0
|
||||
message = "HTTP Error"
|
||||
message = _("HTTP Error")
|
||||
|
||||
def __init__(self, message=None, details=None,
|
||||
response=None, request_id=None,
|
||||
@ -129,7 +136,7 @@ class HttpError(ClientException):
|
||||
|
||||
class HTTPRedirection(HttpError):
|
||||
"""HTTP Redirection."""
|
||||
message = "HTTP Redirection"
|
||||
message = _("HTTP Redirection")
|
||||
|
||||
|
||||
class HTTPClientError(HttpError):
|
||||
@ -137,7 +144,7 @@ class HTTPClientError(HttpError):
|
||||
|
||||
Exception for cases in which the client seems to have erred.
|
||||
"""
|
||||
message = "HTTP Client Error"
|
||||
message = _("HTTP Client Error")
|
||||
|
||||
|
||||
class HttpServerError(HttpError):
|
||||
@ -146,7 +153,7 @@ class HttpServerError(HttpError):
|
||||
Exception for cases in which the server is aware that it has
|
||||
erred or is incapable of performing the request.
|
||||
"""
|
||||
message = "HTTP Server Error"
|
||||
message = _("HTTP Server Error")
|
||||
|
||||
|
||||
class MultipleChoices(HTTPRedirection):
|
||||
@ -156,7 +163,7 @@ class MultipleChoices(HTTPRedirection):
|
||||
"""
|
||||
|
||||
http_status = 300
|
||||
message = "Multiple Choices"
|
||||
message = _("Multiple Choices")
|
||||
|
||||
|
||||
class BadRequest(HTTPClientError):
|
||||
@ -165,7 +172,7 @@ class BadRequest(HTTPClientError):
|
||||
The request cannot be fulfilled due to bad syntax.
|
||||
"""
|
||||
http_status = 400
|
||||
message = "Bad Request"
|
||||
message = _("Bad Request")
|
||||
|
||||
|
||||
class Unauthorized(HTTPClientError):
|
||||
@ -175,7 +182,7 @@ class Unauthorized(HTTPClientError):
|
||||
is required and has failed or has not yet been provided.
|
||||
"""
|
||||
http_status = 401
|
||||
message = "Unauthorized"
|
||||
message = _("Unauthorized")
|
||||
|
||||
|
||||
class PaymentRequired(HTTPClientError):
|
||||
@ -184,7 +191,7 @@ class PaymentRequired(HTTPClientError):
|
||||
Reserved for future use.
|
||||
"""
|
||||
http_status = 402
|
||||
message = "Payment Required"
|
||||
message = _("Payment Required")
|
||||
|
||||
|
||||
class Forbidden(HTTPClientError):
|
||||
@ -194,7 +201,7 @@ class Forbidden(HTTPClientError):
|
||||
to it.
|
||||
"""
|
||||
http_status = 403
|
||||
message = "Forbidden"
|
||||
message = _("Forbidden")
|
||||
|
||||
|
||||
class NotFound(HTTPClientError):
|
||||
@ -204,7 +211,7 @@ class NotFound(HTTPClientError):
|
||||
in the future.
|
||||
"""
|
||||
http_status = 404
|
||||
message = "Not Found"
|
||||
message = _("Not Found")
|
||||
|
||||
|
||||
class MethodNotAllowed(HTTPClientError):
|
||||
@ -214,7 +221,7 @@ class MethodNotAllowed(HTTPClientError):
|
||||
by that resource.
|
||||
"""
|
||||
http_status = 405
|
||||
message = "Method Not Allowed"
|
||||
message = _("Method Not Allowed")
|
||||
|
||||
|
||||
class NotAcceptable(HTTPClientError):
|
||||
@ -224,7 +231,7 @@ class NotAcceptable(HTTPClientError):
|
||||
acceptable according to the Accept headers sent in the request.
|
||||
"""
|
||||
http_status = 406
|
||||
message = "Not Acceptable"
|
||||
message = _("Not Acceptable")
|
||||
|
||||
|
||||
class ProxyAuthenticationRequired(HTTPClientError):
|
||||
@ -233,7 +240,7 @@ class ProxyAuthenticationRequired(HTTPClientError):
|
||||
The client must first authenticate itself with the proxy.
|
||||
"""
|
||||
http_status = 407
|
||||
message = "Proxy Authentication Required"
|
||||
message = _("Proxy Authentication Required")
|
||||
|
||||
|
||||
class RequestTimeout(HTTPClientError):
|
||||
@ -242,7 +249,7 @@ class RequestTimeout(HTTPClientError):
|
||||
The server timed out waiting for the request.
|
||||
"""
|
||||
http_status = 408
|
||||
message = "Request Timeout"
|
||||
message = _("Request Timeout")
|
||||
|
||||
|
||||
class Conflict(HTTPClientError):
|
||||
@ -252,7 +259,7 @@ class Conflict(HTTPClientError):
|
||||
in the request, such as an edit conflict.
|
||||
"""
|
||||
http_status = 409
|
||||
message = "Conflict"
|
||||
message = _("Conflict")
|
||||
|
||||
|
||||
class Gone(HTTPClientError):
|
||||
@ -262,7 +269,7 @@ class Gone(HTTPClientError):
|
||||
not be available again.
|
||||
"""
|
||||
http_status = 410
|
||||
message = "Gone"
|
||||
message = _("Gone")
|
||||
|
||||
|
||||
class LengthRequired(HTTPClientError):
|
||||
@ -272,7 +279,7 @@ class LengthRequired(HTTPClientError):
|
||||
required by the requested resource.
|
||||
"""
|
||||
http_status = 411
|
||||
message = "Length Required"
|
||||
message = _("Length Required")
|
||||
|
||||
|
||||
class PreconditionFailed(HTTPClientError):
|
||||
@ -282,7 +289,7 @@ class PreconditionFailed(HTTPClientError):
|
||||
put on the request.
|
||||
"""
|
||||
http_status = 412
|
||||
message = "Precondition Failed"
|
||||
message = _("Precondition Failed")
|
||||
|
||||
|
||||
class RequestEntityTooLarge(HTTPClientError):
|
||||
@ -291,7 +298,7 @@ class RequestEntityTooLarge(HTTPClientError):
|
||||
The request is larger than the server is willing or able to process.
|
||||
"""
|
||||
http_status = 413
|
||||
message = "Request Entity Too Large"
|
||||
message = _("Request Entity Too Large")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
try:
|
||||
@ -308,7 +315,7 @@ class RequestUriTooLong(HTTPClientError):
|
||||
The URI provided was too long for the server to process.
|
||||
"""
|
||||
http_status = 414
|
||||
message = "Request-URI Too Long"
|
||||
message = _("Request-URI Too Long")
|
||||
|
||||
|
||||
class UnsupportedMediaType(HTTPClientError):
|
||||
@ -318,7 +325,7 @@ class UnsupportedMediaType(HTTPClientError):
|
||||
not support.
|
||||
"""
|
||||
http_status = 415
|
||||
message = "Unsupported Media Type"
|
||||
message = _("Unsupported Media Type")
|
||||
|
||||
|
||||
class RequestedRangeNotSatisfiable(HTTPClientError):
|
||||
@ -328,7 +335,7 @@ class RequestedRangeNotSatisfiable(HTTPClientError):
|
||||
supply that portion.
|
||||
"""
|
||||
http_status = 416
|
||||
message = "Requested Range Not Satisfiable"
|
||||
message = _("Requested Range Not Satisfiable")
|
||||
|
||||
|
||||
class ExpectationFailed(HTTPClientError):
|
||||
@ -337,7 +344,7 @@ class ExpectationFailed(HTTPClientError):
|
||||
The server cannot meet the requirements of the Expect request-header field.
|
||||
"""
|
||||
http_status = 417
|
||||
message = "Expectation Failed"
|
||||
message = _("Expectation Failed")
|
||||
|
||||
|
||||
class UnprocessableEntity(HTTPClientError):
|
||||
@ -347,7 +354,7 @@ class UnprocessableEntity(HTTPClientError):
|
||||
errors.
|
||||
"""
|
||||
http_status = 422
|
||||
message = "Unprocessable Entity"
|
||||
message = _("Unprocessable Entity")
|
||||
|
||||
|
||||
class InternalServerError(HttpServerError):
|
||||
@ -356,7 +363,7 @@ class InternalServerError(HttpServerError):
|
||||
A generic error message, given when no more specific message is suitable.
|
||||
"""
|
||||
http_status = 500
|
||||
message = "Internal Server Error"
|
||||
message = _("Internal Server Error")
|
||||
|
||||
|
||||
# NotImplemented is a python keyword.
|
||||
@ -367,7 +374,7 @@ class HttpNotImplemented(HttpServerError):
|
||||
the ability to fulfill the request.
|
||||
"""
|
||||
http_status = 501
|
||||
message = "Not Implemented"
|
||||
message = _("Not Implemented")
|
||||
|
||||
|
||||
class BadGateway(HttpServerError):
|
||||
@ -377,7 +384,7 @@ class BadGateway(HttpServerError):
|
||||
response from the upstream server.
|
||||
"""
|
||||
http_status = 502
|
||||
message = "Bad Gateway"
|
||||
message = _("Bad Gateway")
|
||||
|
||||
|
||||
class ServiceUnavailable(HttpServerError):
|
||||
@ -386,7 +393,7 @@ class ServiceUnavailable(HttpServerError):
|
||||
The server is currently unavailable.
|
||||
"""
|
||||
http_status = 503
|
||||
message = "Service Unavailable"
|
||||
message = _("Service Unavailable")
|
||||
|
||||
|
||||
class GatewayTimeout(HttpServerError):
|
||||
@ -396,7 +403,7 @@ class GatewayTimeout(HttpServerError):
|
||||
response from the upstream server.
|
||||
"""
|
||||
http_status = 504
|
||||
message = "Gateway Timeout"
|
||||
message = _("Gateway Timeout")
|
||||
|
||||
|
||||
class HttpVersionNotSupported(HttpServerError):
|
||||
@ -405,7 +412,7 @@ class HttpVersionNotSupported(HttpServerError):
|
||||
The server does not support the HTTP protocol version used in the request.
|
||||
"""
|
||||
http_status = 505
|
||||
message = "HTTP Version Not Supported"
|
||||
message = _("HTTP Version Not Supported")
|
||||
|
||||
|
||||
# _code_map contains all the classes that have http_status attribute.
|
||||
@ -423,12 +430,17 @@ def from_response(response, method, url):
|
||||
:param method: HTTP method used for request
|
||||
:param url: URL used for request
|
||||
"""
|
||||
|
||||
req_id = response.headers.get("x-openstack-request-id")
|
||||
#NOTE(hdd) true for older versions of nova and cinder
|
||||
if not req_id:
|
||||
req_id = response.headers.get("x-compute-request-id")
|
||||
kwargs = {
|
||||
"http_status": response.status_code,
|
||||
"response": response,
|
||||
"method": method,
|
||||
"url": url,
|
||||
"request_id": response.headers.get("x-compute-request-id"),
|
||||
"request_id": req_id,
|
||||
}
|
||||
if "retry-after" in response.headers:
|
||||
kwargs["retry_after"] = response.headers["retry-after"]
|
||||
|
||||
@ -28,7 +28,6 @@ import gettext
|
||||
import locale
|
||||
from logging import handlers
|
||||
import os
|
||||
import re
|
||||
|
||||
from babel import localedata
|
||||
import six
|
||||
@ -248,47 +247,22 @@ class Message(six.text_type):
|
||||
if other is None:
|
||||
params = (other,)
|
||||
elif isinstance(other, dict):
|
||||
params = self._trim_dictionary_parameters(other)
|
||||
# Merge the dictionaries
|
||||
# Copy each item in case one does not support deep copy.
|
||||
params = {}
|
||||
if isinstance(self.params, dict):
|
||||
for key, val in self.params.items():
|
||||
params[key] = self._copy_param(val)
|
||||
for key, val in other.items():
|
||||
params[key] = self._copy_param(val)
|
||||
else:
|
||||
params = self._copy_param(other)
|
||||
return params
|
||||
|
||||
def _trim_dictionary_parameters(self, dict_param):
|
||||
"""Return a dict that only has matching entries in the msgid."""
|
||||
# NOTE(luisg): Here we trim down the dictionary passed as parameters
|
||||
# to avoid carrying a lot of unnecessary weight around in the message
|
||||
# object, for example if someone passes in Message() % locals() but
|
||||
# only some params are used, and additionally we prevent errors for
|
||||
# non-deepcopyable objects by unicoding() them.
|
||||
|
||||
# Look for %(param) keys in msgid;
|
||||
# Skip %% and deal with the case where % is first character on the line
|
||||
keys = re.findall('(?:[^%]|^)?%\((\w*)\)[a-z]', self.msgid)
|
||||
|
||||
# If we don't find any %(param) keys but have a %s
|
||||
if not keys and re.findall('(?:[^%]|^)%[a-z]', self.msgid):
|
||||
# Apparently the full dictionary is the parameter
|
||||
params = self._copy_param(dict_param)
|
||||
else:
|
||||
params = {}
|
||||
# Save our existing parameters as defaults to protect
|
||||
# ourselves from losing values if we are called through an
|
||||
# (erroneous) chain that builds a valid Message with
|
||||
# arguments, and then does something like "msg % kwds"
|
||||
# where kwds is an empty dictionary.
|
||||
src = {}
|
||||
if isinstance(self.params, dict):
|
||||
src.update(self.params)
|
||||
src.update(dict_param)
|
||||
for key in keys:
|
||||
params[key] = self._copy_param(src[key])
|
||||
|
||||
return params
|
||||
|
||||
def _copy_param(self, param):
|
||||
try:
|
||||
return copy.deepcopy(param)
|
||||
except TypeError:
|
||||
except Exception:
|
||||
# Fallback to casting to unicode this will handle the
|
||||
# python code-like objects that can't be deep-copied
|
||||
return six.text_type(param)
|
||||
|
||||
@ -31,25 +31,29 @@ This module provides a few things:
|
||||
'''
|
||||
|
||||
|
||||
import codecs
|
||||
import datetime
|
||||
import functools
|
||||
import inspect
|
||||
import itertools
|
||||
import json
|
||||
try:
|
||||
import xmlrpclib
|
||||
except ImportError:
|
||||
# NOTE(jaypipes): xmlrpclib was renamed to xmlrpc.client in Python3
|
||||
# however the function and object call signatures
|
||||
# remained the same. This whole try/except block should
|
||||
# be removed and replaced with a call to six.moves once
|
||||
# six 1.4.2 is released. See http://bit.ly/1bqrVzu
|
||||
import xmlrpc.client as xmlrpclib
|
||||
import sys
|
||||
|
||||
if sys.version_info < (2, 7):
|
||||
# On Python <= 2.6, json module is not C boosted, so try to use
|
||||
# simplejson module if available
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
else:
|
||||
import json
|
||||
|
||||
import six
|
||||
import six.moves.xmlrpc_client as xmlrpclib
|
||||
|
||||
from novaclient.openstack.common import gettextutils
|
||||
from novaclient.openstack.common import importutils
|
||||
from novaclient.openstack.common import strutils
|
||||
from novaclient.openstack.common import timeutils
|
||||
|
||||
netaddr = importutils.try_import("netaddr")
|
||||
@ -164,12 +168,12 @@ def dumps(value, default=to_primitive, **kwargs):
|
||||
return json.dumps(value, default=default, **kwargs)
|
||||
|
||||
|
||||
def loads(s):
|
||||
return json.loads(s)
|
||||
def loads(s, encoding='utf-8'):
|
||||
return json.loads(strutils.safe_decode(s, encoding))
|
||||
|
||||
|
||||
def load(s):
|
||||
return json.load(s)
|
||||
def load(fp, encoding='utf-8'):
|
||||
return json.load(codecs.getreader(encoding)(fp))
|
||||
|
||||
|
||||
try:
|
||||
|
||||
@ -0,0 +1,108 @@
|
||||
# Copyright 2012 OpenStack Foundation.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Network-related utilities and helper functions.
|
||||
"""
|
||||
|
||||
# TODO(jd) Use six.moves once
|
||||
# https://bitbucket.org/gutworth/six/pull-request/28
|
||||
# is merged
|
||||
try:
|
||||
import urllib.parse
|
||||
SplitResult = urllib.parse.SplitResult
|
||||
except ImportError:
|
||||
import urlparse
|
||||
SplitResult = urlparse.SplitResult
|
||||
|
||||
from six.moves.urllib import parse
|
||||
|
||||
|
||||
def parse_host_port(address, default_port=None):
|
||||
"""Interpret a string as a host:port pair.
|
||||
|
||||
An IPv6 address MUST be escaped if accompanied by a port,
|
||||
because otherwise ambiguity ensues: 2001:db8:85a3::8a2e:370:7334
|
||||
means both [2001:db8:85a3::8a2e:370:7334] and
|
||||
[2001:db8:85a3::8a2e:370]:7334.
|
||||
|
||||
>>> parse_host_port('server01:80')
|
||||
('server01', 80)
|
||||
>>> parse_host_port('server01')
|
||||
('server01', None)
|
||||
>>> parse_host_port('server01', default_port=1234)
|
||||
('server01', 1234)
|
||||
>>> parse_host_port('[::1]:80')
|
||||
('::1', 80)
|
||||
>>> parse_host_port('[::1]')
|
||||
('::1', None)
|
||||
>>> parse_host_port('[::1]', default_port=1234)
|
||||
('::1', 1234)
|
||||
>>> parse_host_port('2001:db8:85a3::8a2e:370:7334', default_port=1234)
|
||||
('2001:db8:85a3::8a2e:370:7334', 1234)
|
||||
|
||||
"""
|
||||
if address[0] == '[':
|
||||
# Escaped ipv6
|
||||
_host, _port = address[1:].split(']')
|
||||
host = _host
|
||||
if ':' in _port:
|
||||
port = _port.split(':')[1]
|
||||
else:
|
||||
port = default_port
|
||||
else:
|
||||
if address.count(':') == 1:
|
||||
host, port = address.split(':')
|
||||
else:
|
||||
# 0 means ipv4, >1 means ipv6.
|
||||
# We prohibit unescaped ipv6 addresses with port.
|
||||
host = address
|
||||
port = default_port
|
||||
|
||||
return (host, None if port is None else int(port))
|
||||
|
||||
|
||||
class ModifiedSplitResult(SplitResult):
|
||||
"""Split results class for urlsplit."""
|
||||
|
||||
# NOTE(dims): The functions below are needed for Python 2.6.x.
|
||||
# We can remove these when we drop support for 2.6.x.
|
||||
@property
|
||||
def hostname(self):
|
||||
netloc = self.netloc.split('@', 1)[-1]
|
||||
host, port = parse_host_port(netloc)
|
||||
return host
|
||||
|
||||
@property
|
||||
def port(self):
|
||||
netloc = self.netloc.split('@', 1)[-1]
|
||||
host, port = parse_host_port(netloc)
|
||||
return port
|
||||
|
||||
|
||||
def urlsplit(url, scheme='', allow_fragments=True):
|
||||
"""Parse a URL using urlparse.urlsplit(), splitting query and fragments.
|
||||
This function papers over Python issue9374 when needed.
|
||||
|
||||
The parameters are the same as urlparse.urlsplit.
|
||||
"""
|
||||
scheme, netloc, path, query, fragment = parse.urlsplit(
|
||||
url, scheme, allow_fragments)
|
||||
if allow_fragments and '#' in path:
|
||||
path, fragment = path.split('#', 1)
|
||||
if '?' in path:
|
||||
path, query = path.split('?', 1)
|
||||
return ModifiedSplitResult(scheme, netloc,
|
||||
path, query, fragment)
|
||||
@ -98,7 +98,8 @@ def bool_from_string(subject, strict=False, default=False):
|
||||
|
||||
|
||||
def safe_decode(text, incoming=None, errors='strict'):
|
||||
"""Decodes incoming str using `incoming` if they're not already unicode.
|
||||
"""Decodes incoming text/bytes string using `incoming` if they're not
|
||||
already unicode.
|
||||
|
||||
:param incoming: Text's current encoding
|
||||
:param errors: Errors handling policy. See here for valid
|
||||
@ -107,7 +108,7 @@ def safe_decode(text, incoming=None, errors='strict'):
|
||||
representation of it.
|
||||
:raises TypeError: If text is not an instance of str
|
||||
"""
|
||||
if not isinstance(text, six.string_types):
|
||||
if not isinstance(text, (six.string_types, six.binary_type)):
|
||||
raise TypeError("%s can't be decoded" % type(text))
|
||||
|
||||
if isinstance(text, six.text_type):
|
||||
@ -137,7 +138,7 @@ def safe_decode(text, incoming=None, errors='strict'):
|
||||
|
||||
def safe_encode(text, incoming=None,
|
||||
encoding='utf-8', errors='strict'):
|
||||
"""Encodes incoming str/unicode using `encoding`.
|
||||
"""Encodes incoming text/bytes string using `encoding`.
|
||||
|
||||
If incoming is not specified, text is expected to be encoded with
|
||||
current python's default encoding. (`sys.getdefaultencoding`)
|
||||
@ -150,7 +151,7 @@ def safe_encode(text, incoming=None,
|
||||
representation of it.
|
||||
:raises TypeError: If text is not an instance of str
|
||||
"""
|
||||
if not isinstance(text, six.string_types):
|
||||
if not isinstance(text, (six.string_types, six.binary_type)):
|
||||
raise TypeError("%s can't be encoded" % type(text))
|
||||
|
||||
if not incoming:
|
||||
|
||||
@ -285,6 +285,11 @@ class OpenStackComputeShell(object):
|
||||
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'),
|
||||
@ -385,7 +390,9 @@ class OpenStackComputeShell(object):
|
||||
parser.add_argument('--bypass-url',
|
||||
metavar='<bypass-url>',
|
||||
dest='bypass_url',
|
||||
help="Use this API endpoint instead of the Service Catalog")
|
||||
default=utils.env('NOVACLIENT_BYPASS_URL'),
|
||||
help="Use this API endpoint instead of the Service Catalog. "
|
||||
"Defaults to env[NOVACLIENT_BYPASS_URL]")
|
||||
parser.add_argument('--bypass_url',
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
@ -550,6 +557,7 @@ class OpenStackComputeShell(object):
|
||||
return 0
|
||||
|
||||
os_username = args.os_username
|
||||
os_user_id = args.os_user_id
|
||||
os_password = None # Fetched and set later as needed
|
||||
os_tenant_name = args.os_tenant_name
|
||||
os_tenant_id = args.os_tenant_id
|
||||
@ -606,9 +614,10 @@ class OpenStackComputeShell(object):
|
||||
auth_plugin.parse_opts(args)
|
||||
|
||||
if not auth_plugin or not auth_plugin.opts:
|
||||
if not os_username:
|
||||
if not os_username and not os_user_id:
|
||||
raise exc.CommandError(_("You must provide a username "
|
||||
"via either --os-username or env[OS_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 "
|
||||
@ -639,8 +648,11 @@ class OpenStackComputeShell(object):
|
||||
raise exc.CommandError(_("You must provide an auth url "
|
||||
"via either --os-auth-url or env[OS_AUTH_URL]"))
|
||||
|
||||
self.cs = client.Client(options.os_compute_api_version, os_username,
|
||||
os_password, os_tenant_name, tenant_id=os_tenant_id,
|
||||
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,
|
||||
@ -649,7 +661,8 @@ 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,
|
||||
cacert=cacert, timeout=timeout)
|
||||
cacert=cacert, timeout=timeout,
|
||||
completion_cache=completion_cache)
|
||||
|
||||
# Now check for the password/token of which pieces of the
|
||||
# identifying keyring key can come from the underlying client
|
||||
@ -694,6 +707,12 @@ class OpenStackComputeShell(object):
|
||||
# sometimes need to be able to look up images information
|
||||
# via glance when connected to the nova api.
|
||||
image_service_type = 'image'
|
||||
# NOTE(hdd): the password is needed again because creating a new
|
||||
# Client without specifying bypass_url will force authentication.
|
||||
# We can't reuse self.cs's bypass_url, because that's the URL for
|
||||
# the nova service; we need to get glance's URL for this Client
|
||||
if not os_password:
|
||||
os_password = helper.password
|
||||
self.cs.image_cs = client.Client(
|
||||
options.os_compute_api_version, os_username,
|
||||
os_password, os_tenant_name, tenant_id=os_tenant_id,
|
||||
@ -763,6 +782,11 @@ class OpenStackComputeShell(object):
|
||||
|
||||
# I'm picky about my shell help.
|
||||
class OpenStackHelpFormatter(argparse.HelpFormatter):
|
||||
def __init__(self, prog, indent_increment=2, max_help_position=32,
|
||||
width=None):
|
||||
super(OpenStackHelpFormatter, self).__init__(prog, indent_increment,
|
||||
max_help_position, width)
|
||||
|
||||
def start_section(self, heading):
|
||||
# Title-case the headings
|
||||
heading = '%s%s' % (heading[0].upper(), heading[1:])
|
||||
@ -771,11 +795,14 @@ class OpenStackHelpFormatter(argparse.HelpFormatter):
|
||||
|
||||
def main():
|
||||
try:
|
||||
OpenStackComputeShell().main(map(strutils.safe_decode, sys.argv[1:]))
|
||||
argv = [strutils.safe_decode(a) for a in sys.argv[1:]]
|
||||
OpenStackComputeShell().main(argv)
|
||||
|
||||
except Exception as e:
|
||||
logger.debug(e, exc_info=1)
|
||||
print("ERROR: %s" % strutils.safe_encode(six.text_type(e)),
|
||||
details = {'name': strutils.safe_encode(e.__class__.__name__),
|
||||
'msg': strutils.safe_encode(six.text_type(e))}
|
||||
print("ERROR (%(name)s): %(msg)s" % details,
|
||||
file=sys.stderr)
|
||||
sys.exit(1)
|
||||
except KeyboardInterrupt as e:
|
||||
|
||||
@ -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 httpretty
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests.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
|
||||
}
|
||||
}
|
||||
|
||||
httpretty.register_uri(httpretty.POST, self.url(),
|
||||
body=jsonutils.dumps(post_os_agents),
|
||||
content_type='application/json')
|
||||
|
||||
put_os_agents_1 = {
|
||||
"agent": {
|
||||
"url": "/yyy/yyyy/yyyy",
|
||||
"version": "8.0",
|
||||
"md5hash": "add6bb58e139be103324d04d82d8f546",
|
||||
'id': 1
|
||||
}
|
||||
}
|
||||
|
||||
httpretty.register_uri(httpretty.PUT, self.url(1),
|
||||
body=jsonutils.dumps(put_os_agents_1),
|
||||
content_type='application/json')
|
||||
|
||||
httpretty.register_uri(httpretty.DELETE, self.url(1),
|
||||
content_type='application/json',
|
||||
status=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.
|
||||
|
||||
import httpretty
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests.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'},
|
||||
]}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url(),
|
||||
body=jsonutils.dumps(get_os_aggregates),
|
||||
content_type='application/json')
|
||||
|
||||
r = jsonutils.dumps({'aggregate': get_os_aggregates['aggregates'][0]})
|
||||
|
||||
httpretty.register_uri(httpretty.POST, self.url(), body=r,
|
||||
content_type='application/json')
|
||||
|
||||
for agg_id in (1, 2):
|
||||
for method in (httpretty.GET, httpretty.PUT):
|
||||
httpretty.register_uri(method, self.url(agg_id), body=r,
|
||||
content_type='application/json')
|
||||
|
||||
httpretty.register_uri(httpretty.POST, self.url(agg_id, 'action'),
|
||||
body=r, content_type='application/json')
|
||||
|
||||
httpretty.register_uri(httpretty.DELETE, self.url(1), status=202)
|
||||
@ -0,0 +1,99 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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 httpretty
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests.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
|
||||
}
|
||||
]
|
||||
}
|
||||
httpretty.register_uri(httpretty.GET, self.url(),
|
||||
body=jsonutils.dumps(get_os_availability_zone),
|
||||
content_type='application/json')
|
||||
|
||||
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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url('detail'),
|
||||
body=jsonutils.dumps(get_os_zone_detail),
|
||||
content_type='application/json')
|
||||
|
||||
|
||||
class V3(V1):
|
||||
zone_info_key = 'availability_zone_info'
|
||||
zone_name_key = 'zone_name'
|
||||
zone_state_key = 'zone_state'
|
||||
38
awx/lib/site-packages/novaclient/tests/fixture_data/base.py
Normal file
38
awx/lib/site-packages/novaclient/tests/fixture_data/base.py
Normal file
@ -0,0 +1,38 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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
|
||||
|
||||
def __init__(self, compute_url=COMPUTE_URL):
|
||||
super(Fixture, self).__init__()
|
||||
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
|
||||
58
awx/lib/site-packages/novaclient/tests/fixture_data/certs.py
Normal file
58
awx/lib/site-packages/novaclient/tests/fixture_data/certs.py
Normal file
@ -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.
|
||||
|
||||
import httpretty
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests.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'
|
||||
}
|
||||
}
|
||||
httpretty.register_uri(httpretty.GET, self.url('root'),
|
||||
body=jsonutils.dumps(get_os_certificate),
|
||||
content_type='application/json')
|
||||
|
||||
post_os_certificates = {
|
||||
'certificate': {
|
||||
'private_key': 'foo',
|
||||
'data': 'bar'
|
||||
}
|
||||
}
|
||||
httpretty.register_uri(httpretty.POST, self.url(),
|
||||
body=jsonutils.dumps(post_os_certificates),
|
||||
content_type='application/json')
|
||||
127
awx/lib/site-packages/novaclient/tests/fixture_data/client.py
Normal file
127
awx/lib/site-packages/novaclient/tests/fixture_data/client.py
Normal file
@ -0,0 +1,127 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import fixtures
|
||||
import httpretty
|
||||
from keystoneclient.auth.identity import v2
|
||||
from keystoneclient import session
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.v1_1 import client as v1_1client
|
||||
from novaclient.v3 import client as v3client
|
||||
|
||||
IDENTITY_URL = 'http://identityserver:5000/v2.0'
|
||||
COMPUTE_URL = 'http://compute.host'
|
||||
|
||||
|
||||
class V1(fixtures.Fixture):
|
||||
|
||||
def __init__(self, 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.token = {
|
||||
'access': {
|
||||
"token": {
|
||||
"id": "ab48a9efdfedb23ty3494",
|
||||
"expires": "2010-11-01T03:32:15-05:00",
|
||||
"tenant": {
|
||||
"id": "345",
|
||||
"name": "My Project"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"id": "123",
|
||||
"name": "jqsmith",
|
||||
"roles": [
|
||||
{
|
||||
"id": "234",
|
||||
"name": "compute:admin",
|
||||
},
|
||||
{
|
||||
"id": "235",
|
||||
"name": "object-store:admin",
|
||||
"tenantId": "1",
|
||||
}
|
||||
],
|
||||
"roles_links": [],
|
||||
},
|
||||
"serviceCatalog": [
|
||||
{
|
||||
"name": "Cloud Servers",
|
||||
"type": "compute",
|
||||
"endpoints": [
|
||||
{
|
||||
"publicURL": self.compute_url,
|
||||
"internalURL": "https://compute1.host/v1/1",
|
||||
},
|
||||
],
|
||||
"endpoints_links": [],
|
||||
},
|
||||
{
|
||||
"name": "Cloud Servers",
|
||||
"type": "computev3",
|
||||
"endpoints": [
|
||||
{
|
||||
"publicURL": self.compute_url,
|
||||
"internalURL": "https://compute1.host/v1/1",
|
||||
},
|
||||
],
|
||||
"endpoints_links": [],
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(V1, self).setUp()
|
||||
httpretty.enable()
|
||||
self.addCleanup(httpretty.disable)
|
||||
|
||||
auth_url = '%s/tokens' % self.identity_url
|
||||
httpretty.register_uri(httpretty.POST, auth_url,
|
||||
body=jsonutils.dumps(self.token),
|
||||
content_type='application/json')
|
||||
self.client = self.new_client()
|
||||
|
||||
def new_client(self):
|
||||
return v1_1client.Client(username='xx',
|
||||
api_key='xx',
|
||||
project_id='xx',
|
||||
auth_url=self.identity_url)
|
||||
|
||||
|
||||
class V3(V1):
|
||||
|
||||
def new_client(self):
|
||||
return v3client.Client(username='xx',
|
||||
password='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 v1_1client.Client(session=self.session)
|
||||
|
||||
|
||||
class SessionV3(V1):
|
||||
|
||||
def new_client(self):
|
||||
self.session = session.Session()
|
||||
self.session.auth = v2.Password(self.identity_url, 'xx', 'xx')
|
||||
return v3client.Client(session=self.session)
|
||||
@ -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 httpretty
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests.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}]}
|
||||
httpretty.register_uri(httpretty.GET, self.url(),
|
||||
body=jsonutils.dumps(get_os_cloudpipe),
|
||||
content_type='application/json')
|
||||
|
||||
instance_id = '9d5824aa-20e6-4b9f-b967-76a699fc51fd'
|
||||
post_os_cloudpipe = {'instance_id': instance_id}
|
||||
httpretty.register_uri(httpretty.POST, self.url(),
|
||||
body=jsonutils.dumps(post_os_cloudpipe),
|
||||
content_type='application/json',
|
||||
status=202)
|
||||
|
||||
httpretty.register_uri(httpretty.PUT, self.url('configure-project'),
|
||||
content_type='application/json',
|
||||
status=202)
|
||||
@ -0,0 +1,41 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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 httpretty
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests.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'
|
||||
}
|
||||
}
|
||||
httpretty.register_uri(httpretty.GET, self.url('192.168.1.1'),
|
||||
body=jsonutils.dumps(get_os_fixed_ips),
|
||||
content_type='application/json')
|
||||
|
||||
httpretty.register_uri(httpretty.POST,
|
||||
self.url('192.168.1.1', 'action'),
|
||||
content_type='application/json',
|
||||
status=202)
|
||||
@ -0,0 +1,216 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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 httpretty
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests import fakes
|
||||
from novaclient.tests.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}
|
||||
httpretty.register_uri(httpretty.GET, self.url(),
|
||||
body=jsonutils.dumps(get_os_floating_ips),
|
||||
content_type='application/json')
|
||||
|
||||
for ip in floating_ips:
|
||||
get_os_floating_ip = {'floating_ip': ip}
|
||||
httpretty.register_uri(httpretty.GET, self.url(ip['id']),
|
||||
body=jsonutils.dumps(get_os_floating_ip),
|
||||
content_type='application/json')
|
||||
|
||||
httpretty.register_uri(httpretty.DELETE, self.url(ip['id']),
|
||||
content_type='application/json',
|
||||
status=204)
|
||||
|
||||
def post_os_floating_ips(request, url, headers):
|
||||
body = jsonutils.loads(request.body.decode('utf-8'))
|
||||
ip = floating_ips[0].copy()
|
||||
ip['pool'] = body.get('pool')
|
||||
ip = jsonutils.dumps({'floating_ip': ip})
|
||||
return 200, headers, ip
|
||||
httpretty.register_uri(httpretty.POST, self.url(),
|
||||
body=post_os_floating_ips,
|
||||
content_type='application/json')
|
||||
|
||||
|
||||
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'}
|
||||
]
|
||||
}
|
||||
httpretty.register_uri(httpretty.GET, self.url(),
|
||||
body=jsonutils.dumps(get_os_floating_ip_dns),
|
||||
content_type='application/json',
|
||||
status=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')
|
||||
body = jsonutils.dumps(get_dns_testdomain_entries_testname)
|
||||
httpretty.register_uri(httpretty.GET, url,
|
||||
body=body,
|
||||
content_type='application/json',
|
||||
status=205)
|
||||
|
||||
httpretty.register_uri(httpretty.DELETE, self.url('testdomain'),
|
||||
status=200)
|
||||
|
||||
url = self.url('testdomain', 'entries', 'testname')
|
||||
httpretty.register_uri(httpretty.DELETE, url, status=200)
|
||||
|
||||
def put_dns_testdomain_entries_testname(request, url, headers):
|
||||
body = jsonutils.loads(request.body.decode('utf-8'))
|
||||
fakes.assert_has_keys(body['dns_entry'],
|
||||
required=['ip', 'dns_type'])
|
||||
return 205, headers, request.body
|
||||
httpretty.register_uri(httpretty.PUT, url,
|
||||
body=put_dns_testdomain_entries_testname,
|
||||
content_type='application/json')
|
||||
|
||||
url = self.url('testdomain', 'entries')
|
||||
httpretty.register_uri(httpretty.GET, url, status=404)
|
||||
|
||||
get_os_floating_ip_dns_testdomain_entries = {
|
||||
'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'
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
body = jsonutils.dumps(get_os_floating_ip_dns_testdomain_entries)
|
||||
httpretty.register_uri(httpretty.GET, url + '?ip=1.2.3.4',
|
||||
body=body,
|
||||
status=205,
|
||||
content_type='application/json')
|
||||
|
||||
def put_os_floating_ip_dns_testdomain(request, url, headers):
|
||||
body = jsonutils.loads(request.body.decode('utf-8'))
|
||||
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'])
|
||||
|
||||
headers['Content-Type'] = 'application/json'
|
||||
return (205, headers, request.body)
|
||||
|
||||
httpretty.register_uri(httpretty.PUT, self.url('testdomain'),
|
||||
body=put_os_floating_ip_dns_testdomain)
|
||||
|
||||
|
||||
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'},
|
||||
]
|
||||
}
|
||||
httpretty.register_uri(httpretty.GET, self.url(),
|
||||
body=jsonutils.dumps(get_os_floating_ips_bulk),
|
||||
content_type='application/json')
|
||||
httpretty.register_uri(httpretty.GET, self.url('testHost'),
|
||||
body=jsonutils.dumps(get_os_floating_ips_bulk),
|
||||
content_type='application/json')
|
||||
|
||||
def put_os_floating_ips_bulk_delete(request, url, headers):
|
||||
body = jsonutils.loads(request.body.decode('utf-8'))
|
||||
ip_range = body.get('ip_range')
|
||||
data = {'floating_ips_bulk_delete': ip_range}
|
||||
return 200, headers, jsonutils.dumps(data)
|
||||
|
||||
httpretty.register_uri(httpretty.PUT, self.url('delete'),
|
||||
body=put_os_floating_ips_bulk_delete,
|
||||
content_type='application/json')
|
||||
|
||||
def post_os_floating_ips_bulk(request, url, headers):
|
||||
body = jsonutils.loads(request.body.decode('utf-8'))
|
||||
params = body.get('floating_ips_bulk_create')
|
||||
pool = params.get('pool', 'defaultPool')
|
||||
interface = params.get('interface', 'defaultInterface')
|
||||
data = {
|
||||
'floating_ips_bulk_create': {
|
||||
'ip_range': '192.168.1.0/30',
|
||||
'pool': pool,
|
||||
'interface': interface
|
||||
}
|
||||
}
|
||||
return 200, headers, jsonutils.dumps(data)
|
||||
|
||||
httpretty.register_uri(httpretty.POST, self.url(),
|
||||
body=post_os_floating_ips_bulk,
|
||||
content_type='application/json')
|
||||
|
||||
|
||||
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'}
|
||||
]
|
||||
}
|
||||
httpretty.register_uri(httpretty.GET, self.url(),
|
||||
body=jsonutils.dumps(get_os_floating_ip_pools),
|
||||
content_type='application/json')
|
||||
49
awx/lib/site-packages/novaclient/tests/fixture_data/fping.py
Normal file
49
awx/lib/site-packages/novaclient/tests/fixture_data/fping.py
Normal file
@ -0,0 +1,49 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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 httpretty
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests.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,
|
||||
}
|
||||
}
|
||||
httpretty.register_uri(httpretty.GET, self.url(1),
|
||||
body=jsonutils.dumps(get_os_fping_1),
|
||||
content_type='application/json')
|
||||
|
||||
get_os_fping = {
|
||||
'servers': [
|
||||
get_os_fping_1['server'],
|
||||
{
|
||||
"id": "2",
|
||||
"project_id": "fake-project",
|
||||
"alive": True,
|
||||
},
|
||||
]
|
||||
}
|
||||
httpretty.register_uri(httpretty.GET, self.url(),
|
||||
body=jsonutils.dumps(get_os_fping),
|
||||
content_type='application/json')
|
||||
165
awx/lib/site-packages/novaclient/tests/fixture_data/hosts.py
Normal file
165
awx/lib/site-packages/novaclient/tests/fixture_data/hosts.py
Normal file
@ -0,0 +1,165 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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 httpretty
|
||||
from six.moves.urllib import parse
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests.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}}
|
||||
]
|
||||
}
|
||||
httpretty.register_uri(httpretty.GET, self.url('host'),
|
||||
body=jsonutils.dumps(get_os_hosts_host),
|
||||
content_type='application/json')
|
||||
|
||||
def get_os_hosts(request, url, headers):
|
||||
host, query = parse.splitquery(url)
|
||||
zone = 'nova1'
|
||||
|
||||
if query:
|
||||
qs = parse.parse_qs(query)
|
||||
try:
|
||||
zone = qs['zone'][0]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
data = {
|
||||
'hosts': [
|
||||
{
|
||||
'host': 'host1',
|
||||
'service': 'nova-compute',
|
||||
'zone': zone
|
||||
},
|
||||
{
|
||||
'host': 'host1',
|
||||
'service': 'nova-cert',
|
||||
'zone': zone
|
||||
}
|
||||
]
|
||||
}
|
||||
return 200, headers, jsonutils.dumps(data)
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url(),
|
||||
body=get_os_hosts,
|
||||
content_type='application/json')
|
||||
|
||||
get_os_hosts_sample_host = {
|
||||
'host': [
|
||||
{'resource': {'host': 'sample_host'}}
|
||||
],
|
||||
}
|
||||
httpretty.register_uri(httpretty.GET, self.url('sample_host'),
|
||||
body=jsonutils.dumps(get_os_hosts_sample_host),
|
||||
content_type='application/json')
|
||||
|
||||
httpretty.register_uri(httpretty.PUT, self.url('sample_host', 1),
|
||||
body=jsonutils.dumps(self.put_host_1()),
|
||||
content_type='application/json')
|
||||
|
||||
httpretty.register_uri(httpretty.PUT, self.url('sample_host', 2),
|
||||
body=jsonutils.dumps(self.put_host_2()),
|
||||
content_type='application/json')
|
||||
|
||||
httpretty.register_uri(httpretty.PUT, self.url('sample_host', 3),
|
||||
body=jsonutils.dumps(self.put_host_3()),
|
||||
content_type='application/json')
|
||||
|
||||
url = self.url('sample_host', 'reboot')
|
||||
httpretty.register_uri(httpretty.GET, url,
|
||||
body=jsonutils.dumps(self.get_host_reboot()),
|
||||
content_type='application/json')
|
||||
|
||||
url = self.url('sample_host', 'startup')
|
||||
httpretty.register_uri(httpretty.GET, url,
|
||||
body=jsonutils.dumps(self.get_host_startup()),
|
||||
content_type='application/json')
|
||||
|
||||
url = self.url('sample_host', 'shutdown')
|
||||
httpretty.register_uri(httpretty.GET, url,
|
||||
body=jsonutils.dumps(self.get_host_shutdown()),
|
||||
content_type='application/json')
|
||||
|
||||
def put_os_hosts_sample_host(request, url, headers):
|
||||
result = {'host': 'dummy'}
|
||||
result.update(jsonutils.loads(request.body.decode('utf-8')))
|
||||
return 200, headers, jsonutils.dumps(result)
|
||||
|
||||
httpretty.register_uri(httpretty.PUT, self.url('sample_host'),
|
||||
body=put_os_hosts_sample_host,
|
||||
content_type='application/json')
|
||||
|
||||
|
||||
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'}
|
||||
|
||||
|
||||
class V3(V1):
|
||||
def put_host_1(self):
|
||||
return {'host': super(V3, self).put_host_1()}
|
||||
|
||||
def put_host_2(self):
|
||||
return {'host': super(V3, self).put_host_2()}
|
||||
|
||||
def put_host_3(self):
|
||||
return {'host': super(V3, self).put_host_3()}
|
||||
|
||||
def get_host_reboot(self):
|
||||
return {'host': super(V3, self).get_host_reboot()}
|
||||
|
||||
def get_host_startup(self):
|
||||
return {'host': super(V3, self).get_host_startup()}
|
||||
|
||||
def get_host_shutdown(self):
|
||||
return {'host': super(V3, self).get_host_shutdown()}
|
||||
@ -0,0 +1,210 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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 httpretty
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests.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'},
|
||||
]
|
||||
}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url(),
|
||||
body=jsonutils.dumps(get_os_hypervisors),
|
||||
content_type='application/json')
|
||||
|
||||
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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url('detail'),
|
||||
body=jsonutils.dumps(get_os_hypervisors_detail),
|
||||
content_type='application/json')
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url('statistics'),
|
||||
body=jsonutils.dumps(get_os_hypervisors_stats),
|
||||
content_type='application/json')
|
||||
|
||||
get_os_hypervisors_search = {
|
||||
'hypervisors': [
|
||||
{'id': 1234, 'hypervisor_hostname': 'hyper1'},
|
||||
{'id': 5678, 'hypervisor_hostname': 'hyper2'}
|
||||
]
|
||||
}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url('hyper', 'search'),
|
||||
body=jsonutils.dumps(get_os_hypervisors_search),
|
||||
content_type='application/json')
|
||||
|
||||
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'}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url('hyper', 'servers'),
|
||||
body=jsonutils.dumps(get_hyper_server),
|
||||
content_type='application/json')
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url(1234),
|
||||
body=jsonutils.dumps(get_os_hypervisors_1234),
|
||||
content_type='application/json')
|
||||
|
||||
get_os_hypervisors_uptime = {
|
||||
'hypervisor': {
|
||||
'id': 1234,
|
||||
'hypervisor_hostname': 'hyper1',
|
||||
'uptime': 'fake uptime'
|
||||
}
|
||||
}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url(1234, 'uptime'),
|
||||
body=jsonutils.dumps(get_os_hypervisors_uptime),
|
||||
content_type='application/json')
|
||||
|
||||
|
||||
class V3(V1):
|
||||
|
||||
def setUp(self):
|
||||
super(V3, self).setUp()
|
||||
|
||||
get_os_hypervisors_search = {
|
||||
'hypervisors': [
|
||||
{'id': 1234, 'hypervisor_hostname': 'hyper1'},
|
||||
{'id': 5678, 'hypervisor_hostname': 'hyper2'}
|
||||
]
|
||||
}
|
||||
|
||||
httpretty.register_uri(httpretty.GET,
|
||||
self.url('search', query='hyper'),
|
||||
body=jsonutils.dumps(get_os_hypervisors_search),
|
||||
content_type='application/json')
|
||||
|
||||
get_1234_servers = {
|
||||
'hypervisor': {
|
||||
'id': 1234,
|
||||
'hypervisor_hostname': 'hyper1',
|
||||
'servers': [
|
||||
{'name': 'inst1', 'id': 'uuid1'},
|
||||
{'name': 'inst2', 'id': 'uuid2'}
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url(1234, 'servers'),
|
||||
body=jsonutils.dumps(get_1234_servers),
|
||||
content_type='application/json')
|
||||
119
awx/lib/site-packages/novaclient/tests/fixture_data/images.py
Normal file
119
awx/lib/site-packages/novaclient/tests/fixture_data/images.py
Normal file
@ -0,0 +1,119 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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 httpretty
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests import fakes
|
||||
from novaclient.tests.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'}
|
||||
]
|
||||
}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url(),
|
||||
body=jsonutils.dumps(get_images),
|
||||
content_type='application/json')
|
||||
|
||||
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": {},
|
||||
}
|
||||
|
||||
get_images_detail = {'images': [image_1, image_2]}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url('detail'),
|
||||
body=jsonutils.dumps(get_images_detail),
|
||||
content_type='application/json')
|
||||
|
||||
get_images_1 = {'image': image_1}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url(1),
|
||||
body=jsonutils.dumps(get_images_1),
|
||||
content_type='application/json')
|
||||
|
||||
get_images_2 = {'image': image_2}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url(2),
|
||||
body=jsonutils.dumps(get_images_2),
|
||||
content_type='application/json')
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url(456),
|
||||
body=jsonutils.dumps(get_images_2),
|
||||
content_type='application/json')
|
||||
|
||||
def post_images(request, url, headers):
|
||||
body = jsonutils.loads(request.body.decode('utf-8'))
|
||||
assert list(body) == ['image']
|
||||
fakes.assert_has_keys(body['image'], required=['serverId', 'name'])
|
||||
return 202, headers, jsonutils.dumps(images_1)
|
||||
|
||||
httpretty.register_uri(httpretty.POST, self.url(),
|
||||
body=post_images,
|
||||
content_type='application/json')
|
||||
|
||||
def post_images_1_metadata(request, url, headers):
|
||||
body = jsonutils.loads(request.body.decode('utf-8'))
|
||||
assert list(body) == ['metadata']
|
||||
fakes.assert_has_keys(body['metadata'], required=['test_key'])
|
||||
data = jsonutils.dumps({'metadata': image_1['metadata']})
|
||||
return 200, headers, data
|
||||
|
||||
httpretty.register_uri(httpretty.POST, self.url(1, 'metadata'),
|
||||
body=post_images_1_metadata,
|
||||
content_type='application/json')
|
||||
|
||||
for u in (1, 2, '1/metadata/test_key'):
|
||||
httpretty.register_uri(httpretty.DELETE, self.url(u),
|
||||
status=204)
|
||||
|
||||
httpretty.register_uri(httpretty.HEAD, self.url(1), status=200,
|
||||
x_image_meta_id=1,
|
||||
x_image_meta_name='CentOS 5.2',
|
||||
x_image_meta_updated='2010-10-10T12:00:00Z',
|
||||
x_image_meta_created='2010-10-10T12:00:00Z',
|
||||
x_image_meta_status='ACTIVE',
|
||||
x_image_meta_property_test_key='test_value')
|
||||
|
||||
|
||||
class V3(V1):
|
||||
|
||||
base_url = 'v1/images'
|
||||
@ -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.
|
||||
|
||||
|
||||
import httpretty
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests import fakes
|
||||
from novaclient.tests.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'}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url(),
|
||||
body=jsonutils.dumps({'keypairs': [keypair]}),
|
||||
content_type='application/json')
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url('test'),
|
||||
body=jsonutils.dumps({'keypair': keypair}),
|
||||
content_type='application/json')
|
||||
|
||||
httpretty.register_uri(httpretty.DELETE, self.url('test'), status=202)
|
||||
|
||||
def post_os_keypairs(request, url, headers):
|
||||
body = jsonutils.loads(request.body.decode('utf-8'))
|
||||
assert list(body) == ['keypair']
|
||||
fakes.assert_has_keys(body['keypair'], required=['name'])
|
||||
return 202, headers, jsonutils.dumps({'keypair': keypair})
|
||||
|
||||
httpretty.register_uri(httpretty.POST, self.url(),
|
||||
body=post_os_keypairs,
|
||||
content_type='application/json')
|
||||
|
||||
|
||||
class V3(V1):
|
||||
|
||||
base_url = 'keypairs'
|
||||
@ -0,0 +1,82 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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 httpretty
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests.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
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url(),
|
||||
body=jsonutils.dumps(get_limits),
|
||||
content_type='application/json')
|
||||
@ -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.
|
||||
|
||||
import httpretty
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests.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'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url(),
|
||||
body=jsonutils.dumps(get_os_networks),
|
||||
content_type='application/json')
|
||||
|
||||
def post_os_networks(request, url, headers):
|
||||
body = jsonutils.loads(request.body.decode('utf-8'))
|
||||
data = jsonutils.dumps({'network': body})
|
||||
return 202, headers, data
|
||||
|
||||
httpretty.register_uri(httpretty.POST, self.url(),
|
||||
body=post_os_networks,
|
||||
content_type='application/json')
|
||||
|
||||
get_os_networks_1 = {'network': {"label": "1", "cidr": "10.0.0.0/24"}}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url(1),
|
||||
body=jsonutils.dumps(get_os_networks_1),
|
||||
content_type='application/json')
|
||||
|
||||
httpretty.register_uri(httpretty.DELETE,
|
||||
self.url('networkdelete'),
|
||||
stauts=202)
|
||||
|
||||
for u in ('add', 'networkdisassociate/action', 'networktest/action',
|
||||
'1/action', '2/action'):
|
||||
httpretty.register_uri(httpretty.POST, self.url(u), stauts=202)
|
||||
@ -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.
|
||||
|
||||
import httpretty
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests.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 = jsonutils.dumps({'quota_set': self.test_quota('test')})
|
||||
|
||||
for u in ('test', 'tenant-id', 'tenant-id/defaults',
|
||||
'%s/defaults' % uuid2):
|
||||
httpretty.register_uri(httpretty.GET, self.url(u),
|
||||
body=test_json,
|
||||
content_type='application/json')
|
||||
|
||||
quota_json = jsonutils.dumps({'quota_set': self.test_quota(uuid)})
|
||||
httpretty.register_uri(httpretty.PUT, self.url(uuid),
|
||||
body=quota_json,
|
||||
content_type='application/json')
|
||||
httpretty.register_uri(httpretty.GET, self.url(uuid),
|
||||
body=quota_json,
|
||||
content_type='application/json')
|
||||
|
||||
quota_json2 = jsonutils.dumps({'quota_set': self.test_quota(uuid2)})
|
||||
httpretty.register_uri(httpretty.PUT, self.url(uuid2),
|
||||
body=quota_json2,
|
||||
content_type='application/json')
|
||||
httpretty.register_uri(httpretty.GET, self.url(uuid2),
|
||||
body=quota_json2,
|
||||
content_type='application/json')
|
||||
|
||||
for u in ('test', uuid2):
|
||||
httpretty.register_uri(httpretty.DELETE, self.url(u), status=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
|
||||
}
|
||||
|
||||
|
||||
class V3(V1):
|
||||
|
||||
def setUp(self):
|
||||
super(V3, self).setUp()
|
||||
|
||||
get_detail = {
|
||||
'quota_set': {
|
||||
'cores': {'reserved': 0, 'in_use': 0, 'limit': 10},
|
||||
'instances': {'reserved': 0, 'in_use': 4, 'limit': 50},
|
||||
'ram': {'reserved': 0, 'in_use': 1024, 'limit': 51200}
|
||||
}
|
||||
}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.url('test', 'detail'),
|
||||
body=jsonutils.dumps(get_detail),
|
||||
content_type='application/json')
|
||||
@ -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 httpretty
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests import fakes
|
||||
from novaclient.tests.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'
|
||||
}
|
||||
|
||||
get_rules = {'security_group_rules': [rule]}
|
||||
httpretty.register_uri(httpretty.GET, self.url(),
|
||||
body=jsonutils.dumps(get_rules),
|
||||
content_type='application/json')
|
||||
|
||||
for u in (1, 11, 12):
|
||||
httpretty.register_uri(httpretty.DELETE, self.url(u), status=202)
|
||||
|
||||
def post_rules(request, url, headers):
|
||||
body = jsonutils.loads(request.body.decode('utf-8'))
|
||||
assert list(body) == ['security_group_rule']
|
||||
fakes.assert_has_keys(body['security_group_rule'],
|
||||
required=['parent_group_id'],
|
||||
optional=['group_id', 'ip_protocol',
|
||||
'from_port', 'to_port', 'cidr'])
|
||||
|
||||
return 202, headers, jsonutils.dumps({'security_group_rule': rule})
|
||||
|
||||
httpretty.register_uri(httpretty.POST, self.url(),
|
||||
body=post_rules,
|
||||
content_type='application/json')
|
||||
@ -0,0 +1,99 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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 httpretty
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests import fakes
|
||||
from novaclient.tests.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]}
|
||||
httpretty.register_uri(httpretty.GET, self.url(),
|
||||
body=jsonutils.dumps(get_groups),
|
||||
content_type='application/json')
|
||||
|
||||
get_group_1 = {'security_group': security_group_1}
|
||||
httpretty.register_uri(httpretty.GET, self.url(1),
|
||||
body=jsonutils.dumps(get_group_1),
|
||||
content_type='application/json')
|
||||
|
||||
httpretty.register_uri(httpretty.DELETE, self.url(1), status=202)
|
||||
|
||||
def post_os_security_groups(request, url, headers):
|
||||
body = jsonutils.loads(request.body.decode('utf-8'))
|
||||
assert list(body) == ['security_group']
|
||||
fakes.assert_has_keys(body['security_group'],
|
||||
required=['name', 'description'])
|
||||
r = jsonutils.dumps({'security_group': security_group_1})
|
||||
return 202, headers, r
|
||||
|
||||
httpretty.register_uri(httpretty.POST, self.url(),
|
||||
body=post_os_security_groups,
|
||||
content_type='application/json')
|
||||
|
||||
def put_os_security_groups_1(request, url, headers):
|
||||
body = jsonutils.loads(request.body.decode('utf-8'))
|
||||
assert list(body) == ['security_group']
|
||||
fakes.assert_has_keys(body['security_group'],
|
||||
required=['name', 'description'])
|
||||
return 205, headers, request.body
|
||||
|
||||
httpretty.register_uri(httpretty.PUT, self.url(1),
|
||||
body=put_os_security_groups_1,
|
||||
content_type='application/json')
|
||||
@ -0,0 +1,75 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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 httpretty
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests.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"
|
||||
}
|
||||
]
|
||||
|
||||
get_server_groups = {'server_groups': server_groups}
|
||||
httpretty.register_uri(httpretty.GET, self.url(),
|
||||
body=jsonutils.dumps(get_server_groups),
|
||||
content_type='application/json')
|
||||
|
||||
server = server_groups[0]
|
||||
server_json = jsonutils.dumps({'server_group': server})
|
||||
|
||||
def _register(method, *args):
|
||||
httpretty.register_uri(method, self.url(*args), body=server_json)
|
||||
|
||||
_register(httpretty.POST)
|
||||
_register(httpretty.POST, server['id'])
|
||||
_register(httpretty.GET, server['id'])
|
||||
_register(httpretty.PUT, server['id'])
|
||||
_register(httpretty.POST, server['id'], '/action')
|
||||
|
||||
httpretty.register_uri(httpretty.DELETE, self.url(server['id']),
|
||||
status=202)
|
||||
@ -92,11 +92,11 @@ class DeprecatedAuthPluginTest(utils.TestCase):
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points",
|
||||
mock_iter_entry_points)
|
||||
@mock.patch.object(requests.Session, "request", mock_request)
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_auth_call():
|
||||
plugin = auth_plugin.DeprecatedAuthPlugin("fake")
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
"auth_url/v2.0", auth_system="fake",
|
||||
utils.AUTH_URL_V2, auth_system="fake",
|
||||
auth_plugin=plugin)
|
||||
cs.client.authenticate()
|
||||
|
||||
@ -121,12 +121,12 @@ class DeprecatedAuthPluginTest(utils.TestCase):
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points",
|
||||
mock_iter_entry_points)
|
||||
@mock.patch.object(requests.Session, "request", mock_request)
|
||||
@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",
|
||||
"auth_url/v2.0", auth_system="notexists",
|
||||
utils.AUTH_URL_V2, auth_system="notexists",
|
||||
auth_plugin=plugin)
|
||||
self.assertRaises(exceptions.AuthSystemNotFound,
|
||||
cs.client.authenticate)
|
||||
@ -164,7 +164,7 @@ class DeprecatedAuthPluginTest(utils.TestCase):
|
||||
|
||||
@mock.patch.object(pkg_resources, "iter_entry_points",
|
||||
mock_iter_entry_points)
|
||||
@mock.patch.object(requests.Session, "request", mock_request)
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_auth_call():
|
||||
plugin = auth_plugin.DeprecatedAuthPlugin("fakewithauthurl")
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
@ -197,7 +197,7 @@ class DeprecatedAuthPluginTest(utils.TestCase):
|
||||
|
||||
|
||||
class AuthPluginTest(utils.TestCase):
|
||||
@mock.patch.object(requests.Session, "request")
|
||||
@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."""
|
||||
@ -217,7 +217,7 @@ class AuthPluginTest(utils.TestCase):
|
||||
auth_plugin.discover_auth_systems()
|
||||
plugin = auth_plugin.load_plugin("fake")
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
"auth_url/v2.0", auth_system="fake",
|
||||
utils.AUTH_URL_V2, auth_system="fake",
|
||||
auth_plugin=plugin)
|
||||
cs.client.authenticate()
|
||||
|
||||
|
||||
@ -14,9 +14,12 @@
|
||||
# under the License.
|
||||
|
||||
|
||||
import logging
|
||||
import mock
|
||||
import requests
|
||||
|
||||
import fixtures
|
||||
|
||||
import novaclient.client
|
||||
import novaclient.extension
|
||||
import novaclient.tests.fakes as fakes
|
||||
@ -27,6 +30,16 @@ import novaclient.v3.client
|
||||
import json
|
||||
|
||||
|
||||
class ClientConnectionPoolTest(utils.TestCase):
|
||||
|
||||
@mock.patch("novaclient.client.adapters.HTTPAdapter")
|
||||
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):
|
||||
@ -43,9 +56,9 @@ class ClientTest(utils.TestCase):
|
||||
'x-server-management-url': 'blah.com',
|
||||
'x-auth-token': 'blah',
|
||||
}
|
||||
with mock.patch('requests.Session.request', mock_request):
|
||||
with mock.patch('requests.request', mock_request):
|
||||
instance.authenticate()
|
||||
requests.Session.request.assert_called_with(mock.ANY, mock.ANY,
|
||||
requests.request.assert_called_with(mock.ANY, mock.ANY,
|
||||
timeout=2,
|
||||
headers=mock.ANY,
|
||||
verify=mock.ANY)
|
||||
@ -61,7 +74,7 @@ class ClientTest(utils.TestCase):
|
||||
instance.version = 'v2.0'
|
||||
mock_request = mock.Mock()
|
||||
mock_request.side_effect = novaclient.exceptions.Unauthorized(401)
|
||||
with mock.patch('requests.Session.request', mock_request):
|
||||
with mock.patch('requests.request', mock_request):
|
||||
try:
|
||||
instance.get('/servers/detail')
|
||||
except Exception:
|
||||
@ -197,6 +210,26 @@ class ClientTest(utils.TestCase):
|
||||
cs.authenticate()
|
||||
self.assertTrue(mock_authenticate.called)
|
||||
|
||||
@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.v1_1.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)
|
||||
|
||||
@mock.patch('novaclient.client.HTTPClient')
|
||||
def test_contextmanager_v3(self, mock_http_client):
|
||||
fake_client = mock.Mock()
|
||||
mock_http_client.return_value = fake_client
|
||||
with novaclient.v3.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()
|
||||
@ -216,3 +249,110 @@ class ClientTest(utils.TestCase):
|
||||
cs.password_func = mock.Mock()
|
||||
self.assertEqual(cs._get_password(), "password")
|
||||
self.assertFalse(cs.password_func.called)
|
||||
|
||||
def test_auth_url_rstrip_slash(self):
|
||||
cs = novaclient.client.HTTPClient("user", "password", "project_id",
|
||||
auth_url="foo/v2/")
|
||||
self.assertEqual(cs.auth_url, "foo/v2")
|
||||
|
||||
def test_token_and_bypass_url(self):
|
||||
cs = novaclient.client.HTTPClient(None, None, None,
|
||||
auth_token="12345",
|
||||
bypass_url="compute/v100/")
|
||||
self.assertIsNone(cs.auth_url)
|
||||
self.assertEqual(cs.auth_token, "12345")
|
||||
self.assertEqual(cs.bypass_url, "compute/v100")
|
||||
self.assertEqual(cs.management_url, "compute/v100")
|
||||
|
||||
@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'}
|
||||
})
|
||||
|
||||
output = self.logger.output.split('\n')
|
||||
|
||||
self.assertIn("REQ: curl -i '/foo' -X GET", output)
|
||||
self.assertIn(
|
||||
"REQ: curl -i '/foo' -X GET -H "
|
||||
'"X-Auth-Token: {SHA1}b42162b6ffdbd7c3c37b7c95b7ba9f51dda0236d"',
|
||||
output)
|
||||
self.assertIn(
|
||||
"REQ: curl -i '/foo' -X GET -H "
|
||||
'"X-Auth-Token: {SHA1}b42162b6ffdbd7c3c37b7c95b7ba9f51dda0236d"'
|
||||
' -H "X-Foo: bar"',
|
||||
output)
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
|
||||
import mock
|
||||
import requests
|
||||
import six
|
||||
|
||||
from novaclient import client
|
||||
from novaclient import exceptions
|
||||
@ -37,10 +38,17 @@ bad_req_response = utils.TestResponse({
|
||||
})
|
||||
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)
|
||||
|
||||
|
||||
def get_client():
|
||||
cl = client.HTTPClient("username", "password",
|
||||
"project_id", "auth_test")
|
||||
"project_id",
|
||||
utils.AUTH_URL_V2)
|
||||
return cl
|
||||
|
||||
|
||||
@ -56,7 +64,7 @@ class ClientTest(utils.TestCase):
|
||||
def test_get(self):
|
||||
cl = get_authed_client()
|
||||
|
||||
@mock.patch.object(requests.Session, "request", mock_request)
|
||||
@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")
|
||||
@ -78,7 +86,7 @@ class ClientTest(utils.TestCase):
|
||||
def test_post(self):
|
||||
cl = get_authed_client()
|
||||
|
||||
@mock.patch.object(requests.Session, "request", mock_request)
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_post_call():
|
||||
cl.post("/hi", body=[1, 2, 3])
|
||||
headers = {
|
||||
@ -110,7 +118,7 @@ class ClientTest(utils.TestCase):
|
||||
def test_connection_refused(self):
|
||||
cl = get_client()
|
||||
|
||||
@mock.patch.object(requests.Session, "request", refused_mock_request)
|
||||
@mock.patch.object(requests, "request", refused_mock_request)
|
||||
def test_refused_call():
|
||||
self.assertRaises(exceptions.ConnectionRefused, cl.get, "/hi")
|
||||
|
||||
@ -119,7 +127,7 @@ class ClientTest(utils.TestCase):
|
||||
def test_bad_request(self):
|
||||
cl = get_client()
|
||||
|
||||
@mock.patch.object(requests.Session, "request", bad_req_mock_request)
|
||||
@mock.patch.object(requests, "request", bad_req_mock_request)
|
||||
def test_refused_call():
|
||||
self.assertRaises(exceptions.BadRequest, cl.get, "/hi")
|
||||
|
||||
@ -133,3 +141,16 @@ class ClientTest(utils.TestCase):
|
||||
cl2 = client.HTTPClient("username", "password", "project_id",
|
||||
"auth_test", http_log_debug=True)
|
||||
self.assertEqual(len(cl2._logger.handlers), 1)
|
||||
|
||||
@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')
|
||||
|
||||
@ -32,7 +32,7 @@ FAKE_ENV = {'OS_USERNAME': 'username',
|
||||
'OS_TENANT_NAME': 'tenant_name',
|
||||
'OS_AUTH_URL': 'http://no.where'}
|
||||
|
||||
FAKE_ENV2 = {'OS_USERNAME': 'username',
|
||||
FAKE_ENV2 = {'OS_USER_ID': 'user_id',
|
||||
'OS_PASSWORD': 'password',
|
||||
'OS_TENANT_ID': 'tenant_id',
|
||||
'OS_AUTH_URL': 'http://no.where'}
|
||||
@ -133,25 +133,38 @@ class ShellTest(utils.TestCase):
|
||||
matchers.MatchesRegex(r, re.DOTALL | re.MULTILINE))
|
||||
|
||||
def test_no_username(self):
|
||||
required = ('You must provide a username'
|
||||
' via either --os-username or env[OS_USERNAME]',)
|
||||
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)
|
||||
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 = ('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]',)
|
||||
' env[OS_TENANT_NAME] or env[OS_TENANT_ID]')
|
||||
self.make_env(exclude='OS_TENANT_NAME')
|
||||
try:
|
||||
self.shell('list')
|
||||
except exceptions.CommandError as message:
|
||||
self.assertEqual(required, message.args)
|
||||
self.assertEqual(required, message.args[0])
|
||||
else:
|
||||
self.fail('CommandError not raised')
|
||||
|
||||
@ -244,3 +257,17 @@ class ShellTest(utils.TestCase):
|
||||
@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())
|
||||
|
||||
@ -301,3 +301,23 @@ class ValidationsTestCase(test_utils.TestCase):
|
||||
self.fail("Invalid key passed validation: %s" % key)
|
||||
except exceptions.CommandError as ce:
|
||||
self.assertTrue(key in 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))
|
||||
|
||||
@ -14,9 +14,18 @@
|
||||
import os
|
||||
|
||||
import fixtures
|
||||
import httpretty
|
||||
import requests
|
||||
import six
|
||||
import testscenarios
|
||||
import testtools
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
|
||||
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"
|
||||
|
||||
|
||||
class TestCase(testtools.TestCase):
|
||||
TEST_REQUEST_BASE = {
|
||||
@ -35,6 +44,40 @@ class TestCase(testtools.TestCase):
|
||||
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()
|
||||
|
||||
httpretty.reset()
|
||||
self.data_fixture = None
|
||||
self.client_fixture = None
|
||||
self.cs = None
|
||||
|
||||
if self.client_fixture_class:
|
||||
self.client_fixture = self.useFixture(self.client_fixture_class())
|
||||
self.cs = self.client_fixture.client
|
||||
|
||||
if self.data_fixture_class:
|
||||
self.data_fixture = self.useFixture(self.data_fixture_class())
|
||||
|
||||
def assert_called(self, method, path, body=None):
|
||||
self.assertEqual(httpretty.last_request().method, method)
|
||||
self.assertEqual(httpretty.last_request().path, path)
|
||||
|
||||
if body:
|
||||
req_data = httpretty.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
|
||||
|
||||
@ -32,10 +32,10 @@ cs = fakes.FakeClient(extensions=extensions)
|
||||
class AssistedVolumeSnapshotsTestCase(utils.TestCase):
|
||||
|
||||
def test_create_snap(self):
|
||||
res = cs.assisted_volume_snapshots.create('1', {})
|
||||
cs.assisted_volume_snapshots.create('1', {})
|
||||
cs.assert_called('POST', '/os-assisted-volume-snapshots')
|
||||
|
||||
def test_delete_snap(self):
|
||||
res = cs.assisted_volume_snapshots.delete('x', {})
|
||||
cs.assisted_volume_snapshots.delete('x', {})
|
||||
cs.assert_called('DELETE',
|
||||
'/os-assisted-volume-snapshots/x?delete_info={}')
|
||||
|
||||
@ -369,7 +369,10 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
if 'personality' in body['server']:
|
||||
for pfile in body['server']['personality']:
|
||||
fakes.assert_has_keys(pfile, required=['path', 'contents'])
|
||||
return (202, {}, self.get_servers_1234()[2])
|
||||
if body['server']['name'] == 'some-bad-server':
|
||||
return (202, {}, self.get_servers_1235()[2])
|
||||
else:
|
||||
return (202, {}, self.get_servers_1234()[2])
|
||||
|
||||
def post_os_volumes_boot(self, body, **kw):
|
||||
assert set(body.keys()) <= set(['server', 'os:scheduler_hints'])
|
||||
@ -392,6 +395,13 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
r = {'server': self.get_servers_detail()[2]['servers'][0]}
|
||||
return (200, {}, r)
|
||||
|
||||
def get_servers_1235(self, **kw):
|
||||
r = {'server': self.get_servers_detail()[2]['servers'][0]}
|
||||
r['server']['id'] = 1235
|
||||
r['server']['status'] = 'error'
|
||||
r['server']['fault'] = {'message': 'something went wrong!'}
|
||||
return (200, {}, r)
|
||||
|
||||
def get_servers_5678(self, **kw):
|
||||
r = {'server': self.get_servers_detail()[2]['servers'][1]}
|
||||
return (200, {}, r)
|
||||
@ -405,6 +415,12 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
fakes.assert_has_keys(body['server'], optional=['name', 'adminPass'])
|
||||
return (204, {}, body)
|
||||
|
||||
def delete_os_server_groups_12345(self, **kw):
|
||||
return (202, {}, None)
|
||||
|
||||
def delete_os_server_groups_56789(self, **kw):
|
||||
return (202, {}, None)
|
||||
|
||||
def delete_servers_1234(self, **kw):
|
||||
return (202, {}, None)
|
||||
|
||||
@ -525,11 +541,11 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
assert list(body[action]) == ['type']
|
||||
assert body[action]['type'] in ['HARD', 'SOFT']
|
||||
elif action == 'rebuild':
|
||||
keys = list(body[action])
|
||||
if 'adminPass' in keys:
|
||||
keys.remove('adminPass')
|
||||
assert 'imageRef' in keys
|
||||
body = body[action]
|
||||
adminPass = body.get('adminPass', 'randompassword')
|
||||
assert 'imageRef' in body
|
||||
_body = self.get_servers_1234()[2]
|
||||
_body['server']['adminPass'] = adminPass
|
||||
elif action == 'resize':
|
||||
keys = body[action].keys()
|
||||
assert 'flavorRef' in keys
|
||||
@ -625,6 +641,9 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
raise AssertionError("Unexpected server action: %s" % action)
|
||||
return (resp, _headers, _body)
|
||||
|
||||
def post_servers_5678_action(self, body, **kw):
|
||||
return self.post_servers_1234_action(body, **kw)
|
||||
|
||||
#
|
||||
# Cloudpipe
|
||||
#
|
||||
@ -734,8 +753,11 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
def get_flavors_512_MB_Server(self, **kw):
|
||||
raise exceptions.NotFound('404')
|
||||
|
||||
def get_flavors_128_MB_Server(self, **kw):
|
||||
raise exceptions.NotFound('404')
|
||||
|
||||
def get_flavors_aa1(self, **kw):
|
||||
# Aplhanumeric flavor id are allowed.
|
||||
# Alphanumeric flavor id are allowed.
|
||||
return (
|
||||
200,
|
||||
{},
|
||||
@ -1003,12 +1025,24 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
# Keypairs
|
||||
#
|
||||
def get_os_keypairs_test(self, *kw):
|
||||
return (200, {}, {'keypair': self.get_os_keypairs()[2]['keypairs'][0]})
|
||||
return (200, {}, {'keypair':
|
||||
self.get_os_keypairs()[2]['keypairs'][0]['keypair']})
|
||||
|
||||
def get_os_keypairs(self, *kw):
|
||||
return (200, {}, {"keypairs": [
|
||||
{'fingerprint': 'FAKE_KEYPAIR', 'name': 'test'}
|
||||
]})
|
||||
{"keypair": {
|
||||
"public_key": "FAKE_SSH_RSA",
|
||||
"private_key": "FAKE_PRIVATE_KEY",
|
||||
"user_id":
|
||||
"81e373b596d6466e99c4896826abaa46",
|
||||
"name": "test",
|
||||
"deleted": False,
|
||||
"created_at": "2014-04-19T02:16:44.000000",
|
||||
"updated_at": "2014-04-19T10:12:3.000000",
|
||||
"figerprint": "FAKE_KEYPAIR",
|
||||
"deleted_at": None,
|
||||
"id": 4}
|
||||
}]})
|
||||
|
||||
def delete_os_keypairs_test(self, **kw):
|
||||
return (202, {}, None)
|
||||
@ -1017,7 +1051,7 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
assert list(body) == ['keypair']
|
||||
fakes.assert_has_keys(body['keypair'],
|
||||
required=['name'])
|
||||
r = {'keypair': self.get_os_keypairs()[2]['keypairs'][0]}
|
||||
r = {'keypair': self.get_os_keypairs()[2]['keypairs'][0]['keypair']}
|
||||
return (202, {}, r)
|
||||
|
||||
#
|
||||
@ -1486,6 +1520,9 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
'status': 'disabled',
|
||||
'disabled_reason': body['disabled_reason']}})
|
||||
|
||||
def delete_os_services_1(self, **kw):
|
||||
return (204, {}, None)
|
||||
|
||||
#
|
||||
# Fixed IPs
|
||||
#
|
||||
@ -1996,3 +2033,48 @@ class FakeHTTPClient(base_client.HTTPClient):
|
||||
return (200, {}, {'events': [
|
||||
{'name': 'network-changed',
|
||||
'server_uuid': '1234'}]})
|
||||
|
||||
#
|
||||
# Server Groups
|
||||
#
|
||||
|
||||
def get_os_server_groups(self, *kw):
|
||||
return (200, {},
|
||||
{"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"}]})
|
||||
|
||||
def _return_server_group(self):
|
||||
r = {'server_group':
|
||||
self.get_os_server_groups()[2]['server_groups'][0]}
|
||||
return (200, {}, r)
|
||||
|
||||
def post_os_server_groups(self, body, **kw):
|
||||
return self._return_server_group()
|
||||
|
||||
def get_os_server_groups_2cbd51f4_fafe_4cdb_801b_cf913a6f288b(self,
|
||||
**kw):
|
||||
return self._return_server_group()
|
||||
|
||||
def put_os_server_groups_2cbd51f4_fafe_4cdb_801b_cf913a6f288b(self,
|
||||
**kw):
|
||||
return self._return_server_group()
|
||||
|
||||
def post_os_server_groups_2cbd51f4_fafe_4cdb_801b_cf913a6f288b_action(
|
||||
self, body, **kw):
|
||||
return self._return_server_group()
|
||||
|
||||
def delete_os_server_groups_2cbd51f4_fafe_4cdb_801b_cf913a6f288b(
|
||||
self, **kw):
|
||||
return (202, {}, None)
|
||||
|
||||
@ -13,35 +13,64 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import httpretty
|
||||
|
||||
from novaclient.openstack.common import jsonutils
|
||||
from novaclient.tests.fixture_data import agents as data
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
from novaclient.v1_1 import agents
|
||||
|
||||
|
||||
class AgentsTest(utils.TestCase):
|
||||
def setUp(self):
|
||||
super(AgentsTest, self).setUp()
|
||||
self.cs = self._get_fake_client()
|
||||
self.agent_type = self._get_agent_type()
|
||||
class AgentsTest(utils.FixturedTestCase):
|
||||
|
||||
def _get_fake_client(self):
|
||||
return fakes.FakeClient()
|
||||
data_fixture_class = data.Fixture
|
||||
|
||||
def _get_agent_type(self):
|
||||
return agents.Agent
|
||||
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
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
httpretty.register_uri(httpretty.GET, self.data_fixture.url(),
|
||||
body=jsonutils.dumps(get_os_agents),
|
||||
content_type='application/json')
|
||||
|
||||
def test_list_agents(self):
|
||||
self.stub_hypervisors()
|
||||
ags = self.cs.agents.list()
|
||||
self.cs.assert_called('GET', '/os-agents')
|
||||
self.assert_called('GET', '/os-agents')
|
||||
for a in ags:
|
||||
self.assertIsInstance(a, self.agent_type)
|
||||
self.assertIsInstance(a, agents.Agent)
|
||||
self.assertEqual(a.hypervisor, 'kvm')
|
||||
|
||||
def test_list_agents_with_hypervisor(self):
|
||||
self.stub_hypervisors('xen')
|
||||
ags = self.cs.agents.list('xen')
|
||||
self.cs.assert_called('GET', '/os-agents?hypervisor=xen')
|
||||
self.assert_called('GET', '/os-agents?hypervisor=xen')
|
||||
for a in ags:
|
||||
self.assertIsInstance(a, self.agent_type)
|
||||
self.assertIsInstance(a, agents.Agent)
|
||||
self.assertEqual(a.hypervisor, 'xen')
|
||||
|
||||
def test_agents_create(self):
|
||||
@ -56,12 +85,12 @@ class AgentsTest(utils.TestCase):
|
||||
'version': '7.0',
|
||||
'architecture': 'x86',
|
||||
'os': 'win'}}
|
||||
self.cs.assert_called('POST', '/os-agents', body)
|
||||
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.cs.assert_called('DELETE', '/os-agents/1')
|
||||
self.assert_called('DELETE', '/os-agents/1')
|
||||
|
||||
def _build_example_update_body(self):
|
||||
return {"para": {
|
||||
@ -74,5 +103,5 @@ class AgentsTest(utils.TestCase):
|
||||
'/yyy/yyyy/yyyy',
|
||||
'add6bb58e139be103324d04d82d8f546')
|
||||
body = self._build_example_update_body()
|
||||
self.cs.assert_called('PUT', '/os-agents/1', body)
|
||||
self.assert_called('PUT', '/os-agents/1', body)
|
||||
self.assertEqual(1, ag.id)
|
||||
|
||||
@ -13,51 +13,47 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import aggregates as data
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
from novaclient.v1_1 import aggregates
|
||||
|
||||
|
||||
class AggregatesTest(utils.TestCase):
|
||||
def setUp(self):
|
||||
super(AggregatesTest, self).setUp()
|
||||
self.cs = self._get_fake_client()
|
||||
self.aggregate_type = self._get_aggregate_type()
|
||||
class AggregatesTest(utils.FixturedTestCase):
|
||||
|
||||
def _get_fake_client(self):
|
||||
return fakes.FakeClient()
|
||||
data_fixture_class = data.Fixture
|
||||
|
||||
def _get_aggregate_type(self):
|
||||
return aggregates.Aggregate
|
||||
scenarios = [('original', {'client_fixture_class': client.V1}),
|
||||
('session', {'client_fixture_class': client.SessionV1})]
|
||||
|
||||
def test_list_aggregates(self):
|
||||
result = self.cs.aggregates.list()
|
||||
self.cs.assert_called('GET', '/os-aggregates')
|
||||
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.cs.assert_called('POST', '/os-aggregates', body)
|
||||
self.assert_called('POST', '/os-aggregates', body)
|
||||
self.assertIsInstance(aggregate, aggregates.Aggregate)
|
||||
|
||||
def test_get(self):
|
||||
aggregate = self.cs.aggregates.get("1")
|
||||
self.cs.assert_called('GET', '/os-aggregates/1')
|
||||
self.assert_called('GET', '/os-aggregates/1')
|
||||
self.assertIsInstance(aggregate, aggregates.Aggregate)
|
||||
|
||||
aggregate2 = self.cs.aggregates.get(aggregate)
|
||||
self.cs.assert_called('GET', '/os-aggregates/1')
|
||||
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.cs.assert_called('GET', '/os-aggregates/1')
|
||||
self.assert_called('GET', '/os-aggregates/1')
|
||||
self.assertIsInstance(aggregate, aggregates.Aggregate)
|
||||
|
||||
aggregate2 = self.cs.aggregates.get_details(aggregate)
|
||||
self.cs.assert_called('GET', '/os-aggregates/1')
|
||||
self.assert_called('GET', '/os-aggregates/1')
|
||||
self.assertIsInstance(aggregate2, aggregates.Aggregate)
|
||||
|
||||
def test_update(self):
|
||||
@ -66,11 +62,11 @@ class AggregatesTest(utils.TestCase):
|
||||
body = {"aggregate": values}
|
||||
|
||||
result1 = aggregate.update(values)
|
||||
self.cs.assert_called('PUT', '/os-aggregates/1', body)
|
||||
self.assert_called('PUT', '/os-aggregates/1', body)
|
||||
self.assertIsInstance(result1, aggregates.Aggregate)
|
||||
|
||||
result2 = self.cs.aggregates.update(2, values)
|
||||
self.cs.assert_called('PUT', '/os-aggregates/2', body)
|
||||
self.assert_called('PUT', '/os-aggregates/2', body)
|
||||
self.assertIsInstance(result2, aggregates.Aggregate)
|
||||
|
||||
def test_update_with_availability_zone(self):
|
||||
@ -79,7 +75,7 @@ class AggregatesTest(utils.TestCase):
|
||||
body = {"aggregate": values}
|
||||
|
||||
result3 = self.cs.aggregates.update(aggregate, values)
|
||||
self.cs.assert_called('PUT', '/os-aggregates/1', body)
|
||||
self.assert_called('PUT', '/os-aggregates/1', body)
|
||||
self.assertIsInstance(result3, aggregates.Aggregate)
|
||||
|
||||
def test_add_host(self):
|
||||
@ -88,15 +84,15 @@ class AggregatesTest(utils.TestCase):
|
||||
body = {"add_host": {"host": "host1"}}
|
||||
|
||||
result1 = aggregate.add_host(host)
|
||||
self.cs.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assertIsInstance(result1, aggregates.Aggregate)
|
||||
|
||||
result2 = self.cs.aggregates.add_host("2", host)
|
||||
self.cs.assert_called('POST', '/os-aggregates/2/action', body)
|
||||
self.assert_called('POST', '/os-aggregates/2/action', body)
|
||||
self.assertIsInstance(result2, aggregates.Aggregate)
|
||||
|
||||
result3 = self.cs.aggregates.add_host(aggregate, host)
|
||||
self.cs.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assertIsInstance(result3, aggregates.Aggregate)
|
||||
|
||||
def test_remove_host(self):
|
||||
@ -105,15 +101,15 @@ class AggregatesTest(utils.TestCase):
|
||||
body = {"remove_host": {"host": "host1"}}
|
||||
|
||||
result1 = aggregate.remove_host(host)
|
||||
self.cs.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assertIsInstance(result1, aggregates.Aggregate)
|
||||
|
||||
result2 = self.cs.aggregates.remove_host("2", host)
|
||||
self.cs.assert_called('POST', '/os-aggregates/2/action', body)
|
||||
self.assert_called('POST', '/os-aggregates/2/action', body)
|
||||
self.assertIsInstance(result2, aggregates.Aggregate)
|
||||
|
||||
result3 = self.cs.aggregates.remove_host(aggregate, host)
|
||||
self.cs.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assertIsInstance(result3, aggregates.Aggregate)
|
||||
|
||||
def test_set_metadata(self):
|
||||
@ -122,24 +118,24 @@ class AggregatesTest(utils.TestCase):
|
||||
body = {"set_metadata": {"metadata": metadata}}
|
||||
|
||||
result1 = aggregate.set_metadata(metadata)
|
||||
self.cs.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
self.assertIsInstance(result1, aggregates.Aggregate)
|
||||
|
||||
result2 = self.cs.aggregates.set_metadata(2, metadata)
|
||||
self.cs.assert_called('POST', '/os-aggregates/2/action', body)
|
||||
self.assert_called('POST', '/os-aggregates/2/action', body)
|
||||
self.assertIsInstance(result2, aggregates.Aggregate)
|
||||
|
||||
result3 = self.cs.aggregates.set_metadata(aggregate, metadata)
|
||||
self.cs.assert_called('POST', '/os-aggregates/1/action', body)
|
||||
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.cs.assert_called('DELETE', '/os-aggregates/1')
|
||||
self.assert_called('DELETE', '/os-aggregates/1')
|
||||
|
||||
self.cs.aggregates.delete('1')
|
||||
self.cs.assert_called('DELETE', '/os-aggregates/1')
|
||||
self.assert_called('DELETE', '/os-aggregates/1')
|
||||
|
||||
self.cs.aggregates.delete(aggregate)
|
||||
self.cs.assert_called('DELETE', '/os-aggregates/1')
|
||||
self.assert_called('DELETE', '/os-aggregates/1')
|
||||
|
||||
@ -25,7 +25,7 @@ from novaclient.v1_1 import client
|
||||
class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
def test_authenticate_success(self):
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
"auth_url/v2.0", service_type='compute')
|
||||
utils.AUTH_URL_V2, service_type='compute')
|
||||
resp = {
|
||||
"access": {
|
||||
"token": {
|
||||
@ -57,7 +57,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
|
||||
mock_request = mock.Mock(return_value=(auth_response))
|
||||
|
||||
@mock.patch.object(requests.Session, "request", mock_request)
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_auth_call():
|
||||
cs.client.authenticate()
|
||||
headers = {
|
||||
@ -94,7 +94,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
|
||||
def test_authenticate_failure(self):
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
"auth_url/v2.0")
|
||||
utils.AUTH_URL_V2)
|
||||
resp = {"unauthorized": {"message": "Unauthorized", "code": "401"}}
|
||||
auth_response = utils.TestResponse({
|
||||
"status_code": 401,
|
||||
@ -111,7 +111,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
|
||||
def test_v1_auth_redirect(self):
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
"auth_url/v1.0", service_type='compute')
|
||||
utils.AUTH_URL_V1, service_type='compute')
|
||||
dict_correct_response = {
|
||||
"access": {
|
||||
"token": {
|
||||
@ -160,7 +160,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
|
||||
mock_request = mock.Mock(side_effect=side_effect)
|
||||
|
||||
@mock.patch.object(requests.Session, "request", mock_request)
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_auth_call():
|
||||
cs.client.authenticate()
|
||||
headers = {
|
||||
@ -199,7 +199,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
|
||||
def test_v2_auth_redirect(self):
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
"auth_url/v2.0", service_type='compute')
|
||||
utils.AUTH_URL_V2, service_type='compute')
|
||||
dict_correct_response = {
|
||||
"access": {
|
||||
"token": {
|
||||
@ -248,7 +248,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
|
||||
mock_request = mock.Mock(side_effect=side_effect)
|
||||
|
||||
@mock.patch.object(requests.Session, "request", mock_request)
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_auth_call():
|
||||
cs.client.authenticate()
|
||||
headers = {
|
||||
@ -287,7 +287,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
|
||||
def test_ambiguous_endpoints(self):
|
||||
cs = client.Client("username", "password", "project_id",
|
||||
"auth_url/v2.0", service_type='compute')
|
||||
utils.AUTH_URL_V2, service_type='compute')
|
||||
resp = {
|
||||
"access": {
|
||||
"token": {
|
||||
@ -340,7 +340,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
|
||||
def test_authenticate_with_token_success(self):
|
||||
cs = client.Client("username", None, "project_id",
|
||||
"auth_url/v2.0", service_type='compute')
|
||||
utils.AUTH_URL_V2, service_type='compute')
|
||||
cs.client.auth_token = "FAKE_ID"
|
||||
resp = {
|
||||
"access": {
|
||||
@ -373,7 +373,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
|
||||
mock_request = mock.Mock(return_value=(auth_response))
|
||||
|
||||
with mock.patch.object(requests.Session, "request", mock_request):
|
||||
with mock.patch.object(requests, "request", mock_request):
|
||||
cs.client.authenticate()
|
||||
headers = {
|
||||
'User-Agent': cs.client.USER_AGENT,
|
||||
@ -405,7 +405,7 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
self.assertEqual(cs.client.auth_token, token_id)
|
||||
|
||||
def test_authenticate_with_token_failure(self):
|
||||
cs = client.Client("username", None, "project_id", "auth_url/v2.0")
|
||||
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({
|
||||
@ -421,7 +421,8 @@ class AuthenticateAgainstKeystoneTests(utils.TestCase):
|
||||
|
||||
class AuthenticationTests(utils.TestCase):
|
||||
def test_authenticate_success(self):
|
||||
cs = client.Client("username", "password", "project_id", "auth_url")
|
||||
cs = client.Client("username", "password",
|
||||
"project_id", utils.AUTH_URL)
|
||||
management_url = 'https://localhost/v1.1/443470'
|
||||
auth_response = utils.TestResponse({
|
||||
'status_code': 204,
|
||||
@ -432,7 +433,7 @@ class AuthenticationTests(utils.TestCase):
|
||||
})
|
||||
mock_request = mock.Mock(return_value=(auth_response))
|
||||
|
||||
@mock.patch.object(requests.Session, "request", mock_request)
|
||||
@mock.patch.object(requests, "request", mock_request)
|
||||
def test_auth_call():
|
||||
cs.client.authenticate()
|
||||
headers = {
|
||||
@ -456,18 +457,20 @@ class AuthenticationTests(utils.TestCase):
|
||||
test_auth_call()
|
||||
|
||||
def test_authenticate_failure(self):
|
||||
cs = client.Client("username", "password", "project_id", "auth_url")
|
||||
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.Session, "request", mock_request)
|
||||
@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", "auth_url")
|
||||
cs = client.Client("username", "password",
|
||||
"project_id", utils.AUTH_URL)
|
||||
http_client = cs.client
|
||||
http_client.management_url = ''
|
||||
mock_request = mock.Mock(return_value=(None, None))
|
||||
@ -482,7 +485,8 @@ class AuthenticationTests(utils.TestCase):
|
||||
test_auth_call()
|
||||
|
||||
def test_auth_manual(self):
|
||||
cs = client.Client("username", "password", "project_id", "auth_url")
|
||||
cs = client.Client("username", "password",
|
||||
"project_id", utils.AUTH_URL)
|
||||
|
||||
@mock.patch.object(cs.client, 'authenticate')
|
||||
def test_auth_call(m):
|
||||
|
||||
@ -16,24 +16,26 @@
|
||||
|
||||
import six
|
||||
|
||||
from novaclient.tests.fixture_data import availability_zones as data
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
from novaclient.v1_1 import availability_zones
|
||||
|
||||
|
||||
class AvailabilityZoneTest(utils.TestCase):
|
||||
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.v1_1 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.cs = self._get_fake_client()
|
||||
self.availability_zone_type = self._get_availability_zone_type()
|
||||
|
||||
def _get_fake_client(self):
|
||||
return fakes.FakeClient()
|
||||
|
||||
def _get_availability_zone_type(self):
|
||||
return availability_zones.AvailabilityZone
|
||||
|
||||
@ -43,7 +45,7 @@ class AvailabilityZoneTest(utils.TestCase):
|
||||
|
||||
def test_list_availability_zone(self):
|
||||
zones = self.cs.availability_zones.list(detailed=False)
|
||||
self.cs.assert_called('GET', '/os-availability-zone')
|
||||
self.assert_called('GET', '/os-availability-zone')
|
||||
|
||||
for zone in zones:
|
||||
self.assertIsInstance(zone, self.availability_zone_type)
|
||||
@ -63,7 +65,7 @@ class AvailabilityZoneTest(utils.TestCase):
|
||||
|
||||
def test_detail_availability_zone(self):
|
||||
zones = self.cs.availability_zones.list(detailed=True)
|
||||
self.cs.assert_called('GET', '/os-availability-zone/detail')
|
||||
self.assert_called('GET', '/os-availability-zone/detail')
|
||||
|
||||
for zone in zones:
|
||||
self.assertIsInstance(zone, self.availability_zone_type)
|
||||
|
||||
@ -11,29 +11,26 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import certs as data
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
from novaclient.v1_1 import certs
|
||||
|
||||
|
||||
class CertsTest(utils.TestCase):
|
||||
def setUp(self):
|
||||
super(CertsTest, self).setUp()
|
||||
self.cs = self._get_fake_client()
|
||||
self.cert_type = self._get_cert_type()
|
||||
class CertsTest(utils.FixturedTestCase):
|
||||
|
||||
def _get_fake_client(self):
|
||||
return fakes.FakeClient()
|
||||
data_fixture_class = data.Fixture
|
||||
cert_type = certs.Certificate
|
||||
|
||||
def _get_cert_type(self):
|
||||
return 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.cs.assert_called('POST', '/os-certificates')
|
||||
self.assertIsInstance(cert, certs.Certificate)
|
||||
self.assert_called('POST', '/os-certificates')
|
||||
self.assertIsInstance(cert, self.cert_type)
|
||||
|
||||
def test_get_root_cert(self):
|
||||
cert = self.cs.certs.get()
|
||||
self.cs.assert_called('GET', '/os-certificates/root')
|
||||
self.assertIsInstance(cert, certs.Certificate)
|
||||
self.assert_called('GET', '/os-certificates/root')
|
||||
self.assertIsInstance(cert, self.cert_type)
|
||||
|
||||
@ -11,30 +11,35 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import six
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import cloudpipe as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
from novaclient.v1_1 import cloudpipe
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
class CloudpipeTest(utils.FixturedTestCase):
|
||||
|
||||
data_fixture_class = data.Fixture
|
||||
|
||||
class CloudpipeTest(utils.TestCase):
|
||||
scenarios = [('original', {'client_fixture_class': client.V1}),
|
||||
('session', {'client_fixture_class': client.SessionV1})]
|
||||
|
||||
def test_list_cloudpipes(self):
|
||||
cp = cs.cloudpipe.list()
|
||||
cs.assert_called('GET', '/os-cloudpipe')
|
||||
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 = cs.cloudpipe.create(project)
|
||||
cp = self.cs.cloudpipe.create(project)
|
||||
body = {'cloudpipe': {'project_id': project}}
|
||||
cs.assert_called('POST', '/os-cloudpipe', body)
|
||||
self.assertIsInstance(cp, str)
|
||||
self.assert_called('POST', '/os-cloudpipe', body)
|
||||
self.assertIsInstance(cp, six.string_types)
|
||||
|
||||
def test_update(self):
|
||||
cs.cloudpipe.update("192.168.1.1", 2345)
|
||||
self.cs.cloudpipe.update("192.168.1.1", 2345)
|
||||
body = {'configure_project': {'vpn_ip': "192.168.1.1",
|
||||
'vpn_port': 2345}}
|
||||
cs.assert_called('PUT', '/os-cloudpipe/configure-project', body)
|
||||
self.assert_called('PUT', '/os-cloudpipe/configure-project', body)
|
||||
|
||||
@ -13,17 +13,21 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import fixedips as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
|
||||
|
||||
class FixedIpsTest(utils.TestCase):
|
||||
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 = cs.fixed_ips.get(fixed_ip='192.168.1.1')
|
||||
cs.assert_called('GET', '/os-fixed-ips/192.168.1.1')
|
||||
info = self.cs.fixed_ips.get(fixed_ip='192.168.1.1')
|
||||
self.assert_called('GET', '/os-fixed-ips/192.168.1.1')
|
||||
self.assertEqual(info.cidr, '192.168.1.0/24')
|
||||
self.assertEqual(info.address, '192.168.1.1')
|
||||
self.assertEqual(info.hostname, 'foo')
|
||||
@ -31,10 +35,10 @@ class FixedIpsTest(utils.TestCase):
|
||||
|
||||
def test_reserve_fixed_ip(self):
|
||||
body = {"reserve": None}
|
||||
res = cs.fixed_ips.reserve(fixed_ip='192.168.1.1')
|
||||
cs.assert_called('POST', '/os-fixed-ips/192.168.1.1/action', body)
|
||||
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}
|
||||
res = cs.fixed_ips.unreserve(fixed_ip='192.168.1.1')
|
||||
cs.assert_called('POST', '/os-fixed-ips/192.168.1.1/action', body)
|
||||
self.cs.fixed_ips.unreserve(fixed_ip='192.168.1.1')
|
||||
self.assert_called('POST', '/os-fixed-ips/192.168.1.1/action', body)
|
||||
|
||||
@ -56,3 +56,15 @@ class FlavorAccessTest(utils.TestCase):
|
||||
|
||||
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))
|
||||
|
||||
@ -13,6 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
|
||||
from novaclient import exceptions
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
@ -206,7 +208,12 @@ class FlavorsTest(utils.TestCase):
|
||||
for key in invalid_keys:
|
||||
self.assertRaises(exceptions.CommandError, f.set_keys, {key: 'v1'})
|
||||
|
||||
def test_unset_keys(self):
|
||||
@mock.patch.object(flavors.FlavorManager, '_delete')
|
||||
def test_unset_keys(self, mock_delete):
|
||||
f = self.cs.flavors.get(1)
|
||||
f.unset_keys(['k1'])
|
||||
self.cs.assert_called('DELETE', '/flavors/1/os-extra_specs/k1')
|
||||
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")
|
||||
])
|
||||
|
||||
@ -11,20 +11,20 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import floatingips as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
from novaclient.v1_1 import floating_ip_dns
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
|
||||
|
||||
class FloatingIPDNSDomainTest(utils.TestCase):
|
||||
class FloatingIPDNSDomainTest(utils.FixturedTestCase):
|
||||
|
||||
testdomain = "testdomain"
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.DNSFixture
|
||||
|
||||
def test_dns_domains(self):
|
||||
domainlist = cs.dns_domains.domains()
|
||||
domainlist = self.cs.dns_domains.domains()
|
||||
self.assertEqual(len(domainlist), 2)
|
||||
|
||||
for entry in domainlist:
|
||||
@ -34,30 +34,33 @@ class FloatingIPDNSDomainTest(utils.TestCase):
|
||||
self.assertEqual(domainlist[1].domain, 'example.com')
|
||||
|
||||
def test_create_private_domain(self):
|
||||
cs.dns_domains.create_private(self.testdomain, 'test_avzone')
|
||||
cs.assert_called('PUT', '/os-floating-ip-dns/%s' %
|
||||
self.testdomain)
|
||||
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):
|
||||
cs.dns_domains.create_public(self.testdomain, 'test_project')
|
||||
cs.assert_called('PUT', '/os-floating-ip-dns/%s' %
|
||||
self.testdomain)
|
||||
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):
|
||||
cs.dns_domains.delete(self.testdomain)
|
||||
cs.assert_called('DELETE', '/os-floating-ip-dns/%s' %
|
||||
self.testdomain)
|
||||
self.cs.dns_domains.delete(self.testdomain)
|
||||
self.assert_called('DELETE', '/os-floating-ip-dns/%s' %
|
||||
self.testdomain)
|
||||
|
||||
|
||||
class FloatingIPDNSEntryTest(utils.TestCase):
|
||||
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 = cs.dns_entries.get_for_ip(self.testdomain, ip=self.testip)
|
||||
entries = self.cs.dns_entries.get_for_ip(self.testdomain,
|
||||
ip=self.testip)
|
||||
self.assertEqual(len(entries), 2)
|
||||
|
||||
for entry in entries:
|
||||
@ -68,21 +71,21 @@ class FloatingIPDNSEntryTest(utils.TestCase):
|
||||
self.assertEqual(entries[1].dns_entry['ip'], self.testip)
|
||||
|
||||
def test_get_dns_entry_by_name(self):
|
||||
entry = cs.dns_entries.get(self.testdomain,
|
||||
self.testname)
|
||||
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):
|
||||
cs.dns_entries.create(self.testdomain,
|
||||
self.testname,
|
||||
self.testip,
|
||||
self.testtype)
|
||||
self.cs.dns_entries.create(self.testdomain,
|
||||
self.testname,
|
||||
self.testip,
|
||||
self.testtype)
|
||||
|
||||
cs.assert_called('PUT', '/os-floating-ip-dns/%s/entries/%s' %
|
||||
(self.testdomain, self.testname))
|
||||
self.assert_called('PUT', '/os-floating-ip-dns/%s/entries/%s' %
|
||||
(self.testdomain, self.testname))
|
||||
|
||||
def test_delete_entry(self):
|
||||
cs.dns_entries.delete(self.testdomain, self.testname)
|
||||
cs.assert_called('DELETE', '/os-floating-ip-dns/%s/entries/%s' %
|
||||
(self.testdomain, self.testname))
|
||||
self.cs.dns_entries.delete(self.testdomain, self.testname)
|
||||
self.assert_called('DELETE', '/os-floating-ip-dns/%s/entries/%s' %
|
||||
(self.testdomain, self.testname))
|
||||
|
||||
@ -14,18 +14,19 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import floatingips as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
from novaclient.v1_1 import floating_ip_pools
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
class TestFloatingIPPools(utils.FixturedTestCase):
|
||||
|
||||
|
||||
class TestFloatingIPPools(utils.TestCase):
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.PoolsFixture
|
||||
|
||||
def test_list_floating_ips(self):
|
||||
fl = cs.floating_ip_pools.list()
|
||||
cs.assert_called('GET', '/os-floating-ip-pools')
|
||||
fl = self.cs.floating_ip_pools.list()
|
||||
self.assert_called('GET', '/os-floating-ip-pools')
|
||||
[self.assertIsInstance(f, floating_ip_pools.FloatingIPPool)
|
||||
for f in fl]
|
||||
|
||||
@ -14,38 +14,46 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import floatingips as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
from novaclient.v1_1 import floating_ips
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
class FloatingIPsTest(utils.FixturedTestCase):
|
||||
|
||||
|
||||
class FloatingIPsTest(utils.TestCase):
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.FloatingFixture
|
||||
|
||||
def test_list_floating_ips(self):
|
||||
fl = cs.floating_ips.list()
|
||||
cs.assert_called('GET', '/os-floating-ips')
|
||||
[self.assertIsInstance(f, floating_ips.FloatingIP) for f in fl]
|
||||
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 = cs.floating_ips.list()[0]
|
||||
fl = self.cs.floating_ips.list()[0]
|
||||
fl.delete()
|
||||
cs.assert_called('DELETE', '/os-floating-ips/1')
|
||||
cs.floating_ips.delete(1)
|
||||
cs.assert_called('DELETE', '/os-floating-ips/1')
|
||||
cs.floating_ips.delete(fl)
|
||||
cs.assert_called('DELETE', '/os-floating-ips/1')
|
||||
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 = cs.floating_ips.create()
|
||||
cs.assert_called('POST', '/os-floating-ips')
|
||||
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 = cs.floating_ips.create('foo')
|
||||
cs.assert_called('POST', '/os-floating-ips')
|
||||
fl = self.cs.floating_ips.create('nova')
|
||||
self.assert_called('POST', '/os-floating-ips')
|
||||
self.assertEqual(fl.pool, 'nova')
|
||||
self.assertIsInstance(fl, floating_ips.FloatingIP)
|
||||
|
||||
@ -13,42 +13,43 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import floatingips as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
from novaclient.v1_1 import floating_ips_bulk
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
class FloatingIPsBulkTest(utils.FixturedTestCase):
|
||||
|
||||
|
||||
class FloatingIPsBulkTest(utils.TestCase):
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.BulkFixture
|
||||
|
||||
def test_list_floating_ips_bulk(self):
|
||||
fl = cs.floating_ips_bulk.list()
|
||||
cs.assert_called('GET', '/os-floating-ips-bulk')
|
||||
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 = cs.floating_ips_bulk.list('testHost')
|
||||
cs.assert_called('GET', '/os-floating-ips-bulk/testHost')
|
||||
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 = cs.floating_ips_bulk.create('192.168.1.0/30')
|
||||
fl = self.cs.floating_ips_bulk.create('192.168.1.0/30')
|
||||
body = {'floating_ips_bulk_create': {'ip_range': '192.168.1.0/30'}}
|
||||
cs.assert_called('POST', '/os-floating-ips-bulk', body)
|
||||
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 = cs.floating_ips_bulk.create('192.168.1.0/30', 'poolTest',
|
||||
'interfaceTest')
|
||||
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'}}
|
||||
cs.assert_called('POST', '/os-floating-ips-bulk', body)
|
||||
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,
|
||||
@ -57,7 +58,7 @@ class FloatingIPsBulkTest(utils.TestCase):
|
||||
body['floating_ips_bulk_create']['interface'])
|
||||
|
||||
def test_delete_floating_ips_bulk(self):
|
||||
fl = cs.floating_ips_bulk.delete('192.168.1.0/30')
|
||||
fl = self.cs.floating_ips_bulk.delete('192.168.1.0/30')
|
||||
body = {'ip_range': '192.168.1.0/30'}
|
||||
cs.assert_called('PUT', '/os-floating-ips-bulk/delete', body)
|
||||
self.assert_called('PUT', '/os-floating-ips-bulk/delete', body)
|
||||
self.assertEqual(fl.floating_ips_bulk_delete, body['ip_range'])
|
||||
|
||||
@ -13,49 +13,50 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import fping as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
from novaclient.v1_1 import fping
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
class FpingTest(utils.FixturedTestCase):
|
||||
|
||||
|
||||
class FpingTest(utils.TestCase):
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.Fixture
|
||||
|
||||
def test_fping_repr(self):
|
||||
r = cs.fping.get(1)
|
||||
r = self.cs.fping.get(1)
|
||||
self.assertEqual(repr(r), "<Fping: 1>")
|
||||
|
||||
def test_list_fpings(self):
|
||||
fl = cs.fping.list()
|
||||
cs.assert_called('GET', '/os-fping')
|
||||
fl = self.cs.fping.list()
|
||||
self.assert_called('GET', '/os-fping')
|
||||
for f in fl:
|
||||
self.assertIsInstance(f, fping.Fping)
|
||||
self.assertEqual(f.project_id, "fake-project")
|
||||
self.assertEqual(f.alive, True)
|
||||
|
||||
def test_list_fpings_all_tenants(self):
|
||||
fl = cs.fping.list(all_tenants=True)
|
||||
fl = self.cs.fping.list(all_tenants=True)
|
||||
for f in fl:
|
||||
self.assertIsInstance(f, fping.Fping)
|
||||
cs.assert_called('GET', '/os-fping?all_tenants=1')
|
||||
self.assert_called('GET', '/os-fping?all_tenants=1')
|
||||
|
||||
def test_list_fpings_exclude(self):
|
||||
fl = cs.fping.list(exclude=['1'])
|
||||
fl = self.cs.fping.list(exclude=['1'])
|
||||
for f in fl:
|
||||
self.assertIsInstance(f, fping.Fping)
|
||||
cs.assert_called('GET', '/os-fping?exclude=1')
|
||||
self.assert_called('GET', '/os-fping?exclude=1')
|
||||
|
||||
def test_list_fpings_include(self):
|
||||
fl = cs.fping.list(include=['1'])
|
||||
fl = self.cs.fping.list(include=['1'])
|
||||
for f in fl:
|
||||
self.assertIsInstance(f, fping.Fping)
|
||||
cs.assert_called('GET', '/os-fping?include=1')
|
||||
self.assert_called('GET', '/os-fping?include=1')
|
||||
|
||||
def test_get_fping(self):
|
||||
f = cs.fping.get(1)
|
||||
cs.assert_called('GET', '/os-fping/1')
|
||||
f = self.cs.fping.get(1)
|
||||
self.assert_called('GET', '/os-fping/1')
|
||||
self.assertIsInstance(f, fping.Fping)
|
||||
self.assertEqual(f.project_id, "fake-project")
|
||||
self.assertEqual(f.alive, True)
|
||||
|
||||
@ -11,69 +11,70 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import hosts as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
from novaclient.v1_1 import hosts
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
class HostsTest(utils.FixturedTestCase):
|
||||
|
||||
|
||||
class HostsTest(utils.TestCase):
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.V1
|
||||
|
||||
def test_describe_resource(self):
|
||||
hs = cs.hosts.get('host')
|
||||
cs.assert_called('GET', '/os-hosts/host')
|
||||
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 = cs.hosts.list()
|
||||
cs.assert_called('GET', '/os-hosts')
|
||||
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 = cs.hosts.list('nova')
|
||||
cs.assert_called('GET', '/os-hosts?zone=nova')
|
||||
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 = cs.hosts.get('sample_host')[0]
|
||||
host = self.cs.hosts.get('sample_host')[0]
|
||||
values = {"status": "enabled"}
|
||||
result = host.update(values)
|
||||
cs.assert_called('PUT', '/os-hosts/sample_host', values)
|
||||
self.assert_called('PUT', '/os-hosts/sample_host', values)
|
||||
self.assertIsInstance(result, hosts.Host)
|
||||
|
||||
def test_update_maintenance(self):
|
||||
host = cs.hosts.get('sample_host')[0]
|
||||
host = self.cs.hosts.get('sample_host')[0]
|
||||
values = {"maintenance_mode": "enable"}
|
||||
result = host.update(values)
|
||||
cs.assert_called('PUT', '/os-hosts/sample_host', values)
|
||||
self.assert_called('PUT', '/os-hosts/sample_host', values)
|
||||
self.assertIsInstance(result, hosts.Host)
|
||||
|
||||
def test_update_both(self):
|
||||
host = cs.hosts.get('sample_host')[0]
|
||||
host = self.cs.hosts.get('sample_host')[0]
|
||||
values = {"status": "enabled",
|
||||
"maintenance_mode": "enable"}
|
||||
result = host.update(values)
|
||||
cs.assert_called('PUT', '/os-hosts/sample_host', values)
|
||||
self.assert_called('PUT', '/os-hosts/sample_host', values)
|
||||
self.assertIsInstance(result, hosts.Host)
|
||||
|
||||
def test_host_startup(self):
|
||||
host = cs.hosts.get('sample_host')[0]
|
||||
result = host.startup()
|
||||
cs.assert_called(
|
||||
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 = cs.hosts.get('sample_host')[0]
|
||||
result = host.reboot()
|
||||
cs.assert_called(
|
||||
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 = cs.hosts.get('sample_host')[0]
|
||||
result = host.shutdown()
|
||||
cs.assert_called(
|
||||
host = self.cs.hosts.get('sample_host')[0]
|
||||
host.shutdown()
|
||||
self.assert_called(
|
||||
'GET', '/os-hosts/sample_host/shutdown')
|
||||
|
||||
@ -13,17 +13,15 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import hypervisors as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
|
||||
|
||||
class HypervisorsTest(utils.TestCase):
|
||||
def setUp(self):
|
||||
super(HypervisorsTest, self).setUp()
|
||||
self.cs = self._get_fake_client()
|
||||
class HypervisorsTest(utils.FixturedTestCase):
|
||||
|
||||
def _get_fake_client(self):
|
||||
return fakes.FakeClient()
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.V1
|
||||
|
||||
def compare_to_expected(self, expected, hyper):
|
||||
for key, value in expected.items():
|
||||
@ -36,7 +34,7 @@ class HypervisorsTest(utils.TestCase):
|
||||
]
|
||||
|
||||
result = self.cs.hypervisors.list(False)
|
||||
self.cs.assert_called('GET', '/os-hypervisors')
|
||||
self.assert_called('GET', '/os-hypervisors')
|
||||
|
||||
for idx, hyper in enumerate(result):
|
||||
self.compare_to_expected(expected[idx], hyper)
|
||||
@ -79,7 +77,7 @@ class HypervisorsTest(utils.TestCase):
|
||||
disk_available_least=100)]
|
||||
|
||||
result = self.cs.hypervisors.list()
|
||||
self.cs.assert_called('GET', '/os-hypervisors/detail')
|
||||
self.assert_called('GET', '/os-hypervisors/detail')
|
||||
|
||||
for idx, hyper in enumerate(result):
|
||||
self.compare_to_expected(expected[idx], hyper)
|
||||
@ -91,7 +89,7 @@ class HypervisorsTest(utils.TestCase):
|
||||
]
|
||||
|
||||
result = self.cs.hypervisors.search('hyper')
|
||||
self.cs.assert_called('GET', '/os-hypervisors/hyper/search')
|
||||
self.assert_called('GET', '/os-hypervisors/hyper/search')
|
||||
|
||||
for idx, hyper in enumerate(result):
|
||||
self.compare_to_expected(expected[idx], hyper)
|
||||
@ -111,7 +109,7 @@ class HypervisorsTest(utils.TestCase):
|
||||
]
|
||||
|
||||
result = self.cs.hypervisors.search('hyper', True)
|
||||
self.cs.assert_called('GET', '/os-hypervisors/hyper/servers')
|
||||
self.assert_called('GET', '/os-hypervisors/hyper/servers')
|
||||
|
||||
for idx, hyper in enumerate(result):
|
||||
self.compare_to_expected(expected[idx], hyper)
|
||||
@ -137,7 +135,7 @@ class HypervisorsTest(utils.TestCase):
|
||||
disk_available_least=100)
|
||||
|
||||
result = self.cs.hypervisors.get(1234)
|
||||
self.cs.assert_called('GET', '/os-hypervisors/1234')
|
||||
self.assert_called('GET', '/os-hypervisors/1234')
|
||||
|
||||
self.compare_to_expected(expected, result)
|
||||
|
||||
@ -148,7 +146,7 @@ class HypervisorsTest(utils.TestCase):
|
||||
uptime="fake uptime")
|
||||
|
||||
result = self.cs.hypervisors.uptime(1234)
|
||||
self.cs.assert_called('GET', '/os-hypervisors/1234/uptime')
|
||||
self.assert_called('GET', '/os-hypervisors/1234/uptime')
|
||||
|
||||
self.compare_to_expected(expected, result)
|
||||
|
||||
@ -169,6 +167,6 @@ class HypervisorsTest(utils.TestCase):
|
||||
)
|
||||
|
||||
result = self.cs.hypervisors.statistics()
|
||||
self.cs.assert_called('GET', '/os-hypervisors/statistics')
|
||||
self.assert_called('GET', '/os-hypervisors/statistics')
|
||||
|
||||
self.compare_to_expected(expected, result)
|
||||
|
||||
@ -11,56 +11,56 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import images as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
from novaclient.v1_1 import images
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
class ImagesTest(utils.FixturedTestCase):
|
||||
|
||||
|
||||
class ImagesTest(utils.TestCase):
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.V1
|
||||
|
||||
def test_list_images(self):
|
||||
il = cs.images.list()
|
||||
cs.assert_called('GET', '/images/detail')
|
||||
il = self.cs.images.list()
|
||||
self.assert_called('GET', '/images/detail')
|
||||
[self.assertIsInstance(i, images.Image) for i in il]
|
||||
|
||||
def test_list_images_undetailed(self):
|
||||
il = cs.images.list(detailed=False)
|
||||
cs.assert_called('GET', '/images')
|
||||
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):
|
||||
il = cs.images.list(limit=4)
|
||||
cs.assert_called('GET', '/images/detail?limit=4')
|
||||
self.cs.images.list(limit=4)
|
||||
self.assert_called('GET', '/images/detail?limit=4')
|
||||
|
||||
def test_get_image_details(self):
|
||||
i = cs.images.get(1)
|
||||
cs.assert_called('GET', '/images/1')
|
||||
i = self.cs.images.get(1)
|
||||
self.assert_called('GET', '/images/1')
|
||||
self.assertIsInstance(i, images.Image)
|
||||
self.assertEqual(i.id, 1)
|
||||
self.assertEqual(i.name, 'CentOS 5.2')
|
||||
|
||||
def test_delete_image(self):
|
||||
cs.images.delete(1)
|
||||
cs.assert_called('DELETE', '/images/1')
|
||||
self.cs.images.delete(1)
|
||||
self.assert_called('DELETE', '/images/1')
|
||||
|
||||
def test_delete_meta(self):
|
||||
cs.images.delete_meta(1, {'test_key': 'test_value'})
|
||||
cs.assert_called('DELETE', '/images/1/metadata/test_key')
|
||||
self.cs.images.delete_meta(1, {'test_key': 'test_value'})
|
||||
self.assert_called('DELETE', '/images/1/metadata/test_key')
|
||||
|
||||
def test_set_meta(self):
|
||||
cs.images.set_meta(1, {'test_key': 'test_value'})
|
||||
cs.assert_called('POST', '/images/1/metadata',
|
||||
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 = cs.images.find(name="CentOS 5.2")
|
||||
i = self.cs.images.find(name="CentOS 5.2")
|
||||
self.assertEqual(i.id, 1)
|
||||
cs.assert_called('GET', '/images', pos=-2)
|
||||
cs.assert_called('GET', '/images/1', pos=-1)
|
||||
self.assert_called('GET', '/images/1')
|
||||
|
||||
iml = cs.images.findall(status='SAVING')
|
||||
iml = self.cs.images.findall(status='SAVING')
|
||||
self.assertEqual(len(iml), 1)
|
||||
self.assertEqual(iml[0].name, 'My Server Backup')
|
||||
|
||||
@ -11,50 +11,54 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import keypairs as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
from novaclient.v1_1 import keypairs
|
||||
|
||||
|
||||
class KeypairsTest(utils.TestCase):
|
||||
class KeypairsTest(utils.FixturedTestCase):
|
||||
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.V1
|
||||
|
||||
def setUp(self):
|
||||
super(KeypairsTest, self).setUp()
|
||||
self.cs = self._get_fake_client()
|
||||
self.keypair_type = self._get_keypair_type()
|
||||
self.keypair_prefix = keypairs.KeypairManager.keypair_prefix
|
||||
|
||||
def _get_fake_client(self):
|
||||
return fakes.FakeClient()
|
||||
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.cs.assert_called('GET', '/%s/test' % self.keypair_prefix)
|
||||
self.assert_called('GET', '/%s/test' % self.keypair_prefix)
|
||||
self.assertIsInstance(kp, keypairs.Keypair)
|
||||
self.assertEqual(kp.name, 'test')
|
||||
|
||||
def test_list_keypairs(self):
|
||||
kps = self.cs.keypairs.list()
|
||||
self.cs.assert_called('GET', '/%s' % self.keypair_prefix)
|
||||
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.cs.assert_called('DELETE', '/%s/test' % self.keypair_prefix)
|
||||
self.assert_called('DELETE', '/%s/test' % self.keypair_prefix)
|
||||
self.cs.keypairs.delete('test')
|
||||
self.cs.assert_called('DELETE', '/%s/test' % self.keypair_prefix)
|
||||
self.assert_called('DELETE', '/%s/test' % self.keypair_prefix)
|
||||
self.cs.keypairs.delete(kp)
|
||||
self.cs.assert_called('DELETE', '/%s/test' % self.keypair_prefix)
|
||||
self.assert_called('DELETE', '/%s/test' % self.keypair_prefix)
|
||||
|
||||
def test_create_keypair(self):
|
||||
kp = self.cs.keypairs.create("foo")
|
||||
self.cs.assert_called('POST', '/%s' % self.keypair_prefix)
|
||||
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.cs.assert_called('POST', '/%s' % self.keypair_prefix)
|
||||
self.assert_called('POST', '/%s' % self.keypair_prefix)
|
||||
self.assertIsInstance(kp, keypairs.Keypair)
|
||||
|
||||
@ -11,28 +11,29 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import limits as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
from novaclient.v1_1 import limits
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
class LimitsTest(utils.FixturedTestCase):
|
||||
|
||||
|
||||
class LimitsTest(utils.TestCase):
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.Fixture
|
||||
|
||||
def test_get_limits(self):
|
||||
obj = cs.limits.get()
|
||||
cs.assert_called('GET', '/limits')
|
||||
obj = self.cs.limits.get()
|
||||
self.assert_called('GET', '/limits')
|
||||
self.assertIsInstance(obj, limits.Limits)
|
||||
|
||||
def test_get_limits_for_a_tenant(self):
|
||||
obj = cs.limits.get(tenant_id=1234)
|
||||
cs.assert_called('GET', '/limits?tenant_id=1234')
|
||||
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 = cs.limits.get()
|
||||
obj = self.cs.limits.get()
|
||||
|
||||
expected = (
|
||||
limits.AbsoluteLimit("maxTotalRAMSize", 51200),
|
||||
@ -49,7 +50,7 @@ class LimitsTest(utils.TestCase):
|
||||
self.assertTrue(limit in expected)
|
||||
|
||||
def test_absolute_limits_reserved(self):
|
||||
obj = cs.limits.get(reserved=True)
|
||||
obj = self.cs.limits.get(reserved=True)
|
||||
|
||||
expected = (
|
||||
limits.AbsoluteLimit("maxTotalRAMSize", 51200),
|
||||
@ -59,7 +60,7 @@ class LimitsTest(utils.TestCase):
|
||||
limits.AbsoluteLimit("maxPersonalitySize", 10240),
|
||||
)
|
||||
|
||||
cs.assert_called('GET', '/limits?reserved=1')
|
||||
self.assert_called('GET', '/limits?reserved=1')
|
||||
abs_limits = list(obj.absolute)
|
||||
self.assertEqual(len(abs_limits), len(expected))
|
||||
|
||||
@ -67,7 +68,7 @@ class LimitsTest(utils.TestCase):
|
||||
self.assertTrue(limit in expected)
|
||||
|
||||
def test_rate_limits(self):
|
||||
obj = cs.limits.get()
|
||||
obj = self.cs.limits.get()
|
||||
|
||||
expected = (
|
||||
limits.RateLimit('POST', '*', '.*', 10, 2, 'MINUTE',
|
||||
|
||||
@ -11,34 +11,35 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import networks as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
from novaclient.v1_1 import networks
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
class NetworksTest(utils.FixturedTestCase):
|
||||
|
||||
|
||||
class NetworksTest(utils.TestCase):
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.Fixture
|
||||
|
||||
def test_list_networks(self):
|
||||
fl = cs.networks.list()
|
||||
cs.assert_called('GET', '/os-networks')
|
||||
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 = cs.networks.get(1)
|
||||
cs.assert_called('GET', '/os-networks/1')
|
||||
f = self.cs.networks.get(1)
|
||||
self.assert_called('GET', '/os-networks/1')
|
||||
self.assertIsInstance(f, networks.Network)
|
||||
|
||||
def test_delete(self):
|
||||
cs.networks.delete('networkdelete')
|
||||
cs.assert_called('DELETE', '/os-networks/networkdelete')
|
||||
self.cs.networks.delete('networkdelete')
|
||||
self.assert_called('DELETE', '/os-networks/networkdelete')
|
||||
|
||||
def test_create(self):
|
||||
f = cs.networks.create(label='foo')
|
||||
cs.assert_called('POST', '/os-networks',
|
||||
{'network': {'label': 'foo'}})
|
||||
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):
|
||||
@ -56,43 +57,44 @@ class NetworksTest(utils.TestCase):
|
||||
'multi_host': 'T',
|
||||
'priority': '1',
|
||||
'project_id': '1',
|
||||
'vlan': 5,
|
||||
'vlan_start': 1,
|
||||
'vpn_start': 1
|
||||
}
|
||||
|
||||
f = cs.networks.create(**params)
|
||||
cs.assert_called('POST', '/os-networks', {'network': params})
|
||||
f = self.cs.networks.create(**params)
|
||||
self.assert_called('POST', '/os-networks', {'network': params})
|
||||
self.assertIsInstance(f, networks.Network)
|
||||
|
||||
def test_associate_project(self):
|
||||
cs.networks.associate_project('networktest')
|
||||
cs.assert_called('POST', '/os-networks/add',
|
||||
{'id': 'networktest'})
|
||||
self.cs.networks.associate_project('networktest')
|
||||
self.assert_called('POST', '/os-networks/add',
|
||||
{'id': 'networktest'})
|
||||
|
||||
def test_associate_host(self):
|
||||
cs.networks.associate_host('networktest', 'testHost')
|
||||
cs.assert_called('POST', '/os-networks/networktest/action',
|
||||
{'associate_host': 'testHost'})
|
||||
self.cs.networks.associate_host('networktest', 'testHost')
|
||||
self.assert_called('POST', '/os-networks/networktest/action',
|
||||
{'associate_host': 'testHost'})
|
||||
|
||||
def test_disassociate(self):
|
||||
cs.networks.disassociate('networkdisassociate')
|
||||
cs.assert_called('POST',
|
||||
'/os-networks/networkdisassociate/action',
|
||||
{'disassociate': None})
|
||||
self.cs.networks.disassociate('networkdisassociate')
|
||||
self.assert_called('POST',
|
||||
'/os-networks/networkdisassociate/action',
|
||||
{'disassociate': None})
|
||||
|
||||
def test_disassociate_host_only(self):
|
||||
cs.networks.disassociate('networkdisassociate', True, False)
|
||||
cs.assert_called('POST',
|
||||
'/os-networks/networkdisassociate/action',
|
||||
{'disassociate_host': None})
|
||||
self.cs.networks.disassociate('networkdisassociate', True, False)
|
||||
self.assert_called('POST',
|
||||
'/os-networks/networkdisassociate/action',
|
||||
{'disassociate_host': None})
|
||||
|
||||
def test_disassociate_project(self):
|
||||
cs.networks.disassociate('networkdisassociate', False, True)
|
||||
cs.assert_called('POST',
|
||||
'/os-networks/networkdisassociate/action',
|
||||
{'disassociate_project': None})
|
||||
self.cs.networks.disassociate('networkdisassociate', False, True)
|
||||
self.assert_called('POST',
|
||||
'/os-networks/networkdisassociate/action',
|
||||
{'disassociate_project': None})
|
||||
|
||||
def test_add(self):
|
||||
cs.networks.add('networkadd')
|
||||
cs.assert_called('POST', '/os-networks/add',
|
||||
{'id': 'networkadd'})
|
||||
self.cs.networks.add('networkadd')
|
||||
self.assert_called('POST', '/os-networks/add',
|
||||
{'id': 'networkadd'})
|
||||
|
||||
@ -13,39 +13,37 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import quotas as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
|
||||
|
||||
class QuotaSetsTest(utils.TestCase):
|
||||
def setUp(self):
|
||||
super(QuotaSetsTest, self).setUp()
|
||||
self.cs = self._get_fake_client()
|
||||
class QuotaSetsTest(utils.FixturedTestCase):
|
||||
|
||||
def _get_fake_client(self):
|
||||
return fakes.FakeClient()
|
||||
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.cs.assert_called('GET', '/os-quota-sets/%s' % 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.cs.assert_called('GET', url)
|
||||
self.assert_called('GET', url)
|
||||
|
||||
def test_tenant_quotas_defaults(self):
|
||||
tenant_id = '97f4c221bff44578b0300df4ef119353'
|
||||
self.cs.quotas.defaults(tenant_id)
|
||||
self.cs.assert_called('GET', '/os-quota-sets/%s/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.cs.assert_called(
|
||||
self.assert_called(
|
||||
'PUT', '/os-quota-sets/97f4c221bff44578b0300df4ef119353',
|
||||
{'quota_set': {'force': True,
|
||||
'cores': 2,
|
||||
@ -54,11 +52,11 @@ class QuotaSetsTest(utils.TestCase):
|
||||
def test_quotas_delete(self):
|
||||
tenant_id = 'test'
|
||||
self.cs.quotas.delete(tenant_id)
|
||||
self.cs.assert_called('DELETE', '/os-quota-sets/%s' % 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.cs.assert_called('DELETE', url)
|
||||
self.assert_called('DELETE', url)
|
||||
|
||||
@ -12,21 +12,24 @@
|
||||
# under the License.
|
||||
|
||||
from novaclient import exceptions
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import security_group_rules as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
from novaclient.v1_1 import security_group_rules
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
class SecurityGroupRulesTest(utils.FixturedTestCase):
|
||||
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.Fixture
|
||||
|
||||
class SecurityGroupRulesTest(utils.TestCase):
|
||||
def test_delete_security_group_rule(self):
|
||||
cs.security_group_rules.delete(1)
|
||||
cs.assert_called('DELETE', '/os-security-group-rules/1')
|
||||
self.cs.security_group_rules.delete(1)
|
||||
self.assert_called('DELETE', '/os-security-group-rules/1')
|
||||
|
||||
def test_create_security_group_rule(self):
|
||||
sg = cs.security_group_rules.create(1, "tcp", 1, 65535, "10.0.0.0/16")
|
||||
sg = self.cs.security_group_rules.create(1, "tcp", 1, 65535,
|
||||
"10.0.0.0/16")
|
||||
|
||||
body = {
|
||||
"security_group_rule": {
|
||||
@ -39,12 +42,12 @@ class SecurityGroupRulesTest(utils.TestCase):
|
||||
}
|
||||
}
|
||||
|
||||
cs.assert_called('POST', '/os-security-group-rules', body)
|
||||
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 = cs.security_group_rules.create(1, "tcp", 1, 65535, "10.0.0.0/16",
|
||||
101)
|
||||
sg = self.cs.security_group_rules.create(1, "tcp", 1, 65535,
|
||||
"10.0.0.0/16", 101)
|
||||
|
||||
body = {
|
||||
"security_group_rule": {
|
||||
@ -57,25 +60,27 @@ class SecurityGroupRulesTest(utils.TestCase):
|
||||
}
|
||||
}
|
||||
|
||||
cs.assert_called('POST', '/os-security-group-rules', body)
|
||||
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,
|
||||
cs.security_group_rules.create,
|
||||
self.cs.security_group_rules.create,
|
||||
1, "invalid_ip_protocol", 1, 65535, "10.0.0.0/16", 101)
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
cs.security_group_rules.create,
|
||||
self.cs.security_group_rules.create,
|
||||
1, "tcp", "invalid_from_port", 65535, "10.0.0.0/16", 101)
|
||||
self.assertRaises(exceptions.CommandError,
|
||||
cs.security_group_rules.create,
|
||||
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 = cs.security_group_rules.create(1, "tcp", 1, 65535, "10.0.0.0/16")
|
||||
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 = cs.security_group_rules.create(1, "tcp", 1, 65535, "10.0.0.0/16")
|
||||
sg = self.cs.security_group_rules.create(1, "tcp", 1, 65535,
|
||||
"10.0.0.0/16")
|
||||
sg.delete()
|
||||
cs.assert_called('DELETE', '/os-security-group-rules/1')
|
||||
self.assert_called('DELETE', '/os-security-group-rules/1')
|
||||
|
||||
@ -11,18 +11,20 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import security_groups as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
from novaclient.v1_1 import security_groups
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
class SecurityGroupsTest(utils.FixturedTestCase):
|
||||
|
||||
client_fixture_class = client.V1
|
||||
data_fixture_class = data.Fixture
|
||||
|
||||
class SecurityGroupsTest(utils.TestCase):
|
||||
def _do_test_list_security_groups(self, search_opts, path):
|
||||
sgs = cs.security_groups.list(search_opts=search_opts)
|
||||
cs.assert_called('GET', 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)
|
||||
|
||||
@ -39,34 +41,34 @@ class SecurityGroupsTest(utils.TestCase):
|
||||
{'all_tenants': 0}, '/os-security-groups')
|
||||
|
||||
def test_get_security_groups(self):
|
||||
sg = cs.security_groups.get(1)
|
||||
cs.assert_called('GET', '/os-security-groups/1')
|
||||
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 = cs.security_groups.list()[0]
|
||||
sg = self.cs.security_groups.list()[0]
|
||||
sg.delete()
|
||||
cs.assert_called('DELETE', '/os-security-groups/1')
|
||||
cs.security_groups.delete(1)
|
||||
cs.assert_called('DELETE', '/os-security-groups/1')
|
||||
cs.security_groups.delete(sg)
|
||||
cs.assert_called('DELETE', '/os-security-groups/1')
|
||||
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 = cs.security_groups.create("foo", "foo barr")
|
||||
cs.assert_called('POST', '/os-security-groups')
|
||||
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 = cs.security_groups.list()[0]
|
||||
secgroup = cs.security_groups.update(sg, "update", "update")
|
||||
cs.assert_called('PUT', '/os-security-groups/1')
|
||||
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 = cs.security_groups.get(1)
|
||||
sg2 = cs.security_groups.get(1)
|
||||
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)
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
# 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.fixture_data import client
|
||||
from novaclient.tests.fixture_data import server_groups as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.v1_1 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)
|
||||
@ -104,6 +104,28 @@ class ServersTest(utils.TestCase):
|
||||
|
||||
test_create_server_from_volume()
|
||||
|
||||
def test_create_server_boot_with_nics_ipv6(self):
|
||||
old_boot = 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(cs.servers, '_boot', wrapped_boot):
|
||||
s = cs.servers.create(
|
||||
name="My server",
|
||||
image=1,
|
||||
flavor=1,
|
||||
meta={'foo': 'bar'},
|
||||
userdata="hello moto",
|
||||
key_name="fakekey",
|
||||
nics=nics
|
||||
)
|
||||
cs.assert_called('POST', '/servers')
|
||||
self.assertIsInstance(s, servers.Server)
|
||||
|
||||
def test_create_server_userdata_file_object(self):
|
||||
s = cs.servers.create(
|
||||
name="My server",
|
||||
@ -200,17 +222,17 @@ class ServersTest(utils.TestCase):
|
||||
cs.assert_called('DELETE', '/servers/1234')
|
||||
|
||||
def test_delete_server_meta(self):
|
||||
s = cs.servers.delete_meta(1234, ['test_key'])
|
||||
cs.servers.delete_meta(1234, ['test_key'])
|
||||
cs.assert_called('DELETE', '/servers/1234/metadata/test_key')
|
||||
|
||||
def test_set_server_meta(self):
|
||||
s = cs.servers.set_meta(1234, {'test_key': 'test_value'})
|
||||
reval = cs.assert_called('POST', '/servers/1234/metadata',
|
||||
cs.servers.set_meta(1234, {'test_key': 'test_value'})
|
||||
cs.assert_called('POST', '/servers/1234/metadata',
|
||||
{'metadata': {'test_key': 'test_value'}})
|
||||
|
||||
def test_set_server_meta_item(self):
|
||||
s = cs.servers.set_meta_item(1234, 'test_key', 'test_value')
|
||||
reval = cs.assert_called('PUT', '/servers/1234/metadata/test_key',
|
||||
cs.servers.set_meta_item(1234, 'test_key', 'test_value')
|
||||
cs.assert_called('PUT', '/servers/1234/metadata/test_key',
|
||||
{'meta': {'test_key': 'test_value'}})
|
||||
|
||||
def test_find(self):
|
||||
@ -582,6 +604,32 @@ class ServersTest(utils.TestCase):
|
||||
s.interface_list()
|
||||
cs.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 = cs.servers.get(1234)
|
||||
s.interface_attach(None, None, None)
|
||||
|
||||
@ -78,6 +78,10 @@ class ServicesTest(utils.TestCase):
|
||||
self.assertIsInstance(service, self._get_service_type())
|
||||
self.assertEqual(service.status, 'enabled')
|
||||
|
||||
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")
|
||||
|
||||
@ -23,6 +23,7 @@ import os
|
||||
import fixtures
|
||||
import mock
|
||||
import six
|
||||
from six.moves import builtins
|
||||
|
||||
import novaclient.client
|
||||
from novaclient import exceptions
|
||||
@ -128,20 +129,6 @@ class ShellTest(utils.TestCase):
|
||||
}},
|
||||
)
|
||||
|
||||
def test_boot_multiple(self):
|
||||
self.run_command('boot --flavor 1 --image 1'
|
||||
' --num-instances 3 some-server')
|
||||
self.assert_called_anytime(
|
||||
'POST', '/servers',
|
||||
{'server': {
|
||||
'flavorRef': '1',
|
||||
'name': 'some-server',
|
||||
'imageRef': '1',
|
||||
'min_count': 1,
|
||||
'max_count': 3,
|
||||
}},
|
||||
)
|
||||
|
||||
def test_boot_image_with(self):
|
||||
self.run_command("boot --flavor 1"
|
||||
" --image-with test_key=test_value some-server")
|
||||
@ -172,8 +159,8 @@ class ShellTest(utils.TestCase):
|
||||
|
||||
def test_boot_user_data(self):
|
||||
testfile = os.path.join(os.path.dirname(__file__), 'testfile.txt')
|
||||
data = open(testfile).read()
|
||||
expected_file_data = base64.b64encode(data.encode('utf-8'))
|
||||
data = open(testfile).read().encode('utf-8')
|
||||
expected_file_data = base64.b64encode(data).decode('utf-8')
|
||||
self.run_command(
|
||||
'boot --flavor 1 --image 1 --user_data %s some-server' % testfile)
|
||||
self.assert_called_anytime(
|
||||
@ -497,7 +484,33 @@ class ShellTest(utils.TestCase):
|
||||
},
|
||||
)
|
||||
|
||||
def tets_boot_nics_no_value(self):
|
||||
def test_boot_nics_ipv6(self):
|
||||
cmd = ('boot --image 1 --flavor 1 '
|
||||
'--nic net-id=a=c,v6-fixed-ip=2001:db9:0:1::10 some-server')
|
||||
self.run_command(cmd)
|
||||
self.assert_called_anytime(
|
||||
'POST', '/servers',
|
||||
{
|
||||
'server': {
|
||||
'flavorRef': '1',
|
||||
'name': 'some-server',
|
||||
'imageRef': '1',
|
||||
'min_count': 1,
|
||||
'max_count': 1,
|
||||
'networks': [
|
||||
{'uuid': 'a=c', 'fixed_ip': '2001:db9:0:1::10'},
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
def test_boot_nics_both_ipv4_and_ipv6(self):
|
||||
cmd = ('boot --image 1 --flavor 1 '
|
||||
'--nic net-id=a=c,v4-fixed-ip=10.0.0.1,'
|
||||
'v6-fixed-ip=2001:db9:0:1::10 some-server')
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
|
||||
def test_boot_nics_no_value(self):
|
||||
cmd = ('boot --image 1 --flavor 1 '
|
||||
'--nic net-id some-server')
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
@ -512,10 +525,15 @@ class ShellTest(utils.TestCase):
|
||||
'--nic v4-fixed-ip=10.0.0.1 some-server')
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
|
||||
def test_boot_nics_netid_and_portid(self):
|
||||
cmd = ('boot --image 1 --flavor 1 '
|
||||
'--nic port-id=some=port,net-id=some=net some-server')
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
|
||||
def test_boot_files(self):
|
||||
testfile = os.path.join(os.path.dirname(__file__), 'testfile.txt')
|
||||
data = open(testfile).read()
|
||||
expected_file_data = base64.b64encode(data.encode('utf-8'))
|
||||
expected = base64.b64encode(data.encode('utf-8')).decode('utf-8')
|
||||
|
||||
cmd = ('boot some-server --flavor 1 --image 1'
|
||||
' --file /tmp/foo=%s --file /tmp/bar=%s')
|
||||
@ -530,8 +548,8 @@ class ShellTest(utils.TestCase):
|
||||
'min_count': 1,
|
||||
'max_count': 1,
|
||||
'personality': [
|
||||
{'path': '/tmp/bar', 'contents': expected_file_data},
|
||||
{'path': '/tmp/foo', 'contents': expected_file_data},
|
||||
{'path': '/tmp/bar', 'contents': expected},
|
||||
{'path': '/tmp/foo', 'contents': expected},
|
||||
]
|
||||
}},
|
||||
)
|
||||
@ -558,11 +576,69 @@ class ShellTest(utils.TestCase):
|
||||
})
|
||||
|
||||
def test_boot_invalid_num_instances(self):
|
||||
cmd = 'boot --image 1 --flavor 1 --num-instances 1 server'
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
cmd = 'boot --image 1 --flavor 1 --num-instances 0 server'
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
|
||||
def test_boot_num_instances_and_count(self):
|
||||
cmd = 'boot --image 1 --flavor 1 --num-instances 3 --min-count 3 serv'
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
cmd = 'boot --image 1 --flavor 1 --num-instances 3 --max-count 3 serv'
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
|
||||
def test_boot_min_max_count(self):
|
||||
self.run_command('boot --image 1 --flavor 1 --max-count 3 server')
|
||||
self.assert_called_anytime(
|
||||
'POST', '/servers',
|
||||
{
|
||||
'server': {
|
||||
'flavorRef': '1',
|
||||
'name': 'server',
|
||||
'imageRef': '1',
|
||||
'min_count': 1,
|
||||
'max_count': 3,
|
||||
}
|
||||
})
|
||||
self.run_command('boot --image 1 --flavor 1 --min-count 3 server')
|
||||
self.assert_called_anytime(
|
||||
'POST', '/servers',
|
||||
{
|
||||
'server': {
|
||||
'flavorRef': '1',
|
||||
'name': 'server',
|
||||
'imageRef': '1',
|
||||
'min_count': 3,
|
||||
'max_count': 3,
|
||||
}
|
||||
})
|
||||
self.run_command('boot --image 1 --flavor 1 '
|
||||
'--min-count 3 --max-count 3 server')
|
||||
self.assert_called_anytime(
|
||||
'POST', '/servers',
|
||||
{
|
||||
'server': {
|
||||
'flavorRef': '1',
|
||||
'name': 'server',
|
||||
'imageRef': '1',
|
||||
'min_count': 3,
|
||||
'max_count': 3,
|
||||
}
|
||||
})
|
||||
self.run_command('boot --image 1 --flavor 1 '
|
||||
'--min-count 3 --max-count 5 server')
|
||||
self.assert_called_anytime(
|
||||
'POST', '/servers',
|
||||
{
|
||||
'server': {
|
||||
'flavorRef': '1',
|
||||
'name': 'server',
|
||||
'imageRef': '1',
|
||||
'min_count': 3,
|
||||
'max_count': 5,
|
||||
}
|
||||
})
|
||||
cmd = 'boot --image 1 --flavor 1 --min-count 3 --max-count 1 serv'
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
|
||||
@mock.patch('novaclient.v1_1.shell._poll_for_status')
|
||||
def test_boot_with_poll(self, poll_method):
|
||||
self.run_command('boot --flavor 1 --image 1 some-server --poll')
|
||||
@ -581,6 +657,31 @@ class ShellTest(utils.TestCase):
|
||||
[mock.call(self.shell.cs.servers.get, 1234, 'building',
|
||||
['active'])])
|
||||
|
||||
def test_boot_with_poll_to_check_VM_state_error(self):
|
||||
self.assertRaises(exceptions.InstanceInErrorState, self.run_command,
|
||||
'boot --flavor 1 --image 1 some-bad-server --poll')
|
||||
|
||||
def test_boot_named_flavor(self):
|
||||
self.run_command(["boot", "--image", "1",
|
||||
"--flavor", "512 MB Server",
|
||||
"--max-count", "3", "server"])
|
||||
self.assert_called('GET', '/images/1', pos=0)
|
||||
self.assert_called('GET', '/flavors/512 MB Server', pos=1)
|
||||
self.assert_called('GET', '/flavors?is_public=None', pos=2)
|
||||
self.assert_called('GET', '/flavors?is_public=None', pos=3)
|
||||
self.assert_called('GET', '/flavors/2', pos=4)
|
||||
self.assert_called(
|
||||
'POST', '/servers',
|
||||
{
|
||||
'server': {
|
||||
'flavorRef': '2',
|
||||
'name': 'server',
|
||||
'imageRef': '1',
|
||||
'min_count': 1,
|
||||
'max_count': 3,
|
||||
}
|
||||
}, pos=5)
|
||||
|
||||
def test_flavor_list(self):
|
||||
self.run_command('flavor-list')
|
||||
self.assert_called_anytime('GET', '/flavors/detail')
|
||||
@ -602,6 +703,22 @@ class ShellTest(utils.TestCase):
|
||||
self.run_command('flavor-show aa1')
|
||||
self.assert_called_anytime('GET', '/flavors/aa1')
|
||||
|
||||
def test_flavor_show_by_name(self):
|
||||
self.run_command(['flavor-show', '128 MB Server'])
|
||||
self.assert_called('GET', '/flavors/128 MB Server', pos=0)
|
||||
self.assert_called('GET', '/flavors?is_public=None', pos=1)
|
||||
self.assert_called('GET', '/flavors?is_public=None', pos=2)
|
||||
self.assert_called('GET', '/flavors/aa1', pos=3)
|
||||
self.assert_called('GET', '/flavors/aa1/os-extra_specs', pos=4)
|
||||
|
||||
def test_flavor_show_by_name_priv(self):
|
||||
self.run_command(['flavor-show', '512 MB Server'])
|
||||
self.assert_called('GET', '/flavors/512 MB Server', pos=0)
|
||||
self.assert_called('GET', '/flavors?is_public=None', pos=1)
|
||||
self.assert_called('GET', '/flavors?is_public=None', pos=2)
|
||||
self.assert_called('GET', '/flavors/2', pos=3)
|
||||
self.assert_called('GET', '/flavors/2/os-extra_specs', pos=4)
|
||||
|
||||
def test_flavor_key_set(self):
|
||||
self.run_command('flavor-key 1 set k1=v1')
|
||||
self.assert_called('POST', '/flavors/1/os-extra_specs',
|
||||
@ -735,43 +852,37 @@ class ShellTest(utils.TestCase):
|
||||
{'reboot': {'type': 'HARD'}})
|
||||
|
||||
def test_rebuild(self):
|
||||
self.run_command('rebuild sample-server 1')
|
||||
self.assert_called('GET', '/servers', pos=-8)
|
||||
self.assert_called('GET', '/servers/1234', pos=-7)
|
||||
self.assert_called('GET', '/images/1', pos=-6)
|
||||
output = self.run_command('rebuild sample-server 1')
|
||||
self.assert_called('GET', '/servers', pos=-6)
|
||||
self.assert_called('GET', '/servers/1234', pos=-5)
|
||||
self.assert_called('GET', '/images/1', pos=-4)
|
||||
self.assert_called('POST', '/servers/1234/action',
|
||||
{'rebuild': {'imageRef': 1}}, pos=-5)
|
||||
{'rebuild': {'imageRef': 1}}, pos=-3)
|
||||
self.assert_called('GET', '/flavors/1', pos=-2)
|
||||
self.assert_called('GET', '/images/2')
|
||||
self.assertIn('adminPass', output)
|
||||
|
||||
self.run_command('rebuild sample-server 1 --rebuild-password asdf')
|
||||
self.assert_called('GET', '/servers', pos=-8)
|
||||
self.assert_called('GET', '/servers/1234', pos=-7)
|
||||
self.assert_called('GET', '/images/1', pos=-6)
|
||||
def test_rebuild_password(self):
|
||||
output = self.run_command('rebuild sample-server 1'
|
||||
' --rebuild-password asdf')
|
||||
self.assert_called('GET', '/servers', pos=-6)
|
||||
self.assert_called('GET', '/servers/1234', pos=-5)
|
||||
self.assert_called('GET', '/images/1', pos=-4)
|
||||
self.assert_called('POST', '/servers/1234/action',
|
||||
{'rebuild': {'imageRef': 1, 'adminPass': 'asdf'}},
|
||||
pos=-5)
|
||||
pos=-3)
|
||||
self.assert_called('GET', '/flavors/1', pos=-2)
|
||||
self.assert_called('GET', '/images/2')
|
||||
self.assertIn('adminPass', output)
|
||||
|
||||
def test_rebuild_preserve_ephemeral(self):
|
||||
self.run_command('rebuild sample-server 1 --preserve-ephemeral')
|
||||
self.assert_called('GET', '/servers', pos=-8)
|
||||
self.assert_called('GET', '/servers/1234', pos=-7)
|
||||
self.assert_called('GET', '/images/1', pos=-6)
|
||||
self.assert_called('GET', '/servers', pos=-6)
|
||||
self.assert_called('GET', '/servers/1234', pos=-5)
|
||||
self.assert_called('GET', '/images/1', pos=-4)
|
||||
self.assert_called('POST', '/servers/1234/action',
|
||||
{'rebuild': {'imageRef': 1,
|
||||
'preserve_ephemeral': True}}, pos=-5)
|
||||
self.assert_called('GET', '/flavors/1', pos=-2)
|
||||
self.assert_called('GET', '/images/2')
|
||||
|
||||
self.run_command('rebuild sample-server 1 --rebuild-password asdf')
|
||||
self.assert_called('GET', '/servers', pos=-8)
|
||||
self.assert_called('GET', '/servers/1234', pos=-7)
|
||||
self.assert_called('GET', '/images/1', pos=-6)
|
||||
self.assert_called('POST', '/servers/1234/action',
|
||||
{'rebuild': {'imageRef': 1, 'adminPass': 'asdf'}},
|
||||
pos=-5)
|
||||
'preserve_ephemeral': True}}, pos=-3)
|
||||
self.assert_called('GET', '/flavors/1', pos=-2)
|
||||
self.assert_called('GET', '/images/2')
|
||||
|
||||
@ -1039,6 +1150,10 @@ class ShellTest(utils.TestCase):
|
||||
self.run_command('floating-ip-list')
|
||||
self.assert_called('GET', '/os-floating-ips')
|
||||
|
||||
def test_floating_ip_list_all_tenants(self):
|
||||
self.run_command('floating-ip-list --all-tenants')
|
||||
self.assert_called('GET', '/os-floating-ips?all_tenants=1')
|
||||
|
||||
def test_floating_ip_create(self):
|
||||
self.run_command('floating-ip-create')
|
||||
self.assert_called('GET', '/os-floating-ips/1')
|
||||
@ -1251,6 +1366,20 @@ class ShellTest(utils.TestCase):
|
||||
self.assert_called('POST', '/servers/1234/action',
|
||||
{'os-resetState': {'state': 'active'}})
|
||||
|
||||
def test_reset_state_multiple(self):
|
||||
self.run_command('reset-state sample-server sample-server2')
|
||||
self.assert_called('POST', '/servers/1234/action',
|
||||
{'os-resetState': {'state': 'error'}}, pos=-4)
|
||||
self.assert_called('POST', '/servers/5678/action',
|
||||
{'os-resetState': {'state': 'error'}}, pos=-1)
|
||||
|
||||
def test_reset_state_active_multiple(self):
|
||||
self.run_command('reset-state --active sample-server sample-server2')
|
||||
self.assert_called('POST', '/servers/1234/action',
|
||||
{'os-resetState': {'state': 'active'}}, pos=-4)
|
||||
self.assert_called('POST', '/servers/5678/action',
|
||||
{'os-resetState': {'state': 'active'}}, pos=-1)
|
||||
|
||||
def test_reset_network(self):
|
||||
self.run_command('reset-network sample-server')
|
||||
self.assert_called('POST', '/servers/1234/action',
|
||||
@ -1288,6 +1417,10 @@ class ShellTest(utils.TestCase):
|
||||
'disabled_reason': 'no_reason'}
|
||||
self.assert_called('PUT', '/os-services/disable-log-reason', body)
|
||||
|
||||
def test_services_delete(self):
|
||||
self.run_command('service-delete 1')
|
||||
self.assert_called('DELETE', '/os-services/1')
|
||||
|
||||
def test_fixed_ips_get(self):
|
||||
self.run_command('fixed-ip-get 192.168.1.1')
|
||||
self.assert_called('GET', '/os-fixed-ips/192.168.1.1')
|
||||
@ -1518,11 +1651,22 @@ class ShellTest(utils.TestCase):
|
||||
self.assert_called('GET', '/os-quota-class-sets/test')
|
||||
|
||||
def test_quota_class_update(self):
|
||||
self.run_command('quota-class-update 97f4c221bff44578b0300df4ef119353'
|
||||
' --instances=5')
|
||||
self.assert_called('PUT',
|
||||
'/os-quota-class-sets/97f4c221bff44578b0300'
|
||||
'df4ef119353')
|
||||
# The list of args we can update.
|
||||
args = (
|
||||
'--instances', '--cores', '--ram', '--floating-ips', '--fixed-ips',
|
||||
'--metadata-items', '--injected-files',
|
||||
'--injected-file-content-bytes', '--injected-file-path-bytes',
|
||||
'--key-pairs', '--security-groups', '--security-group-rules'
|
||||
)
|
||||
for arg in args:
|
||||
self.run_command('quota-class-update '
|
||||
'97f4c221bff44578b0300df4ef119353 '
|
||||
'%s=5' % arg)
|
||||
request_param = arg[2:].replace('-', '_')
|
||||
body = {'quota_class_set': {request_param: 5}}
|
||||
self.assert_called(
|
||||
'PUT', '/os-quota-class-sets/97f4c221bff44578b0300df4ef119353',
|
||||
body)
|
||||
|
||||
def test_network_list(self):
|
||||
self.run_command('network-list')
|
||||
@ -1613,7 +1757,14 @@ class ShellTest(utils.TestCase):
|
||||
self.run_command('network-create --fixed-range-v4 192.168.0.0/24'
|
||||
' --vlan=200 new_network')
|
||||
body = {'network': {'cidr': '192.168.0.0/24', 'label': 'new_network',
|
||||
'vlan_start': '200'}}
|
||||
'vlan': '200'}}
|
||||
self.assert_called('POST', '/os-networks', body)
|
||||
|
||||
def test_network_create_vlan_start(self):
|
||||
self.run_command('network-create --fixed-range-v4 192.168.0.0/24'
|
||||
' --vlan-start=100 new_network')
|
||||
body = {'network': {'cidr': '192.168.0.0/24', 'label': 'new_network',
|
||||
'vlan_start': '100'}}
|
||||
self.assert_called('POST', '/os-networks', body)
|
||||
|
||||
def test_add_fixed_ip(self):
|
||||
@ -1918,6 +2069,45 @@ class ShellTest(utils.TestCase):
|
||||
mock_system.assert_called_with("ssh -6 -p22 "
|
||||
"root@2607:f0d0:1002::4 -1")
|
||||
|
||||
def test_keypair_add(self):
|
||||
self.run_command('keypair-add test')
|
||||
self.assert_called('POST', '/os-keypairs',
|
||||
{'keypair':
|
||||
{'name': 'test'}})
|
||||
|
||||
@mock.patch.object(builtins, 'open',
|
||||
mock.mock_open(read_data='FAKE_PUBLIC_KEY'))
|
||||
def test_keypair_import(self):
|
||||
self.run_command('keypair-add --pub-key test.pub test')
|
||||
self.assert_called('POST', '/os-keypairs',
|
||||
{'keypair':
|
||||
{'public_key': 'FAKE_PUBLIC_KEY',
|
||||
'name': 'test'}})
|
||||
|
||||
def test_keypair_list(self):
|
||||
self.run_command('keypair-list')
|
||||
self.assert_called('GET', '/os-keypairs')
|
||||
|
||||
def test_keypair_show(self):
|
||||
self.run_command('keypair-show test')
|
||||
self.assert_called('GET', '/os-keypairs/test')
|
||||
|
||||
def test_keypair_delete(self):
|
||||
self.run_command('keypair-delete test')
|
||||
self.assert_called('DELETE', '/os-keypairs/test')
|
||||
|
||||
def test_create_server_group(self):
|
||||
self.run_command('server-group-create wjsg affinity')
|
||||
self.assert_called('POST', '/os-server-groups',
|
||||
{'server_group':
|
||||
{'name': 'wjsg',
|
||||
'policies': ['affinity']}})
|
||||
|
||||
def test_delete_multi_server_groups(self):
|
||||
self.run_command('server-group-delete 12345 56789')
|
||||
self.assert_called('DELETE', '/os-server-groups/56789')
|
||||
self.assert_called('DELETE', '/os-server-groups/12345', pos=-2)
|
||||
|
||||
|
||||
class GetSecgroupTest(utils.TestCase):
|
||||
def test_with_integer(self):
|
||||
@ -1949,3 +2139,15 @@ class GetSecgroupTest(utils.TestCase):
|
||||
novaclient.v1_1.shell._get_secgroup,
|
||||
cs,
|
||||
'abc')
|
||||
|
||||
def test_with_non_unique_name(self):
|
||||
group_one = mock.MagicMock()
|
||||
group_one.name = 'group_one'
|
||||
cs = mock.Mock(**{
|
||||
'security_groups.get.return_value': 'sec_group',
|
||||
'security_groups.list.return_value': [group_one, group_one],
|
||||
})
|
||||
self.assertRaises(exceptions.NoUniqueMatch,
|
||||
novaclient.v1_1.shell._get_secgroup,
|
||||
cs,
|
||||
'group_one')
|
||||
|
||||
@ -120,6 +120,10 @@ class FakeHTTPClient(fakes_v1_1.FakeHTTPClient):
|
||||
#
|
||||
get_flavors_2_flavor_access = (
|
||||
fakes_v1_1.FakeHTTPClient.get_flavors_2_os_flavor_access)
|
||||
get_flavors_2_flavor_extra_specs = (
|
||||
fakes_v1_1.FakeHTTPClient.get_flavors_2_os_extra_specs)
|
||||
get_flavors_aa1_flavor_extra_specs = (
|
||||
fakes_v1_1.FakeHTTPClient.get_flavors_aa1_os_extra_specs)
|
||||
|
||||
#
|
||||
# Images
|
||||
@ -170,7 +174,10 @@ class FakeHTTPClient(fakes_v1_1.FakeHTTPClient):
|
||||
required=['name', 'image_ref', 'flavor_ref'],
|
||||
optional=['metadata', 'personality',
|
||||
'os-scheduler-hints:scheduler_hints'])
|
||||
return (202, {}, self.get_servers_1234()[2])
|
||||
if body['server']['name'] == 'some-bad-server':
|
||||
return (202, {}, self.get_servers_1235()[2])
|
||||
else:
|
||||
return (202, {}, self.get_servers_1234()[2])
|
||||
|
||||
#
|
||||
# Server Actions
|
||||
@ -204,9 +211,10 @@ class FakeHTTPClient(fakes_v1_1.FakeHTTPClient):
|
||||
'create_image': ['name', 'metadata'],
|
||||
'migrate_live': ['host', 'block_migration', 'disk_over_commit'],
|
||||
'create_backup': ['name', 'backup_type', 'rotation'],
|
||||
'attach': ['volume_id', 'device'],
|
||||
'detach': ['volume_id'],
|
||||
'swap_volume_attachment': ['old_volume_id', 'new_volume_id']}
|
||||
body_params_check_superset = {
|
||||
'attach': ['volume_id', 'device']}
|
||||
|
||||
assert len(body.keys()) == 1
|
||||
action = list(body)[0]
|
||||
@ -224,6 +232,9 @@ class FakeHTTPClient(fakes_v1_1.FakeHTTPClient):
|
||||
if action in body_params_check_exact:
|
||||
assert set(body[action]) == set(body_params_check_exact[action])
|
||||
|
||||
if action in body_params_check_superset:
|
||||
assert set(body[action]) >= set(body_params_check_superset[action])
|
||||
|
||||
if action == 'reboot':
|
||||
assert body[action]['type'] in ['HARD', 'SOFT']
|
||||
elif action == 'confirm_resize':
|
||||
@ -234,7 +245,8 @@ class FakeHTTPClient(fakes_v1_1.FakeHTTPClient):
|
||||
|
||||
if action not in set.union(set(body_is_none_list),
|
||||
set(body_params_check_exact.keys()),
|
||||
set(body_param_check_exists.keys())):
|
||||
set(body_param_check_exists.keys()),
|
||||
set(body_params_check_superset.keys())):
|
||||
raise AssertionError("Unexpected server action: %s" % action)
|
||||
|
||||
return (resp, _headers, _body)
|
||||
@ -329,8 +341,8 @@ class FakeHTTPClient(fakes_v1_1.FakeHTTPClient):
|
||||
{'id': 1234,
|
||||
'hypervisor_hostname': 'hyper1',
|
||||
'servers': [
|
||||
{'name': 'inst1', 'uuid': 'uuid1'},
|
||||
{'name': 'inst2', 'uuid': 'uuid2'}
|
||||
{'name': 'inst1', 'id': 'uuid1'},
|
||||
{'name': 'inst2', 'id': 'uuid2'}
|
||||
]},
|
||||
})
|
||||
|
||||
@ -341,3 +353,31 @@ class FakeHTTPClient(fakes_v1_1.FakeHTTPClient):
|
||||
get_keypairs = fakes_v1_1.FakeHTTPClient.get_os_keypairs
|
||||
delete_keypairs_test = fakes_v1_1.FakeHTTPClient.delete_os_keypairs_test
|
||||
post_keypairs = fakes_v1_1.FakeHTTPClient.post_os_keypairs
|
||||
|
||||
#
|
||||
# List all extensions
|
||||
#
|
||||
def get_extensions(self, **kw):
|
||||
exts = [
|
||||
{
|
||||
"alias": "os-multinic",
|
||||
"description": "Multiple network support",
|
||||
"name": "Multinic",
|
||||
"version": 1,
|
||||
},
|
||||
{
|
||||
"alias": "os-extended-server-attributes",
|
||||
"description": "Extended Server Attributes support.",
|
||||
"name": "ExtendedServerAttributes",
|
||||
"version": 1,
|
||||
},
|
||||
{
|
||||
"alias": "os-extended-status",
|
||||
"description": "Extended Status support",
|
||||
"name": "ExtendedStatus",
|
||||
"version": 1,
|
||||
},
|
||||
]
|
||||
return (200, {}, {
|
||||
"extensions": exts,
|
||||
})
|
||||
|
||||
@ -13,20 +13,17 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.v1_1 import test_agents
|
||||
from novaclient.tests.v3 import fakes
|
||||
from novaclient.v3 import agents
|
||||
|
||||
|
||||
class AgentsTest(test_agents.AgentsTest):
|
||||
|
||||
scenarios = [('original', {'client_fixture_class': client.V3}),
|
||||
('session', {'client_fixture_class': client.SessionV3})]
|
||||
|
||||
def _build_example_update_body(self):
|
||||
return {"agent": {
|
||||
"url": "/yyy/yyyy/yyyy",
|
||||
"version": "8.0",
|
||||
"md5hash": "add6bb58e139be103324d04d82d8f546"}}
|
||||
|
||||
def _get_fake_client(self):
|
||||
return fakes.FakeClient()
|
||||
|
||||
def _get_agent_type(self):
|
||||
return agents.Agent
|
||||
|
||||
@ -12,19 +12,11 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.v1_1 import test_aggregates
|
||||
from novaclient.tests.v3 import fakes
|
||||
from novaclient.v3 import aggregates
|
||||
|
||||
|
||||
class AggregatesTest(test_aggregates.AggregatesTest):
|
||||
def setUp(self):
|
||||
super(AggregatesTest, self).setUp()
|
||||
self.cs = self._get_fake_client()
|
||||
self.aggregate_type = self._get_aggregate_type()
|
||||
|
||||
def _get_fake_client(self):
|
||||
return fakes.FakeClient()
|
||||
|
||||
def _get_aggregate_type(self):
|
||||
return aggregates.Aggregate
|
||||
scenarios = [('original', {'client_fixture_class': client.V3}),
|
||||
('session', {'client_fixture_class': client.SessionV3})]
|
||||
|
||||
@ -14,25 +14,23 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import availability_zones as data
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.v1_1 import test_availability_zone
|
||||
from novaclient.tests.v3 import fakes
|
||||
from novaclient.v3 import availability_zones
|
||||
|
||||
|
||||
class AvailabilityZoneTest(test_availability_zone.AvailabilityZoneTest):
|
||||
from novaclient.v3 import shell # noqa
|
||||
|
||||
def setUp(self):
|
||||
super(AvailabilityZoneTest, self).setUp()
|
||||
self.cs = self._get_fake_client()
|
||||
self.availability_zone_type = self._get_availability_zone_type()
|
||||
data_fixture_class = data.V3
|
||||
|
||||
scenarios = [('original', {'client_fixture_class': client.V3}),
|
||||
('session', {'client_fixture_class': client.SessionV3})]
|
||||
|
||||
def _assertZone(self, zone, name, status):
|
||||
self.assertEqual(zone.zone_name, name)
|
||||
self.assertEqual(zone.zone_state, status)
|
||||
|
||||
def _get_fake_client(self):
|
||||
return fakes.FakeClient()
|
||||
|
||||
def _get_availability_zone_type(self):
|
||||
return availability_zones.AvailabilityZone
|
||||
|
||||
@ -11,19 +11,11 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.v1_1 import fakes
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.v1_1 import test_certs
|
||||
from novaclient.v3 import certs
|
||||
|
||||
|
||||
class CertsTest(test_certs.CertsTest):
|
||||
def setUp(self):
|
||||
super(CertsTest, self).setUp()
|
||||
self.cs = self._get_fake_client()
|
||||
self.cert_type = self._get_cert_type()
|
||||
|
||||
def _get_fake_client(self):
|
||||
return fakes.FakeClient()
|
||||
|
||||
def _get_cert_type(self):
|
||||
return certs.Certificate
|
||||
scenarios = [('original', {'client_fixture_class': client.V3}),
|
||||
('session', {'client_fixture_class': client.SessionV3})]
|
||||
|
||||
@ -13,6 +13,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
|
||||
from novaclient.tests.v1_1 import test_flavors
|
||||
from novaclient.tests.v3 import fakes
|
||||
from novaclient.v3 import flavors
|
||||
@ -57,10 +59,15 @@ class FlavorsTest(test_flavors.FlavorsTest):
|
||||
self.cs.assert_called('POST', '/flavors/4/flavor-extra-specs',
|
||||
{"extra_specs": {key: 'v4'}})
|
||||
|
||||
def test_unset_keys(self):
|
||||
@mock.patch.object(flavors.FlavorManager, '_delete')
|
||||
def test_unset_keys(self, mock_delete):
|
||||
f = self.cs.flavors.get(1)
|
||||
f.unset_keys(['k1'])
|
||||
self.cs.assert_called('DELETE', '/flavors/1/flavor-extra-specs/k1')
|
||||
keys = ['k1', 'k2']
|
||||
f.unset_keys(keys)
|
||||
mock_delete.assert_has_calls([
|
||||
mock.call("/flavors/1/flavor-extra-specs/k1"),
|
||||
mock.call("/flavors/1/flavor-extra-specs/k2")
|
||||
])
|
||||
|
||||
def test_get_flavor_details_diablo(self):
|
||||
# Don't need for V3 API to work against diablo
|
||||
|
||||
@ -12,72 +12,73 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import hosts as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v3 import fakes
|
||||
from novaclient.v3 import hosts
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
class HostsTest(utils.FixturedTestCase):
|
||||
|
||||
|
||||
class HostsTest(utils.TestCase):
|
||||
client_fixture_class = client.V3
|
||||
data_fixture_class = data.V3
|
||||
|
||||
def test_describe_resource(self):
|
||||
hs = cs.hosts.get('host')
|
||||
cs.assert_called('GET', '/os-hosts/host')
|
||||
hs = self.cs.hosts.get('host')
|
||||
self.assert_called('GET', '/os-hosts/host')
|
||||
for h in hs:
|
||||
self.assertIsInstance(h, hosts.Host)
|
||||
|
||||
def test_list_host(self):
|
||||
hs = cs.hosts.list()
|
||||
cs.assert_called('GET', '/os-hosts')
|
||||
hs = self.cs.hosts.list()
|
||||
self.assert_called('GET', '/os-hosts')
|
||||
for h in hs:
|
||||
self.assertIsInstance(h, hosts.Host)
|
||||
self.assertEqual(h.zone, 'nova1')
|
||||
|
||||
def test_list_host_with_zone(self):
|
||||
hs = cs.hosts.list('nova')
|
||||
cs.assert_called('GET', '/os-hosts?zone=nova')
|
||||
hs = self.cs.hosts.list('nova')
|
||||
self.assert_called('GET', '/os-hosts?zone=nova')
|
||||
for h in hs:
|
||||
self.assertIsInstance(h, hosts.Host)
|
||||
self.assertEqual(h.zone, 'nova')
|
||||
|
||||
def test_update_enable(self):
|
||||
host = cs.hosts.get('sample_host')[0]
|
||||
host = self.cs.hosts.get('sample_host')[0]
|
||||
values = {"status": "enabled"}
|
||||
result = host.update(values)
|
||||
cs.assert_called('PUT', '/os-hosts/sample_host', {"host": values})
|
||||
self.assert_called('PUT', '/os-hosts/sample_host', {"host": values})
|
||||
self.assertIsInstance(result, hosts.Host)
|
||||
|
||||
def test_update_maintenance(self):
|
||||
host = cs.hosts.get('sample_host')[0]
|
||||
host = self.cs.hosts.get('sample_host')[0]
|
||||
values = {"maintenance_mode": "enable"}
|
||||
result = host.update(values)
|
||||
cs.assert_called('PUT', '/os-hosts/sample_host', {"host": values})
|
||||
self.assert_called('PUT', '/os-hosts/sample_host', {"host": values})
|
||||
self.assertIsInstance(result, hosts.Host)
|
||||
|
||||
def test_update_both(self):
|
||||
host = cs.hosts.get('sample_host')[0]
|
||||
host = self.cs.hosts.get('sample_host')[0]
|
||||
values = {"status": "enabled",
|
||||
"maintenance_mode": "enable"}
|
||||
result = host.update(values)
|
||||
cs.assert_called('PUT', '/os-hosts/sample_host', {"host": values})
|
||||
self.assert_called('PUT', '/os-hosts/sample_host', {"host": values})
|
||||
self.assertIsInstance(result, hosts.Host)
|
||||
|
||||
def test_host_startup(self):
|
||||
host = cs.hosts.get('sample_host')[0]
|
||||
result = host.startup()
|
||||
cs.assert_called(
|
||||
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 = cs.hosts.get('sample_host')[0]
|
||||
result = host.reboot()
|
||||
cs.assert_called(
|
||||
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 = cs.hosts.get('sample_host')[0]
|
||||
result = host.shutdown()
|
||||
cs.assert_called(
|
||||
host = self.cs.hosts.get('sample_host')[0]
|
||||
host.shutdown()
|
||||
self.assert_called(
|
||||
'GET', '/os-hosts/sample_host/shutdown')
|
||||
|
||||
@ -13,17 +13,15 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import hypervisors as data
|
||||
from novaclient.tests.v1_1 import test_hypervisors
|
||||
from novaclient.tests.v3 import fakes
|
||||
|
||||
|
||||
class HypervisorsTest(test_hypervisors.HypervisorsTest):
|
||||
def setUp(self):
|
||||
super(HypervisorsTest, self).setUp()
|
||||
self.cs = self._get_fake_client()
|
||||
|
||||
def _get_fake_client(self):
|
||||
return fakes.FakeClient()
|
||||
client_fixture_class = client.V3
|
||||
data_fixture_class = data.V3
|
||||
|
||||
def test_hypervisor_search(self):
|
||||
expected = [
|
||||
@ -32,7 +30,7 @@ class HypervisorsTest(test_hypervisors.HypervisorsTest):
|
||||
]
|
||||
|
||||
result = self.cs.hypervisors.search('hyper')
|
||||
self.cs.assert_called('GET', '/os-hypervisors/search?query=hyper')
|
||||
self.assert_called('GET', '/os-hypervisors/search?query=hyper')
|
||||
|
||||
for idx, hyper in enumerate(result):
|
||||
self.compare_to_expected(expected[idx], hyper)
|
||||
@ -41,10 +39,10 @@ class HypervisorsTest(test_hypervisors.HypervisorsTest):
|
||||
expected = dict(id=1234,
|
||||
hypervisor_hostname='hyper1',
|
||||
servers=[
|
||||
dict(name='inst1', uuid='uuid1'),
|
||||
dict(name='inst2', uuid='uuid2')])
|
||||
dict(name='inst1', id='uuid1'),
|
||||
dict(name='inst2', id='uuid2')])
|
||||
|
||||
result = self.cs.hypervisors.servers('1234')
|
||||
self.cs.assert_called('GET', '/os-hypervisors/1234/servers')
|
||||
self.assert_called('GET', '/os-hypervisors/1234/servers')
|
||||
|
||||
self.compare_to_expected(expected, result)
|
||||
|
||||
@ -13,45 +13,45 @@
|
||||
# under the License.
|
||||
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import images as data
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v3 import fakes
|
||||
from novaclient.v3 import images
|
||||
|
||||
|
||||
cs = fakes.FakeClient()
|
||||
class ImagesTest(utils.FixturedTestCase):
|
||||
|
||||
|
||||
class ImagesTest(utils.TestCase):
|
||||
client_fixture_class = client.V3
|
||||
data_fixture_class = data.V3
|
||||
|
||||
def test_list_images(self):
|
||||
il = cs.images.list()
|
||||
cs.assert_called('GET', '/v1/images/detail')
|
||||
il = self.cs.images.list()
|
||||
self.assert_called('GET', '/v1/images/detail')
|
||||
for i in il:
|
||||
self.assertIsInstance(i, images.Image)
|
||||
|
||||
def test_list_images_undetailed(self):
|
||||
il = cs.images.list(detailed=False)
|
||||
cs.assert_called('GET', '/v1/images')
|
||||
il = self.cs.images.list(detailed=False)
|
||||
self.assert_called('GET', '/v1/images')
|
||||
for i in il:
|
||||
self.assertIsInstance(i, images.Image)
|
||||
|
||||
def test_list_images_with_limit(self):
|
||||
il = cs.images.list(limit=4)
|
||||
cs.assert_called('GET', '/v1/images/detail?limit=4')
|
||||
self.cs.images.list(limit=4)
|
||||
self.assert_called('GET', '/v1/images/detail?limit=4')
|
||||
|
||||
def test_get_image_details(self):
|
||||
i = cs.images.get(1)
|
||||
cs.assert_called('HEAD', '/v1/images/1')
|
||||
i = self.cs.images.get(1)
|
||||
self.assert_called('HEAD', '/v1/images/1')
|
||||
self.assertIsInstance(i, images.Image)
|
||||
self.assertEqual(i.id, '1')
|
||||
self.assertEqual(i.name, 'CentOS 5.2')
|
||||
|
||||
def test_find(self):
|
||||
i = cs.images.find(name="CentOS 5.2")
|
||||
i = self.cs.images.find(name="CentOS 5.2")
|
||||
self.assertEqual(i.id, '1')
|
||||
cs.assert_called('GET', '/v1/images', pos=-2)
|
||||
cs.assert_called('HEAD', '/v1/images/1', pos=-1)
|
||||
self.assert_called('HEAD', '/v1/images/1')
|
||||
|
||||
iml = cs.images.findall(status='SAVING')
|
||||
iml = self.cs.images.findall(status='SAVING')
|
||||
self.assertEqual(len(iml), 1)
|
||||
self.assertEqual(iml[0].name, 'My Server Backup')
|
||||
|
||||
@ -12,20 +12,19 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import keypairs as data
|
||||
from novaclient.tests.v1_1 import test_keypairs
|
||||
from novaclient.tests.v3 import fakes
|
||||
from novaclient.v3 import keypairs
|
||||
|
||||
|
||||
class KeypairsTest(test_keypairs.KeypairsTest):
|
||||
def setUp(self):
|
||||
super(KeypairsTest, self).setUp()
|
||||
self.cs = self._get_fake_client()
|
||||
self.keypair_type = self._get_keypair_type()
|
||||
self.keypair_prefix = keypairs.KeypairManager.keypair_prefix
|
||||
|
||||
def _get_fake_client(self):
|
||||
return fakes.FakeClient()
|
||||
client_fixture_class = client.V3
|
||||
data_fixture_class = data.V3
|
||||
|
||||
def _get_keypair_type(self):
|
||||
return keypairs.Keypair
|
||||
|
||||
def _get_keypair_prefix(self):
|
||||
return keypairs.KeypairManager.keypair_prefix
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
# Copyright 2014 NEC Corporation. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient import extension
|
||||
from novaclient.tests import utils
|
||||
from novaclient.tests.v3 import fakes
|
||||
from novaclient.v3 import list_extensions
|
||||
|
||||
|
||||
extensions = [
|
||||
extension.Extension("list_extensions", 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)
|
||||
@ -12,22 +12,20 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from novaclient.tests.fixture_data import client
|
||||
from novaclient.tests.fixture_data import quotas as data
|
||||
from novaclient.tests.v1_1 import test_quotas
|
||||
from novaclient.tests.v3 import fakes
|
||||
|
||||
|
||||
class QuotaSetsTest(test_quotas.QuotaSetsTest):
|
||||
def setUp(self):
|
||||
super(QuotaSetsTest, self).setUp()
|
||||
self.cs = self._get_fake_client()
|
||||
|
||||
def _get_fake_client(self):
|
||||
return fakes.FakeClient()
|
||||
client_fixture_class = client.V3
|
||||
data_fixture_class = data.V3
|
||||
|
||||
def test_force_update_quota(self):
|
||||
q = self.cs.quotas.get('97f4c221bff44578b0300df4ef119353')
|
||||
q.update(cores=2, force=True)
|
||||
self.cs.assert_called(
|
||||
self.assert_called(
|
||||
'PUT', '/os-quota-sets/97f4c221bff44578b0300df4ef119353',
|
||||
{'quota_set': {'force': True,
|
||||
'cores': 2}})
|
||||
@ -35,11 +33,11 @@ class QuotaSetsTest(test_quotas.QuotaSetsTest):
|
||||
def test_tenant_quotas_get_detail(self):
|
||||
tenant_id = 'test'
|
||||
self.cs.quotas.get(tenant_id, detail=True)
|
||||
self.cs.assert_called('GET', '/os-quota-sets/%s/detail' % tenant_id)
|
||||
self.assert_called('GET', '/os-quota-sets/%s/detail' % tenant_id)
|
||||
|
||||
def test_user_quotas_get_detail(self):
|
||||
tenant_id = 'test'
|
||||
user_id = 'fake_user'
|
||||
self.cs.quotas.get(tenant_id, user_id=user_id, detail=True)
|
||||
url = '/os-quota-sets/%s/detail?user_id=%s' % (tenant_id, user_id)
|
||||
self.cs.assert_called('GET', url)
|
||||
self.assert_called('GET', url)
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
import six
|
||||
|
||||
from novaclient import exceptions
|
||||
@ -74,6 +75,50 @@ class ServersTest(utils.TestCase):
|
||||
cs.assert_called('POST', '/servers')
|
||||
self.assertIsInstance(s, servers.Server)
|
||||
|
||||
def test_create_server_boot_with_nics_ipv4(self):
|
||||
old_boot = cs.servers._boot
|
||||
nics = [{'net-id': '11111111-1111-1111-1111-111111111111',
|
||||
'v4-fixed-ip': '10.10.0.7'}]
|
||||
|
||||
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(cs.servers, '_boot', wrapped_boot):
|
||||
s = cs.servers.create(
|
||||
name="My server",
|
||||
image=1,
|
||||
flavor=1,
|
||||
meta={'foo': 'bar'},
|
||||
userdata="hello moto",
|
||||
key_name="fakekey",
|
||||
nics=nics
|
||||
)
|
||||
cs.assert_called('POST', '/servers')
|
||||
self.assertIsInstance(s, servers.Server)
|
||||
|
||||
def test_create_server_boot_with_nics_ipv6(self):
|
||||
old_boot = 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(cs.servers, '_boot', wrapped_boot):
|
||||
s = cs.servers.create(
|
||||
name="My server",
|
||||
image=1,
|
||||
flavor=1,
|
||||
meta={'foo': 'bar'},
|
||||
userdata="hello moto",
|
||||
key_name="fakekey",
|
||||
nics=nics
|
||||
)
|
||||
cs.assert_called('POST', '/servers')
|
||||
self.assertIsInstance(s, servers.Server)
|
||||
|
||||
def test_create_server_userdata_file_object(self):
|
||||
s = cs.servers.create(
|
||||
name="My server",
|
||||
@ -121,6 +166,26 @@ class ServersTest(utils.TestCase):
|
||||
cs.assert_called('POST', '/servers')
|
||||
self.assertIsInstance(s, servers.Server)
|
||||
|
||||
def test_create_server_return_reservation_id(self):
|
||||
s = cs.servers.create(
|
||||
name="My server",
|
||||
image=1,
|
||||
flavor=1,
|
||||
reservation_id=True
|
||||
)
|
||||
expected_body = {
|
||||
'server': {
|
||||
'name': 'My server',
|
||||
'image_ref': '1',
|
||||
'flavor_ref': '1',
|
||||
'os-multiple-create:min_count': 1,
|
||||
'os-multiple-create:max_count': 1,
|
||||
'os-multiple-create:return_reservation_id': True,
|
||||
}
|
||||
}
|
||||
cs.assert_called('POST', '/servers', expected_body)
|
||||
self.assertIsInstance(s, servers.Server)
|
||||
|
||||
def test_update_server(self):
|
||||
s = cs.servers.get(1234)
|
||||
|
||||
@ -147,12 +212,12 @@ class ServersTest(utils.TestCase):
|
||||
cs.assert_called('DELETE', '/servers/1234')
|
||||
|
||||
def test_delete_server_meta(self):
|
||||
s = cs.servers.delete_meta(1234, ['test_key'])
|
||||
cs.servers.delete_meta(1234, ['test_key'])
|
||||
cs.assert_called('DELETE', '/servers/1234/metadata/test_key')
|
||||
|
||||
def test_set_server_meta(self):
|
||||
s = cs.servers.set_meta(1234, {'test_key': 'test_value'})
|
||||
reval = cs.assert_called('POST', '/servers/1234/metadata',
|
||||
cs.servers.set_meta(1234, {'test_key': 'test_value'})
|
||||
cs.assert_called('POST', '/servers/1234/metadata',
|
||||
{'metadata': {'test_key': 'test_value'}})
|
||||
|
||||
def test_find(self):
|
||||
@ -299,7 +364,8 @@ class ServersTest(utils.TestCase):
|
||||
|
||||
cs.servers.get_console_output(s)
|
||||
self.assertEqual(cs.servers.get_console_output(s), success)
|
||||
cs.assert_called('POST', '/servers/1234/action')
|
||||
cs.assert_called('POST', '/servers/1234/action',
|
||||
{'get_console_output': {'length': -1}})
|
||||
|
||||
def test_get_console_output_with_length(self):
|
||||
success = 'foo'
|
||||
@ -307,11 +373,13 @@ class ServersTest(utils.TestCase):
|
||||
s = cs.servers.get(1234)
|
||||
s.get_console_output(length=50)
|
||||
self.assertEqual(s.get_console_output(length=50), success)
|
||||
cs.assert_called('POST', '/servers/1234/action')
|
||||
cs.assert_called('POST', '/servers/1234/action',
|
||||
{'get_console_output': {'length': 50}})
|
||||
|
||||
cs.servers.get_console_output(s, length=50)
|
||||
self.assertEqual(cs.servers.get_console_output(s, length=50), success)
|
||||
cs.assert_called('POST', '/servers/1234/action')
|
||||
cs.assert_called('POST', '/servers/1234/action',
|
||||
{'get_console_output': {'length': 50}})
|
||||
|
||||
def test_get_password(self):
|
||||
s = cs.servers.get(1234)
|
||||
|
||||
@ -186,20 +186,6 @@ class ShellTest(utils.TestCase):
|
||||
}},
|
||||
)
|
||||
|
||||
def test_boot_multiple(self):
|
||||
self.run_command('boot --flavor 1 --image 1'
|
||||
' --num-instances 3 some-server')
|
||||
self.assert_called_anytime(
|
||||
'POST', '/servers',
|
||||
{'server': {
|
||||
'flavor_ref': '1',
|
||||
'name': 'some-server',
|
||||
'image_ref': '1',
|
||||
'os-multiple-create:min_count': 1,
|
||||
'os-multiple-create:max_count': 3,
|
||||
}},
|
||||
)
|
||||
|
||||
def test_boot_image_with(self):
|
||||
self.run_command("boot --flavor 1"
|
||||
" --image-with test_key=test_value some-server")
|
||||
@ -240,6 +226,7 @@ class ShellTest(utils.TestCase):
|
||||
|
||||
mock_open.assert_called_once_with(testfile)
|
||||
|
||||
user_data = base64.b64encode(file_text.encode('utf-8')).decode('utf-8')
|
||||
self.assert_called_anytime(
|
||||
'POST', '/servers',
|
||||
{'server': {
|
||||
@ -248,9 +235,7 @@ class ShellTest(utils.TestCase):
|
||||
'image_ref': '1',
|
||||
'os-multiple-create:min_count': 1,
|
||||
'os-multiple-create:max_count': 1,
|
||||
'os-user-data:user_data': base64.b64encode(
|
||||
file_text.encode('utf-8'))
|
||||
}},
|
||||
'os-user-data:user_data': user_data}},
|
||||
)
|
||||
|
||||
def test_boot_avzone(self):
|
||||
@ -428,7 +413,33 @@ class ShellTest(utils.TestCase):
|
||||
},
|
||||
)
|
||||
|
||||
def tets_boot_nics_no_value(self):
|
||||
def test_boot_nics_ipv6(self):
|
||||
cmd = ('boot --image 1 --flavor 1 '
|
||||
'--nic net-id=a=c,v6-fixed-ip=2001:db9:0:1::10 some-server')
|
||||
self.run_command(cmd)
|
||||
self.assert_called_anytime(
|
||||
'POST', '/servers',
|
||||
{
|
||||
'server': {
|
||||
'flavor_ref': '1',
|
||||
'name': 'some-server',
|
||||
'image_ref': '1',
|
||||
'os-multiple-create:min_count': 1,
|
||||
'os-multiple-create:max_count': 1,
|
||||
'networks': [
|
||||
{'uuid': 'a=c', 'fixed_ip': '2001:db9:0:1::10'},
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
def test_boot_nics_both_ipv4_and_ipv6(self):
|
||||
cmd = ('boot --image 1 --flavor 1 '
|
||||
'--nic net-id=a=c,v4-fixed-ip=10.0.0.1,'
|
||||
'v6-fixed-ip=2001:db9:0:1::10 some-server')
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
|
||||
def test_boot_nics_no_value(self):
|
||||
cmd = ('boot --image 1 --flavor 1 '
|
||||
'--nic net-id some-server')
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
@ -443,6 +454,11 @@ class ShellTest(utils.TestCase):
|
||||
'--nic v4-fixed-ip=10.0.0.1 some-server')
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
|
||||
def test_boot_nics_netid_and_portid(self):
|
||||
cmd = ('boot --image 1 --flavor 1 '
|
||||
'--nic port-id=some=port,net-id=some=net some-server')
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
|
||||
def test_boot_num_instances(self):
|
||||
self.run_command('boot --image 1 --flavor 1 --num-instances 3 server')
|
||||
self.assert_called_anytime(
|
||||
@ -458,11 +474,69 @@ class ShellTest(utils.TestCase):
|
||||
})
|
||||
|
||||
def test_boot_invalid_num_instances(self):
|
||||
cmd = 'boot --image 1 --flavor 1 --num-instances 1 server'
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
cmd = 'boot --image 1 --flavor 1 --num-instances 0 server'
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
|
||||
def test_boot_num_instances_and_count(self):
|
||||
cmd = 'boot --image 1 --flavor 1 --num-instances 3 --min-count 3 serv'
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
cmd = 'boot --image 1 --flavor 1 --num-instances 3 --max-count 3 serv'
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
|
||||
def test_boot_min_max_count(self):
|
||||
self.run_command('boot --image 1 --flavor 1 --max-count 3 server')
|
||||
self.assert_called_anytime(
|
||||
'POST', '/servers',
|
||||
{
|
||||
'server': {
|
||||
'flavor_ref': '1',
|
||||
'name': 'server',
|
||||
'image_ref': '1',
|
||||
'os-multiple-create:min_count': 1,
|
||||
'os-multiple-create:max_count': 3,
|
||||
}
|
||||
})
|
||||
self.run_command('boot --image 1 --flavor 1 --min-count 3 server')
|
||||
self.assert_called_anytime(
|
||||
'POST', '/servers',
|
||||
{
|
||||
'server': {
|
||||
'flavor_ref': '1',
|
||||
'name': 'server',
|
||||
'image_ref': '1',
|
||||
'os-multiple-create:min_count': 3,
|
||||
'os-multiple-create:max_count': 3,
|
||||
}
|
||||
})
|
||||
self.run_command('boot --image 1 --flavor 1 '
|
||||
'--min-count 3 --max-count 3 server')
|
||||
self.assert_called_anytime(
|
||||
'POST', '/servers',
|
||||
{
|
||||
'server': {
|
||||
'flavor_ref': '1',
|
||||
'name': 'server',
|
||||
'image_ref': '1',
|
||||
'os-multiple-create:min_count': 3,
|
||||
'os-multiple-create:max_count': 3,
|
||||
}
|
||||
})
|
||||
self.run_command('boot --image 1 --flavor 1 '
|
||||
'--min-count 3 --max-count 5 server')
|
||||
self.assert_called_anytime(
|
||||
'POST', '/servers',
|
||||
{
|
||||
'server': {
|
||||
'flavor_ref': '1',
|
||||
'name': 'server',
|
||||
'image_ref': '1',
|
||||
'os-multiple-create:min_count': 3,
|
||||
'os-multiple-create:max_count': 5,
|
||||
}
|
||||
})
|
||||
cmd = 'boot --image 1 --flavor 1 --min-count 3 --max-count 1 serv'
|
||||
self.assertRaises(exceptions.CommandError, self.run_command, cmd)
|
||||
|
||||
@mock.patch('novaclient.v3.shell._poll_for_status')
|
||||
def test_boot_with_poll(self, poll_method):
|
||||
self.run_command('boot --flavor 1 --image 1 some-server --poll')
|
||||
@ -480,3 +554,43 @@ class ShellTest(utils.TestCase):
|
||||
poll_method.assert_has_calls(
|
||||
[mock.call(self.shell.cs.servers.get, 1234, 'building',
|
||||
['active'])])
|
||||
|
||||
def test_boot_with_poll_to_check_VM_state_error(self):
|
||||
self.assertRaises(exceptions.InstanceInErrorState, self.run_command,
|
||||
'boot --flavor 1 --image 1 some-bad-server --poll')
|
||||
|
||||
def test_boot_named_flavor(self):
|
||||
self.run_command(["boot", "--image", "1",
|
||||
"--flavor", "512 MB Server",
|
||||
"--max-count", "3", "server"])
|
||||
self.assert_called('GET', '/flavors/512 MB Server', pos=0)
|
||||
self.assert_called('GET', '/flavors?is_public=None', pos=1)
|
||||
self.assert_called('GET', '/flavors?is_public=None', pos=2)
|
||||
self.assert_called('GET', '/flavors/2', pos=3)
|
||||
self.assert_called(
|
||||
'POST', '/servers',
|
||||
{
|
||||
'server': {
|
||||
'flavor_ref': '2',
|
||||
'name': 'server',
|
||||
'image_ref': '1',
|
||||
'os-multiple-create:min_count': 1,
|
||||
'os-multiple-create:max_count': 3,
|
||||
}
|
||||
}, pos=4)
|
||||
|
||||
def test_flavor_show_by_name(self):
|
||||
self.run_command(['flavor-show', '128 MB Server'])
|
||||
self.assert_called('GET', '/flavors/128 MB Server', pos=0)
|
||||
self.assert_called('GET', '/flavors?is_public=None', pos=1)
|
||||
self.assert_called('GET', '/flavors?is_public=None', pos=2)
|
||||
self.assert_called('GET', '/flavors/aa1', pos=3)
|
||||
self.assert_called('GET', '/flavors/aa1/flavor-extra-specs', pos=4)
|
||||
|
||||
def test_flavor_show_by_name_priv(self):
|
||||
self.run_command(['flavor-show', '512 MB Server'])
|
||||
self.assert_called('GET', '/flavors/512 MB Server', pos=0)
|
||||
self.assert_called('GET', '/flavors?is_public=None', pos=1)
|
||||
self.assert_called('GET', '/flavors?is_public=None', pos=2)
|
||||
self.assert_called('GET', '/flavors/2', pos=3)
|
||||
self.assert_called('GET', '/flavors/2/flavor-extra-specs', pos=4)
|
||||
|
||||
@ -26,16 +26,33 @@ class VolumesTest(utils.TestCase):
|
||||
return fakes.FakeClient()
|
||||
|
||||
def test_attach_server_volume(self):
|
||||
v = self.cs.volumes.attach_server_volume(
|
||||
self.cs.volumes.attach_server_volume(
|
||||
server=1234,
|
||||
volume_id='15e59938-07d5-11e1-90e3-e3dffe0c5983',
|
||||
device='/dev/vdb'
|
||||
)
|
||||
self.cs.assert_called('POST', '/servers/1234/action')
|
||||
|
||||
def test_attach_server_volume_disk_bus_device_type(self):
|
||||
volume_id = '15e59938-07d5-11e1-90e3-e3dffe0c5983'
|
||||
device = '/dev/vdb'
|
||||
disk_bus = 'ide'
|
||||
device_type = 'cdrom'
|
||||
self.cs.volumes.attach_server_volume(server=1234,
|
||||
volume_id=volume_id,
|
||||
device=device,
|
||||
disk_bus=disk_bus,
|
||||
device_type=device_type)
|
||||
body_params = {'volume_id': volume_id,
|
||||
'device': device,
|
||||
'disk_bus': disk_bus,
|
||||
'device_type': device_type}
|
||||
body = {'attach': body_params}
|
||||
self.cs.assert_called('POST', '/servers/1234/action', body)
|
||||
|
||||
def test_update_server_volume(self):
|
||||
vol_id = '15e59938-07d5-11e1-90e3-e3dffe0c5983'
|
||||
v = self.cs.volumes.update_server_volume(
|
||||
self.cs.volumes.update_server_volume(
|
||||
server=1234,
|
||||
old_volume_id='Work',
|
||||
new_volume_id=vol_id
|
||||
|
||||
@ -57,13 +57,14 @@ def get_resource_manager_extra_kwargs(f, args, allow_conflicts=False):
|
||||
extra_kwargs = {}
|
||||
for hook in hooks:
|
||||
hook_kwargs = hook(args)
|
||||
|
||||
hook_name = hook.__name__
|
||||
conflicting_keys = set(hook_kwargs.keys()) & set(extra_kwargs.keys())
|
||||
if conflicting_keys and not allow_conflicts:
|
||||
raise Exception(_("Hook '%(hook_name)s' is attempting to redefine"
|
||||
" attributes '%(conflicting_keys)s'") %
|
||||
{'hook_name': hook_name,
|
||||
'conflicting_keys': conflicting_keys})
|
||||
msg = (_("Hook '%(hook_name)s' is attempting to redefine "
|
||||
"attributes '%(conflicting_keys)s'") %
|
||||
{'hook_name': hook_name,
|
||||
'conflicting_keys': conflicting_keys})
|
||||
raise exceptions.NoUniqueMatch(msg)
|
||||
|
||||
extra_kwargs.update(hook_kwargs)
|
||||
|
||||
@ -203,7 +204,7 @@ def print_dict(d, dict_property="Property", dict_value="Value", wrap=0):
|
||||
|
||||
def find_resource(manager, name_or_id, **find_args):
|
||||
"""Helper for the _find_* methods."""
|
||||
# for str id which is not uuid (for Flavor search currently)
|
||||
# for str id which is not uuid (for Flavor and Keypair search currently)
|
||||
if getattr(manager, 'is_alphanum_id_allowed', False):
|
||||
try:
|
||||
return manager.get(name_or_id)
|
||||
@ -351,7 +352,7 @@ def _load_entry_point(ep_name, name=None):
|
||||
def is_integer_like(val):
|
||||
"""Returns validation of a value as an integer."""
|
||||
try:
|
||||
value = int(val)
|
||||
int(val)
|
||||
return True
|
||||
except (TypeError, ValueError, AttributeError):
|
||||
return False
|
||||
|
||||
@ -37,7 +37,7 @@ class CertificateManager(base.Manager):
|
||||
|
||||
def create(self):
|
||||
"""
|
||||
Create a x509 certificates for a user in tenant.
|
||||
Create a x509 certificate for a user in tenant.
|
||||
"""
|
||||
return self._create('/os-certificates', {}, 'certificate')
|
||||
|
||||
|
||||
@ -37,6 +37,7 @@ from novaclient.v1_1 import quota_classes
|
||||
from novaclient.v1_1 import quotas
|
||||
from novaclient.v1_1 import security_group_rules
|
||||
from novaclient.v1_1 import security_groups
|
||||
from novaclient.v1_1 import server_groups
|
||||
from novaclient.v1_1 import servers
|
||||
from novaclient.v1_1 import services
|
||||
from novaclient.v1_1 import usage
|
||||
@ -61,24 +62,46 @@ class Client(object):
|
||||
>>> client.flavors.list()
|
||||
...
|
||||
|
||||
It is also possible to use an instance as a context manager in which
|
||||
case there will be a session kept alive for the duration of the with
|
||||
statement::
|
||||
|
||||
>>> with Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL) as client:
|
||||
... client.servers.list()
|
||||
... client.flavors.list()
|
||||
...
|
||||
|
||||
It is also possible to have a permanent (process-long) connection pool,
|
||||
by passing a connection_pool=True::
|
||||
|
||||
>>> client = Client(USERNAME, PASSWORD, PROJECT_ID,
|
||||
... AUTH_URL, connection_pool=True)
|
||||
"""
|
||||
|
||||
# FIXME(jesse): project_id isn't required to authenticate
|
||||
def __init__(self, username, api_key, project_id, auth_url=None,
|
||||
insecure=False, timeout=None, proxy_tenant_id=None,
|
||||
proxy_token=None, region_name=None,
|
||||
endpoint_type='publicURL', extensions=None,
|
||||
service_type='compute', service_name=None,
|
||||
volume_service_name=None, timings=False,
|
||||
bypass_url=None, os_cache=False, no_cache=True,
|
||||
http_log_debug=False, auth_system='keystone',
|
||||
auth_plugin=None, auth_token=None,
|
||||
cacert=None, tenant_id=None):
|
||||
def __init__(self, username=None, api_key=None, project_id=None,
|
||||
auth_url=None, insecure=False, timeout=None,
|
||||
proxy_tenant_id=None, proxy_token=None, region_name=None,
|
||||
endpoint_type='publicURL', extensions=None,
|
||||
service_type='compute', service_name=None,
|
||||
volume_service_name=None, timings=False, bypass_url=None,
|
||||
os_cache=False, no_cache=True, http_log_debug=False,
|
||||
auth_system='keystone', auth_plugin=None, auth_token=None,
|
||||
cacert=None, tenant_id=None, user_id=None,
|
||||
connection_pool=False, session=None, auth=None,
|
||||
completion_cache=None):
|
||||
# FIXME(comstud): Rename the api_key argument above when we
|
||||
# know it's not being used as keyword argument
|
||||
|
||||
# NOTE(cyeoh): In the novaclient context (unlike Nova) the
|
||||
# project_id is not the same as the tenant_id. Here project_id
|
||||
# is a name (what the Nova API often refers to as a project or
|
||||
# tenant name) and tenant_id is a UUID (what the Nova API
|
||||
# often refers to as a project_id or tenant_id).
|
||||
|
||||
password = api_key
|
||||
self.projectid = project_id
|
||||
self.tenant_id = tenant_id
|
||||
self.user_id = user_id
|
||||
self.flavors = flavors.FlavorManager(self)
|
||||
self.flavor_access = flavor_access.FlavorAccessManager(self)
|
||||
self.images = images.ImageManager(self)
|
||||
@ -116,6 +139,7 @@ class Client(object):
|
||||
self.os_cache = os_cache or not no_cache
|
||||
self.availability_zones = \
|
||||
availability_zones.AvailabilityZoneManager(self)
|
||||
self.server_groups = server_groups.ServerGroupsManager(self)
|
||||
|
||||
# Add in any extensions...
|
||||
if extensions:
|
||||
@ -124,38 +148,66 @@ class Client(object):
|
||||
setattr(self, extension.name,
|
||||
extension.manager_class(self))
|
||||
|
||||
self.client = client.HTTPClient(username,
|
||||
password,
|
||||
projectid=project_id,
|
||||
tenant_id=tenant_id,
|
||||
auth_url=auth_url,
|
||||
auth_token=auth_token,
|
||||
insecure=insecure,
|
||||
timeout=timeout,
|
||||
auth_system=auth_system,
|
||||
auth_plugin=auth_plugin,
|
||||
proxy_token=proxy_token,
|
||||
proxy_tenant_id=proxy_tenant_id,
|
||||
region_name=region_name,
|
||||
endpoint_type=endpoint_type,
|
||||
service_type=service_type,
|
||||
service_name=service_name,
|
||||
volume_service_name=volume_service_name,
|
||||
timings=timings,
|
||||
bypass_url=bypass_url,
|
||||
os_cache=self.os_cache,
|
||||
http_log_debug=http_log_debug,
|
||||
cacert=cacert)
|
||||
self.client = client._construct_http_client(
|
||||
username=username,
|
||||
password=password,
|
||||
user_id=user_id,
|
||||
project_id=project_id,
|
||||
tenant_id=tenant_id,
|
||||
auth_url=auth_url,
|
||||
auth_token=auth_token,
|
||||
insecure=insecure,
|
||||
timeout=timeout,
|
||||
auth_system=auth_system,
|
||||
auth_plugin=auth_plugin,
|
||||
proxy_token=proxy_token,
|
||||
proxy_tenant_id=proxy_tenant_id,
|
||||
region_name=region_name,
|
||||
endpoint_type=endpoint_type,
|
||||
service_type=service_type,
|
||||
service_name=service_name,
|
||||
volume_service_name=volume_service_name,
|
||||
timings=timings,
|
||||
bypass_url=bypass_url,
|
||||
os_cache=self.os_cache,
|
||||
http_log_debug=http_log_debug,
|
||||
cacert=cacert,
|
||||
connection_pool=connection_pool,
|
||||
session=session,
|
||||
auth=auth)
|
||||
|
||||
self.completion_cache = completion_cache
|
||||
|
||||
def write_object_to_completion_cache(self, obj):
|
||||
if self.completion_cache:
|
||||
self.completion_cache.write_object(obj)
|
||||
|
||||
def clear_completion_cache_for_class(self, obj_class):
|
||||
if self.completion_cache:
|
||||
self.completion_cache.clear_class(obj_class)
|
||||
|
||||
@client._original_only
|
||||
def __enter__(self):
|
||||
self.client.open_session()
|
||||
return self
|
||||
|
||||
@client._original_only
|
||||
def __exit__(self, t, v, tb):
|
||||
self.client.close_session()
|
||||
|
||||
@client._original_only
|
||||
def set_management_url(self, url):
|
||||
self.client.set_management_url(url)
|
||||
|
||||
@client._original_only
|
||||
def get_timings(self):
|
||||
return self.client.get_timings()
|
||||
|
||||
@client._original_only
|
||||
def reset_timings(self):
|
||||
self.client.reset_timings()
|
||||
|
||||
@client._original_only
|
||||
def authenticate(self):
|
||||
"""
|
||||
Authenticate against the server.
|
||||
|
||||
@ -194,7 +194,7 @@ def do_baremetal_node_create(cs, args):
|
||||
|
||||
@utils.arg('node',
|
||||
metavar='<node>',
|
||||
help='ID of the node to delete.')
|
||||
help=_('ID of the node to delete.'))
|
||||
def do_baremetal_node_delete(cs, args):
|
||||
"""Remove a baremetal node and any associated interfaces."""
|
||||
node = _find_baremetal_node(cs, args.node)
|
||||
@ -219,10 +219,22 @@ def _translate_baremetal_node_keys(collection):
|
||||
|
||||
def _print_baremetal_nodes_list(nodes):
|
||||
"""Print the list of baremetal nodes."""
|
||||
|
||||
def _parse_address(fields):
|
||||
macs = []
|
||||
for interface in fields.interfaces:
|
||||
macs.append(interface['address'])
|
||||
return ', '.join("%s" % i for i in macs)
|
||||
|
||||
formatters = {
|
||||
'MAC Address': _parse_address
|
||||
}
|
||||
|
||||
_translate_baremetal_node_keys(nodes)
|
||||
utils.print_list(nodes, [
|
||||
'ID',
|
||||
'Host',
|
||||
'Task State',
|
||||
'CPUs',
|
||||
'Memory_MB',
|
||||
'Disk_GB',
|
||||
@ -231,7 +243,7 @@ def _print_baremetal_nodes_list(nodes):
|
||||
'PM Username',
|
||||
'PM Password',
|
||||
'Terminal Port',
|
||||
])
|
||||
], formatters=formatters)
|
||||
|
||||
|
||||
def do_baremetal_node_list(cs, _args):
|
||||
@ -263,7 +275,7 @@ def _print_baremetal_node_interfaces(interfaces):
|
||||
|
||||
@utils.arg('node',
|
||||
metavar='<node>',
|
||||
help="ID of node")
|
||||
help=_("ID of node"))
|
||||
def do_baremetal_node_show(cs, args):
|
||||
"""Show information about a baremetal node."""
|
||||
node = _find_baremetal_node(cs, args.node)
|
||||
@ -298,7 +310,7 @@ def do_baremetal_interface_remove(cs, args):
|
||||
cs.baremetal.remove_interface(args.node, args.address)
|
||||
|
||||
|
||||
@utils.arg('node', metavar='<node>', help="ID of node")
|
||||
@utils.arg('node', metavar='<node>', help=_("ID of node"))
|
||||
def do_baremetal_interface_list(cs, args):
|
||||
"""List network interfaces associated with a baremetal node."""
|
||||
interfaces = cs.baremetal.list_interfaces(args.node)
|
||||
|
||||
@ -21,7 +21,8 @@ from novaclient.openstack.common.gettextutils import _
|
||||
|
||||
class FlavorAccess(base.Resource):
|
||||
def __repr__(self):
|
||||
return "<FlavorAccess: %s>" % self.name
|
||||
return ("<FlavorAccess flavor id: %s, tenant id: %s>" %
|
||||
(self.flavor_id, self.tenant_id))
|
||||
|
||||
|
||||
class FlavorAccessManager(base.ManagerWithFind):
|
||||
|
||||
@ -83,9 +83,9 @@ class Flavor(base.Resource):
|
||||
:param keys: A list of keys to be unset
|
||||
"""
|
||||
for k in keys:
|
||||
return self.manager._delete(
|
||||
"/flavors/%s/os-extra_specs/%s" % (
|
||||
base.getid(self), k))
|
||||
self.manager._delete(
|
||||
"/flavors/%s/os-extra_specs/%s" % (
|
||||
base.getid(self), k))
|
||||
|
||||
def delete(self):
|
||||
"""
|
||||
|
||||
@ -28,11 +28,14 @@ class FloatingIP(base.Resource):
|
||||
class FloatingIPManager(base.ManagerWithFind):
|
||||
resource_class = FloatingIP
|
||||
|
||||
def list(self):
|
||||
def list(self, all_tenants=False):
|
||||
"""
|
||||
List floating ips for a tenant
|
||||
List floating ips
|
||||
"""
|
||||
return self._list("/os-floating-ips", "floating_ips")
|
||||
url = '/os-floating-ips'
|
||||
if all_tenants:
|
||||
url += '?all_tenants=1'
|
||||
return self._list(url, "floating_ips")
|
||||
|
||||
def create(self, pool=None):
|
||||
"""
|
||||
|
||||
@ -53,6 +53,7 @@ class Keypair(base.Resource):
|
||||
class KeypairManager(base.ManagerWithFind):
|
||||
resource_class = Keypair
|
||||
keypair_prefix = "os-keypairs"
|
||||
is_alphanum_id_allowed = True
|
||||
|
||||
def get(self, keypair):
|
||||
"""
|
||||
|
||||
@ -86,6 +86,7 @@ class NetworkManager(base.ManagerWithFind):
|
||||
:param multi_host: str
|
||||
:param priority: str
|
||||
:param project_id: str
|
||||
:param vlan: int
|
||||
:param vlan_start: int
|
||||
:param vpn_start: int
|
||||
|
||||
|
||||
71
awx/lib/site-packages/novaclient/v1_1/server_groups.py
Normal file
71
awx/lib/site-packages/novaclient/v1_1/server_groups.py
Normal file
@ -0,0 +1,71 @@
|
||||
# Copyright (c) 2014 VMware, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Server group interface.
|
||||
"""
|
||||
|
||||
from novaclient import base
|
||||
|
||||
|
||||
class ServerGroup(base.Resource):
|
||||
"""
|
||||
A server group.
|
||||
"""
|
||||
NAME_ATTR = 'server_group_name'
|
||||
|
||||
def __repr__(self):
|
||||
return '<ServerGroup: %s>' % self.id
|
||||
|
||||
def delete(self):
|
||||
self.manager.delete(self)
|
||||
|
||||
|
||||
class ServerGroupsManager(base.ManagerWithFind):
|
||||
"""
|
||||
Manage :class:`ServerGroup` resources.
|
||||
"""
|
||||
resource_class = ServerGroup
|
||||
|
||||
def list(self):
|
||||
"""Get a list of all server groups.
|
||||
|
||||
:rtype: list of :class:`ServerGroup`.
|
||||
"""
|
||||
return self._list('/os-server-groups', 'server_groups')
|
||||
|
||||
def get(self, id):
|
||||
"""Get a specific server group.
|
||||
|
||||
:param id: The ID of the :class:`ServerGroup` to get.
|
||||
:rtype: :class:`ServerGroup`
|
||||
"""
|
||||
return self._get('/os-server-groups/%s' % id,
|
||||
'server_group')
|
||||
|
||||
def delete(self, id):
|
||||
"""Delete a specific server group.
|
||||
|
||||
:param id: The ID of the :class:`ServerGroup` to delete.
|
||||
"""
|
||||
self._delete('/os-server-groups/%s' % id)
|
||||
|
||||
def create(self, **kwargs):
|
||||
"""Create (allocate) a server group.
|
||||
|
||||
:rtype: list of :class:`ServerGroup`
|
||||
"""
|
||||
body = {'server_group': kwargs}
|
||||
return self._create('/os-server-groups', body, 'server_group')
|
||||
@ -26,6 +26,7 @@ from six.moves.urllib import parse
|
||||
|
||||
from novaclient import base
|
||||
from novaclient import crypto
|
||||
from novaclient.openstack.common.gettextutils import _
|
||||
from novaclient.openstack.common import strutils
|
||||
from novaclient.v1_1.security_groups import SecurityGroup
|
||||
|
||||
@ -36,7 +37,7 @@ class Server(base.Resource):
|
||||
HUMAN_ID = True
|
||||
|
||||
def __repr__(self):
|
||||
return "<Server: %s>" % self.name
|
||||
return '<Server: %s>' % getattr(self, 'name', 'unknown-name')
|
||||
|
||||
def delete(self):
|
||||
"""
|
||||
@ -434,8 +435,8 @@ class ServerManager(base.BootingManagerWithFind):
|
||||
connected networks, fixed ips, etc.
|
||||
:param scheduler_hints: (optional extension) arbitrary key-value pairs
|
||||
specified by the client to help boot an instance.
|
||||
:param config_drive: (optional extension) value for config drive
|
||||
either boolean, or volume-id
|
||||
:param config_drive: (optional extension) If True, enable config drive
|
||||
on the server.
|
||||
:param admin_pass: admin password for the server.
|
||||
:param disk_config: (optional extension) control how the disk is
|
||||
partitioned when the server is created.
|
||||
@ -454,7 +455,8 @@ class ServerManager(base.BootingManagerWithFind):
|
||||
else:
|
||||
userdata = strutils.safe_encode(userdata)
|
||||
|
||||
body["server"]["user_data"] = base64.b64encode(userdata)
|
||||
userdata_b64 = base64.b64encode(userdata).decode('utf-8')
|
||||
body["server"]["user_data"] = userdata_b64
|
||||
if meta:
|
||||
body["server"]["metadata"] = meta
|
||||
if reservation_id:
|
||||
@ -490,9 +492,11 @@ class ServerManager(base.BootingManagerWithFind):
|
||||
data = file_or_string.read()
|
||||
else:
|
||||
data = file_or_string
|
||||
|
||||
cont = base64.b64encode(data.encode('utf-8')).decode('utf-8')
|
||||
personality.append({
|
||||
'path': filepath,
|
||||
'contents': base64.b64encode(data.encode('utf-8')),
|
||||
'contents': cont,
|
||||
})
|
||||
|
||||
if availability_zone:
|
||||
@ -520,8 +524,15 @@ class ServerManager(base.BootingManagerWithFind):
|
||||
# if value is empty string, do not send value in body
|
||||
if nic_info.get('net-id'):
|
||||
net_data['uuid'] = nic_info['net-id']
|
||||
if nic_info.get('v4-fixed-ip'):
|
||||
if (nic_info.get('v4-fixed-ip') and
|
||||
nic_info.get('v6-fixed-ip')):
|
||||
raise base.exceptions.CommandError(_(
|
||||
"Only one of 'v4-fixed-ip' and 'v6-fixed-ip' may be"
|
||||
" provided."))
|
||||
elif nic_info.get('v4-fixed-ip'):
|
||||
net_data['fixed_ip'] = nic_info['v4-fixed-ip']
|
||||
elif nic_info.get('v6-fixed-ip'):
|
||||
net_data['fixed_ip'] = nic_info['v6-fixed-ip']
|
||||
if nic_info.get('port-id'):
|
||||
net_data['port'] = nic_info['port-id']
|
||||
all_net_data.append(net_data)
|
||||
|
||||
@ -69,3 +69,7 @@ class ServiceManager(base.ManagerWithFind):
|
||||
"""Disable the service with reason."""
|
||||
body = self._update_body(host, binary, reason)
|
||||
return self._update("/os-services/disable-log-reason", body, "service")
|
||||
|
||||
def delete(self, service_id):
|
||||
"""Delete a service."""
|
||||
return self._delete("/os-services/%s" % service_id)
|
||||
|
||||
@ -129,19 +129,8 @@ def _parse_block_device_mapping_v2(args, image):
|
||||
return bdm
|
||||
|
||||
|
||||
def _boot(cs, args, reservation_id=None, min_count=None, max_count=None):
|
||||
def _boot(cs, args):
|
||||
"""Boot a new server."""
|
||||
if min_count is None:
|
||||
min_count = 1
|
||||
if max_count is None:
|
||||
max_count = min_count
|
||||
if min_count > max_count:
|
||||
raise exceptions.CommandError(_("min_instances should be <= "
|
||||
"max_instances"))
|
||||
if not min_count or not max_count:
|
||||
raise exceptions.CommandError(_("min_instances nor max_instances "
|
||||
"should be 0"))
|
||||
|
||||
if args.image:
|
||||
image = _find_image(cs, args.image)
|
||||
else:
|
||||
@ -157,10 +146,31 @@ def _boot(cs, args, reservation_id=None, min_count=None, max_count=None):
|
||||
if not args.flavor:
|
||||
raise exceptions.CommandError(_("you need to specify a Flavor ID "))
|
||||
|
||||
if args.num_instances is not None:
|
||||
if args.num_instances <= 1:
|
||||
raise exceptions.CommandError(_("num_instances should be > 1"))
|
||||
min_count = 1
|
||||
max_count = 1
|
||||
# Don't let user mix num_instances and max_count/min_count.
|
||||
if (args.num_instances is not None and
|
||||
args.min_count is None and args.max_count is None):
|
||||
if args.num_instances < 1:
|
||||
raise exceptions.CommandError(_("num_instances should be >= 1"))
|
||||
max_count = args.num_instances
|
||||
elif (args.num_instances is not None and
|
||||
(args.min_count is not None or args.max_count is not None)):
|
||||
raise exceptions.CommandError(_("Don't mix num-instances and "
|
||||
"max/min-count"))
|
||||
if args.min_count is not None:
|
||||
if args.min_count < 1:
|
||||
raise exceptions.CommandError(_("min_count should be >= 1"))
|
||||
min_count = args.min_count
|
||||
max_count = min_count
|
||||
if args.max_count is not None:
|
||||
if args.max_count < 1:
|
||||
raise exceptions.CommandError(_("max_count should be >= 1"))
|
||||
max_count = args.max_count
|
||||
if (args.min_count is not None and args.max_count is not None and
|
||||
args.min_count > args.max_count):
|
||||
raise exceptions.CommandError(_(
|
||||
"min_count should be <= max_count"))
|
||||
|
||||
flavor = _find_flavor(cs, args.flavor)
|
||||
|
||||
@ -235,9 +245,10 @@ def _boot(cs, args, reservation_id=None, min_count=None, max_count=None):
|
||||
for nic_str in args.nics:
|
||||
err_msg = (_("Invalid nic argument '%s'. Nic arguments must be of "
|
||||
"the form --nic <net-id=net-uuid,v4-fixed-ip=ip-addr,"
|
||||
"port-id=port-uuid>, with at minimum net-id or port-id "
|
||||
"specified.") % nic_str)
|
||||
nic_info = {"net-id": "", "v4-fixed-ip": "", "port-id": ""}
|
||||
"v6-fixed-ip=ip-addr,port-id=port-uuid>, with at minimum "
|
||||
"net-id or port-id (but not both) specified.") % nic_str)
|
||||
nic_info = {"net-id": "", "v4-fixed-ip": "", "v6-fixed-ip": "",
|
||||
"port-id": ""}
|
||||
|
||||
for kv_str in nic_str.split(","):
|
||||
try:
|
||||
@ -250,7 +261,7 @@ def _boot(cs, args, reservation_id=None, min_count=None, max_count=None):
|
||||
else:
|
||||
raise exceptions.CommandError(err_msg)
|
||||
|
||||
if not nic_info['net-id'] and not nic_info['port-id']:
|
||||
if bool(nic_info['net-id']) == bool(nic_info['port-id']):
|
||||
raise exceptions.CommandError(err_msg)
|
||||
|
||||
nics.append(nic_info)
|
||||
@ -280,7 +291,6 @@ def _boot(cs, args, reservation_id=None, min_count=None, max_count=None):
|
||||
meta=meta,
|
||||
files=files,
|
||||
key_name=key_name,
|
||||
reservation_id=reservation_id,
|
||||
min_count=min_count,
|
||||
max_count=max_count,
|
||||
userdata=userdata,
|
||||
@ -321,7 +331,17 @@ def _boot(cs, args, reservation_id=None, min_count=None, max_count=None):
|
||||
default=None,
|
||||
type=int,
|
||||
metavar='<number>',
|
||||
help=_("boot multiple servers at a time (limited by quota)."))
|
||||
help=argparse.SUPPRESS)
|
||||
@utils.arg('--min-count',
|
||||
default=None,
|
||||
type=int,
|
||||
metavar='<number>',
|
||||
help=_("Boot at least <number> servers (limited by quota)."))
|
||||
@utils.arg('--max-count',
|
||||
default=None,
|
||||
type=int,
|
||||
metavar='<number>',
|
||||
help=_("Boot up to <number> servers (limited by quota)."))
|
||||
@utils.arg('--meta',
|
||||
metavar="<key=value>",
|
||||
action='append',
|
||||
@ -374,16 +394,25 @@ def _boot(cs, args, reservation_id=None, min_count=None, max_count=None):
|
||||
action='append',
|
||||
default=[],
|
||||
help=_("Block device mapping with the keys: "
|
||||
"id=image_id, snapshot_id or volume_id, "
|
||||
"id=UUID (image_id, snapshot_id or volume_id only if using source "
|
||||
"image, snapshot or volume) "
|
||||
"source=source type (image, snapshot, volume or blank), "
|
||||
"dest=destination type of the block device (volume or local), "
|
||||
"bus=device's bus, "
|
||||
"device=name of the device (e.g. vda, xda, ...), "
|
||||
"size=size of the block device in GB, "
|
||||
"format=device will be formatted (e.g. swap, ext3, ntfs, ...), "
|
||||
"bootindex=integer used for ordering the boot disks, "
|
||||
"type=device type (e.g. disk, cdrom, ...) and "
|
||||
"shutdown=shutdown behaviour (either preserve or remove)."))
|
||||
"bus=device's bus (e.g. uml, lxc, virtio, ...; if omitted, "
|
||||
"hypervisor driver chooses a suitable default, "
|
||||
"honoured only if device type is supplied) "
|
||||
"type=device type (e.g. disk, cdrom, ...; defaults to 'disk') "
|
||||
"device=name of the device (e.g. vda, xda, ...; "
|
||||
"if omitted, hypervisor driver chooses suitable device "
|
||||
"depending on selected bus), "
|
||||
"size=size of the block device in GB (if omitted, "
|
||||
"hypervisor driver calculates size), "
|
||||
"format=device will be formatted (e.g. swap, ntfs, ...; optional), "
|
||||
"bootindex=integer used for ordering the boot disks "
|
||||
"(for image backed instances it is equal to 0, "
|
||||
"for others need to be specified) and "
|
||||
"shutdown=shutdown behaviour (either preserve or remove, "
|
||||
"for local destination set to remove)."))
|
||||
@utils.arg('--swap',
|
||||
metavar="<swap_size>",
|
||||
default=None,
|
||||
@ -402,17 +431,19 @@ def _boot(cs, args, reservation_id=None, min_count=None, max_count=None):
|
||||
help=_("Send arbitrary key/value pairs to the scheduler for custom "
|
||||
"use."))
|
||||
@utils.arg('--nic',
|
||||
metavar="<net-id=net-uuid,v4-fixed-ip=ip-addr,port-id=port-uuid>",
|
||||
metavar="<net-id=net-uuid,v4-fixed-ip=ip-addr,v6-fixed-ip=ip-addr,"
|
||||
"port-id=port-uuid>",
|
||||
action='append',
|
||||
dest='nics',
|
||||
default=[],
|
||||
help=_("Create a NIC on the server. "
|
||||
"Specify option multiple times to create multiple NICs. "
|
||||
"net-id: attach NIC to network with this UUID "
|
||||
"(required if no port-id), "
|
||||
"(either port-id or net-id must be provided), "
|
||||
"v4-fixed-ip: IPv4 fixed address for NIC (optional), "
|
||||
"v6-fixed-ip: IPv6 fixed address for NIC (optional), "
|
||||
"port-id: attach NIC to port with this UUID "
|
||||
"(required if no net-id)"))
|
||||
"(either port-id or net-id must be provided)."))
|
||||
@utils.arg('--config-drive',
|
||||
metavar="<value>",
|
||||
dest='config_drive',
|
||||
@ -422,7 +453,7 @@ def _boot(cs, args, reservation_id=None, min_count=None, max_count=None):
|
||||
dest='poll',
|
||||
action="store_true",
|
||||
default=False,
|
||||
help=_('Blocks while server builds so progress can be reported.'))
|
||||
help=_('Report the new server boot progress until it completes.'))
|
||||
def do_boot(cs, args):
|
||||
"""Boot a new server."""
|
||||
boot_args, boot_kwargs = _boot(cs, args)
|
||||
@ -494,7 +525,7 @@ def _poll_for_status(poll_fn, obj_id, action, final_ok_states,
|
||||
elif status == "error":
|
||||
if not silent:
|
||||
print(_("\nError %s server") % action)
|
||||
break
|
||||
raise exceptions.InstanceInErrorState(obj.fault['message'])
|
||||
|
||||
if not silent:
|
||||
print_progress(progress)
|
||||
@ -555,7 +586,7 @@ def _print_flavor_list(flavors, show_extra_specs=False):
|
||||
'Memory_MB',
|
||||
'Disk',
|
||||
'Ephemeral',
|
||||
'Swap',
|
||||
'Swap_MB',
|
||||
'VCPUs',
|
||||
'RXTX_Factor',
|
||||
'Is_Public',
|
||||
@ -711,7 +742,7 @@ def do_flavor_access_list(cs, args):
|
||||
help=_('Tenant ID to add flavor access for.'))
|
||||
def do_flavor_access_add(cs, args):
|
||||
"""Add flavor access for the given tenant."""
|
||||
flavor = _find_flavor_for_admin(cs, args.flavor)
|
||||
flavor = _find_flavor(cs, args.flavor)
|
||||
access_list = cs.flavor_access.add_tenant_access(flavor, args.tenant)
|
||||
columns = ['Flavor_ID', 'Tenant_ID']
|
||||
utils.print_list(access_list, columns)
|
||||
@ -724,7 +755,7 @@ def do_flavor_access_add(cs, args):
|
||||
help=_('Tenant ID to remove flavor access for.'))
|
||||
def do_flavor_access_remove(cs, args):
|
||||
"""Remove flavor access for the given tenant."""
|
||||
flavor = _find_flavor_for_admin(cs, args.flavor)
|
||||
flavor = _find_flavor(cs, args.flavor)
|
||||
access_list = cs.flavor_access.remove_tenant_access(flavor, args.tenant)
|
||||
columns = ['Flavor_ID', 'Tenant_ID']
|
||||
utils.print_list(access_list, columns)
|
||||
@ -813,7 +844,7 @@ def _filter_network_create_options(args):
|
||||
valid_args = ['label', 'cidr', 'vlan_start', 'vpn_start', 'cidr_v6',
|
||||
'gateway', 'gateway_v6', 'bridge', 'bridge_interface',
|
||||
'multi_host', 'dns1', 'dns2', 'uuid', 'fixed_cidr',
|
||||
'project_id', 'priority']
|
||||
'project_id', 'priority', 'vlan']
|
||||
kwargs = {}
|
||||
for k, v in args.__dict__.items():
|
||||
if k in valid_args and v is not None:
|
||||
@ -833,9 +864,14 @@ def _filter_network_create_options(args):
|
||||
dest="cidr_v6",
|
||||
help=_('IPv6 subnet (ex: fe80::/64'))
|
||||
@utils.arg('--vlan',
|
||||
dest='vlan_start',
|
||||
dest='vlan',
|
||||
metavar='<vlan id>',
|
||||
help=_("vlan id"))
|
||||
help=_("vlan id to be assigned to project"))
|
||||
@utils.arg('--vlan-start',
|
||||
dest='vlan_start',
|
||||
metavar='<vlan start>',
|
||||
help=_('First vlan ID to be assigned to project. Subsequent vlan'
|
||||
' IDs will be assigned incrementally'))
|
||||
@utils.arg('--vpn',
|
||||
dest='vpn_start',
|
||||
metavar='<vpn start>',
|
||||
@ -1020,13 +1056,12 @@ def do_image_delete(cs, args):
|
||||
dest='ip',
|
||||
metavar='<ip-regexp>',
|
||||
default=None,
|
||||
help=_('Search with regular expression match by IP address (Admin only).'))
|
||||
help=_('Search with regular expression match by IP address.'))
|
||||
@utils.arg('--ip6',
|
||||
dest='ip6',
|
||||
metavar='<ip6-regexp>',
|
||||
default=None,
|
||||
help=_('Search with regular expression match by IPv6 address '
|
||||
'(Admin only).'))
|
||||
help=_('Search with regular expression match by IPv6 address.'))
|
||||
@utils.arg('--name',
|
||||
dest='name',
|
||||
metavar='<name-regexp>',
|
||||
@ -1036,8 +1071,7 @@ def do_image_delete(cs, args):
|
||||
dest='instance_name',
|
||||
metavar='<name-regexp>',
|
||||
default=None,
|
||||
help=_('Search with regular expression match by server name '
|
||||
'(Admin only).'))
|
||||
help=_('Search with regular expression match by server name.'))
|
||||
@utils.arg('--instance_name',
|
||||
help=argparse.SUPPRESS)
|
||||
@utils.arg('--status',
|
||||
@ -1174,7 +1208,7 @@ def do_list(cs, args):
|
||||
dest='poll',
|
||||
action="store_true",
|
||||
default=False,
|
||||
help=_('Blocks while server is rebooting.'))
|
||||
help=_('Poll until reboot is complete.'))
|
||||
def do_reboot(cs, args):
|
||||
"""Reboot a server."""
|
||||
server = _find_server(cs, args.server)
|
||||
@ -1198,7 +1232,7 @@ def do_reboot(cs, args):
|
||||
dest='poll',
|
||||
action="store_true",
|
||||
default=False,
|
||||
help=_('Blocks while server rebuilds so progress can be reported.'))
|
||||
help=_('Report the server rebuild progress until it completes.'))
|
||||
@utils.arg('--minimal',
|
||||
dest='minimal',
|
||||
action="store_true",
|
||||
@ -1220,8 +1254,8 @@ def do_rebuild(cs, args):
|
||||
|
||||
kwargs = utils.get_resource_manager_extra_kwargs(do_rebuild, args)
|
||||
kwargs['preserve_ephemeral'] = args.preserve_ephemeral
|
||||
server.rebuild(image, _password, **kwargs)
|
||||
_print_server(cs, args)
|
||||
server = server.rebuild(image, _password, **kwargs)
|
||||
_print_server(cs, args, server)
|
||||
|
||||
if args.poll:
|
||||
_poll_for_status(cs.servers.get, server.id, 'rebuilding', ['active'])
|
||||
@ -1241,7 +1275,7 @@ def do_rename(cs, args):
|
||||
dest='poll',
|
||||
action="store_true",
|
||||
default=False,
|
||||
help=_('Blocks while servers resizes so progress can be reported.'))
|
||||
help=_('Report the server resize progress until it completes.'))
|
||||
def do_resize(cs, args):
|
||||
"""Resize a server."""
|
||||
server = _find_server(cs, args.server)
|
||||
@ -1270,7 +1304,7 @@ def do_resize_revert(cs, args):
|
||||
dest='poll',
|
||||
action="store_true",
|
||||
default=False,
|
||||
help=_('Blocks while server migrates so progress can be reported.'))
|
||||
help=_('Report the server migration progress until it completes.'))
|
||||
def do_migrate(cs, args):
|
||||
"""Migrate a server. The new host will be selected by the scheduler."""
|
||||
server = _find_server(cs, args.server)
|
||||
@ -1331,13 +1365,15 @@ def do_resume(cs, args):
|
||||
|
||||
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
||||
def do_rescue(cs, args):
|
||||
"""Rescue a server."""
|
||||
"""Reboots a server into rescue mode, which starts the machine
|
||||
from the initial image, attaching the current boot disk as secondary.
|
||||
"""
|
||||
utils.print_dict(_find_server(cs, args.server).rescue()[1])
|
||||
|
||||
|
||||
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
||||
def do_unrescue(cs, args):
|
||||
"""Unrescue a server."""
|
||||
"""Restart the server from normal boot disk again."""
|
||||
_find_server(cs, args.server).unrescue()
|
||||
|
||||
|
||||
@ -1400,7 +1436,8 @@ def do_root_password(cs, args):
|
||||
dest='poll',
|
||||
action="store_true",
|
||||
default=False,
|
||||
help=_('Blocks while server snapshots so progress can be reported.'))
|
||||
help=_('Report the snapshot progress and poll until image creation is '
|
||||
'complete.'))
|
||||
def do_image_create(cs, args):
|
||||
"""Create a new image by taking a snapshot of a running server."""
|
||||
server = _find_server(cs, args.server)
|
||||
@ -1535,6 +1572,7 @@ def do_delete(cs, args):
|
||||
for server in args.server:
|
||||
try:
|
||||
_find_server(cs, server).delete()
|
||||
print(_("Request to delete server %s has been accepted.") % server)
|
||||
except Exception as e:
|
||||
failure_count += 1
|
||||
print(e)
|
||||
@ -1554,18 +1592,10 @@ def _find_image(cs, image):
|
||||
return utils.find_resource(cs.images, image)
|
||||
|
||||
|
||||
def _find_flavor_for_admin(cs, flavor):
|
||||
"""Get a flavor for administrator by name, ID, or RAM size."""
|
||||
try:
|
||||
return utils.find_resource(cs.flavors, flavor, is_public=None)
|
||||
except exceptions.NotFound:
|
||||
return cs.flavors.find(ram=flavor)
|
||||
|
||||
|
||||
def _find_flavor(cs, flavor):
|
||||
"""Get a flavor by name, ID, or RAM size."""
|
||||
try:
|
||||
return utils.find_resource(cs.flavors, flavor)
|
||||
return utils.find_resource(cs.flavors, flavor, is_public=None)
|
||||
except exceptions.NotFound:
|
||||
return cs.flavors.find(ram=flavor)
|
||||
|
||||
@ -1746,17 +1776,16 @@ def do_volume_attach(cs, args):
|
||||
metavar='<server>',
|
||||
help=_('Name or ID of server.'))
|
||||
@utils.arg('attachment_id',
|
||||
metavar='<volume>',
|
||||
metavar='<attachment>',
|
||||
help=_('Attachment ID of the volume.'))
|
||||
@utils.arg('new_volume',
|
||||
metavar='<volume>',
|
||||
help=_('ID of the volume to attach.'))
|
||||
def do_volume_update(cs, args):
|
||||
"""Update volume attachment."""
|
||||
volume = cs.volumes.update_server_volume(_find_server(cs, args.server).id,
|
||||
args.attachment_id,
|
||||
args.new_volume)
|
||||
_print_volume(volume)
|
||||
cs.volumes.update_server_volume(_find_server(cs, args.server).id,
|
||||
args.attachment_id,
|
||||
args.new_volume)
|
||||
|
||||
|
||||
@utils.arg('server',
|
||||
@ -1764,11 +1793,11 @@ def do_volume_update(cs, args):
|
||||
help=_('Name or ID of server.'))
|
||||
@utils.arg('attachment_id',
|
||||
metavar='<volume>',
|
||||
help=_('Attachment ID of the volume.'))
|
||||
help=_('ID of the volume to detach.'))
|
||||
def do_volume_detach(cs, args):
|
||||
"""Detach a volume from a server."""
|
||||
cs.volumes.delete_server_volume(_find_server(cs, args.server).id,
|
||||
args.attachment_id)
|
||||
args.attachment_id)
|
||||
|
||||
|
||||
@utils.service_type('volume')
|
||||
@ -1843,7 +1872,7 @@ def do_volume_type_list(cs, args):
|
||||
|
||||
@utils.arg('name',
|
||||
metavar='<name>',
|
||||
help=_("Name of the new flavor"))
|
||||
help=_("Name of the new volume type"))
|
||||
@utils.service_type('volume')
|
||||
def do_volume_type_create(cs, args):
|
||||
"""Create a new volume type."""
|
||||
@ -1856,7 +1885,7 @@ def do_volume_type_create(cs, args):
|
||||
help=_("Unique ID of the volume type to delete"))
|
||||
@utils.service_type('volume')
|
||||
def do_volume_type_delete(cs, args):
|
||||
"""Delete a specific flavor"""
|
||||
"""Delete a specific volume type."""
|
||||
cs.volume_types.delete(args.id)
|
||||
|
||||
|
||||
@ -1935,6 +1964,9 @@ def do_clear_password(cs, args):
|
||||
|
||||
|
||||
def _print_floating_ip_list(floating_ips):
|
||||
convert = [('instance_id', 'server_id')]
|
||||
_translate_keys(floating_ips, convert)
|
||||
|
||||
utils.print_list(floating_ips, ['Ip', 'Server Id', 'Fixed Ip', 'Pool'])
|
||||
|
||||
|
||||
@ -2041,9 +2073,13 @@ def do_floating_ip_delete(cs, args):
|
||||
args.address)
|
||||
|
||||
|
||||
def do_floating_ip_list(cs, _args):
|
||||
"""List floating ips for this tenant."""
|
||||
_print_floating_ip_list(cs.floating_ips.list())
|
||||
@utils.arg('--all-tenants',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=_('Display floatingips from all tenants (Admin only).'))
|
||||
def do_floating_ip_list(cs, args):
|
||||
"""List floating ips."""
|
||||
_print_floating_ip_list(cs.floating_ips.list(args.all_tenants))
|
||||
|
||||
|
||||
def do_floating_ip_pool_list(cs, _args):
|
||||
@ -2435,7 +2471,7 @@ def do_keypair_add(cs, args):
|
||||
@utils.arg('name', metavar='<name>', help=_('Keypair name to delete.'))
|
||||
def do_keypair_delete(cs, args):
|
||||
"""Delete keypair given by its name."""
|
||||
name = args.name
|
||||
name = _find_keypair(cs, args.name)
|
||||
cs.keypairs.delete(name)
|
||||
|
||||
|
||||
@ -2458,10 +2494,15 @@ def _print_keypair(keypair):
|
||||
help=_("Name or ID of keypair"))
|
||||
def do_keypair_show(cs, args):
|
||||
"""Show details about the given keypair."""
|
||||
keypair = cs.keypairs.get(args.keypair)
|
||||
keypair = _find_keypair(cs, args.keypair)
|
||||
_print_keypair(keypair)
|
||||
|
||||
|
||||
def _find_keypair(cs, keypair):
|
||||
"""Get a keypair by name or ID."""
|
||||
return utils.find_resource(cs.keypairs, keypair)
|
||||
|
||||
|
||||
@utils.arg('--tenant',
|
||||
#nova db searches by project_id
|
||||
dest='tenant',
|
||||
@ -2739,7 +2780,8 @@ def do_aggregate_update(cs, args):
|
||||
nargs='+',
|
||||
action='append',
|
||||
default=[],
|
||||
help=_('Metadata to add/update to aggregate'))
|
||||
help=_('Metadata to add/update to aggregate. '
|
||||
'Specify only the key to delete a metadata item.'))
|
||||
def do_aggregate_set_metadata(cs, args):
|
||||
"""Update the metadata associated with the aggregate."""
|
||||
aggregate = _find_aggregate(cs, args.aggregate)
|
||||
@ -2828,14 +2870,27 @@ def do_live_migration(cs, args):
|
||||
args.disk_over_commit)
|
||||
|
||||
|
||||
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
||||
@utils.arg('server', metavar='<server>', nargs='+',
|
||||
help=_('Name or ID of server(s).'))
|
||||
@utils.arg('--active', action='store_const', dest='state',
|
||||
default='error', const='active',
|
||||
help=_('Request the server be reset to "active" state instead '
|
||||
'of "error" state (the default).'))
|
||||
def do_reset_state(cs, args):
|
||||
"""Reset the state of a server."""
|
||||
_find_server(cs, args.server).reset_state(args.state)
|
||||
failure_flag = False
|
||||
|
||||
for server in args.server:
|
||||
try:
|
||||
_find_server(cs, server).reset_state(args.state)
|
||||
except Exception as e:
|
||||
failure_flag = True
|
||||
msg = "Reset state for server %s failed: %s" % (server, e)
|
||||
print(msg)
|
||||
|
||||
if failure_flag:
|
||||
msg = "Unable to reset the state for the specified server(s)."
|
||||
raise exceptions.CommandError(msg)
|
||||
|
||||
|
||||
@utils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
||||
@ -2856,6 +2911,12 @@ def do_service_list(cs, args):
|
||||
# so as not to add the column when the extended ext is not enabled.
|
||||
if result and hasattr(result[0], 'disabled_reason'):
|
||||
columns.append("Disabled Reason")
|
||||
|
||||
# NOTE(gtt): After https://review.openstack.org/#/c/39998/ nova will
|
||||
# show id in response.
|
||||
if result and hasattr(result[0], 'id'):
|
||||
columns.insert(0, "Id")
|
||||
|
||||
utils.print_list(result, columns)
|
||||
|
||||
|
||||
@ -2883,6 +2944,12 @@ def do_service_disable(cs, args):
|
||||
utils.print_list([result], ['Host', 'Binary', 'Status'])
|
||||
|
||||
|
||||
@utils.arg('id', metavar='<id>', help=_('Id of service.'))
|
||||
def do_service_delete(cs, args):
|
||||
"""Delete the service."""
|
||||
cs.services.delete(args.id)
|
||||
|
||||
|
||||
@utils.arg('fixed_ip', metavar='<fixed_ip>', help=_('Fixed IP Address.'))
|
||||
def do_fixed_ip_get(cs, args):
|
||||
"""Retrieve info on a fixed ip."""
|
||||
@ -3328,6 +3395,11 @@ def do_quota_class_show(cs, args):
|
||||
@utils.arg('--floating_ips',
|
||||
type=int,
|
||||
help=argparse.SUPPRESS)
|
||||
@utils.arg('--fixed-ips',
|
||||
metavar='<fixed-ips>',
|
||||
type=int,
|
||||
default=None,
|
||||
help=_('New value for the "fixed-ips" quota.'))
|
||||
@utils.arg('--metadata-items',
|
||||
metavar='<metadata-items>',
|
||||
type=int,
|
||||
@ -3512,3 +3584,73 @@ def do_availability_zone_list(cs, _args):
|
||||
_translate_availability_zone_keys(result)
|
||||
utils.print_list(result, ['Name', 'Status'],
|
||||
sortby_index=None)
|
||||
|
||||
|
||||
def _print_server_group_details(server_group):
|
||||
columns = ['Id', 'Name', 'Policies', 'Members', 'Metadata']
|
||||
utils.print_list(server_group, columns)
|
||||
|
||||
|
||||
def do_server_group_list(cs, args):
|
||||
"""Print a list of all server groups."""
|
||||
server_groups = cs.server_groups.list()
|
||||
_print_server_group_details(server_groups)
|
||||
|
||||
|
||||
@utils.arg('name', metavar='<name>', help='Server group name.')
|
||||
# NOTE(wingwj): The '--policy' way is still reserved here for preserving
|
||||
# the backwards compatibility of CLI, even if a user won't get this usage
|
||||
# in '--help' description. It will be deprecated after an suitable deprecation
|
||||
# period(probably 2 coordinated releases or so).
|
||||
#
|
||||
# Moreover, we imagine that a given user will use only positional parameters or
|
||||
# only the "--policy" option. So we don't need to properly handle
|
||||
# the possibility that they might mix them here. That usage is unsupported.
|
||||
# The related discussion can be found in
|
||||
# https://review.openstack.org/#/c/96382/2/.
|
||||
@utils.arg('policy',
|
||||
metavar='<policy>',
|
||||
default=argparse.SUPPRESS,
|
||||
nargs='*',
|
||||
help='Policies for the server groups '
|
||||
'("affinity" or "anti-affinity")')
|
||||
@utils.arg('--policy',
|
||||
default=[],
|
||||
action='append',
|
||||
help=argparse.SUPPRESS)
|
||||
def do_server_group_create(cs, args):
|
||||
"""Create a new server group with the specified details."""
|
||||
if not args.policy:
|
||||
raise exceptions.CommandError(_("at least one policy must be "
|
||||
"specified"))
|
||||
kwargs = {'name': args.name,
|
||||
'policies': args.policy}
|
||||
server_group = cs.server_groups.create(**kwargs)
|
||||
_print_server_group_details([server_group])
|
||||
|
||||
|
||||
@utils.arg('id', metavar='<id>', nargs='+',
|
||||
help="Unique ID(s) of the server group to delete")
|
||||
def do_server_group_delete(cs, args):
|
||||
"""Delete specific server group(s)."""
|
||||
failure_count = 0
|
||||
|
||||
for sg in args.id:
|
||||
try:
|
||||
cs.server_groups.delete(sg)
|
||||
print(_("Server group %s has been successfully deleted.") % sg)
|
||||
except Exception as e:
|
||||
failure_count += 1
|
||||
print(_("Delete for server group %(sg)s failed: %(e)s") %
|
||||
{'sg': sg, 'e': e})
|
||||
if failure_count == len(args.id):
|
||||
raise exceptions.CommandError(_("Unable to delete any of the "
|
||||
"specified server groups."))
|
||||
|
||||
|
||||
@utils.arg('id', metavar='<id>',
|
||||
help="Unique ID of the server group to get")
|
||||
def do_server_group_get(cs, args):
|
||||
"""Get a specific server group."""
|
||||
server_group = cs.server_groups.get(args.id)
|
||||
_print_server_group_details([server_group])
|
||||
|
||||
@ -24,7 +24,7 @@ from novaclient.v3 import hosts
|
||||
from novaclient.v3 import hypervisors
|
||||
from novaclient.v3 import images
|
||||
from novaclient.v3 import keypairs
|
||||
from novaclient.v3 import quota_classes
|
||||
from novaclient.v3 import list_extensions
|
||||
from novaclient.v3 import quotas
|
||||
from novaclient.v3 import servers
|
||||
from novaclient.v3 import services
|
||||
@ -47,21 +47,42 @@ class Client(object):
|
||||
>>> client.flavors.list()
|
||||
...
|
||||
|
||||
It is also possible to use an instance as a context manager in which
|
||||
case there will be a session kept alive for the duration of the with
|
||||
statement::
|
||||
|
||||
>>> with Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL) as client:
|
||||
... client.servers.list()
|
||||
... client.flavors.list()
|
||||
...
|
||||
|
||||
It is also possible to have a permanent (process-long) connection pool,
|
||||
by passing a connection_pool=True::
|
||||
|
||||
>>> client = Client(USERNAME, PASSWORD, PROJECT_ID,
|
||||
... AUTH_URL, connection_pool=True)
|
||||
"""
|
||||
|
||||
# FIXME(jesse): project_id isn't required to authenticate
|
||||
def __init__(self, username, password, project_id, auth_url=None,
|
||||
insecure=False, timeout=None, proxy_tenant_id=None,
|
||||
proxy_token=None, region_name=None,
|
||||
endpoint_type='publicURL', extensions=None,
|
||||
service_type='computev3', service_name=None,
|
||||
volume_service_name=None, timings=False,
|
||||
bypass_url=None, os_cache=False, no_cache=True,
|
||||
http_log_debug=False, auth_system='keystone',
|
||||
auth_plugin=None, auth_token=None,
|
||||
cacert=None, tenant_id=None):
|
||||
def __init__(self, username=None, password=None, project_id=None,
|
||||
auth_url=None, insecure=False, timeout=None,
|
||||
proxy_tenant_id=None, proxy_token=None, region_name=None,
|
||||
endpoint_type='publicURL', extensions=None,
|
||||
service_type='computev3', service_name=None,
|
||||
volume_service_name=None, timings=False, bypass_url=None,
|
||||
os_cache=False, no_cache=True, http_log_debug=False,
|
||||
auth_system='keystone', auth_plugin=None, auth_token=None,
|
||||
cacert=None, tenant_id=None, user_id=None,
|
||||
connection_pool=False, session=None, auth=None,
|
||||
completion_cache=None):
|
||||
# NOTE(cyeoh): In the novaclient context (unlike Nova) the
|
||||
# project_id is not the same as the tenant_id. Here project_id
|
||||
# is a name (what the Nova API often refers to as a project or
|
||||
# tenant name) and tenant_id is a UUID (what the Nova API
|
||||
# often refers to as a project_id or tenant_id).
|
||||
|
||||
self.projectid = project_id
|
||||
self.tenant_id = tenant_id
|
||||
self.user_id = user_id
|
||||
self.os_cache = os_cache or not no_cache
|
||||
#TODO(bnemec): Add back in v3 extensions
|
||||
self.agents = agents.AgentsManager(self)
|
||||
@ -69,6 +90,7 @@ class Client(object):
|
||||
self.availability_zones = \
|
||||
availability_zones.AvailabilityZoneManager(self)
|
||||
self.certs = certs.CertificateManager(self)
|
||||
self.list_extensions = list_extensions.ListExtManager(self)
|
||||
self.hosts = hosts.HostManager(self)
|
||||
self.flavors = flavors.FlavorManager(self)
|
||||
self.flavor_access = flavor_access.FlavorAccessManager(self)
|
||||
@ -76,7 +98,6 @@ class Client(object):
|
||||
self.images = images.ImageManager(self)
|
||||
self.keypairs = keypairs.KeypairManager(self)
|
||||
self.quotas = quotas.QuotaSetManager(self)
|
||||
self.quota_classes = quota_classes.QuotaClassSetManager(self)
|
||||
self.servers = servers.ServerManager(self)
|
||||
self.services = services.ServiceManager(self)
|
||||
self.usage = usage.UsageManager(self)
|
||||
@ -89,38 +110,66 @@ class Client(object):
|
||||
setattr(self, extension.name,
|
||||
extension.manager_class(self))
|
||||
|
||||
self.client = client.HTTPClient(username,
|
||||
password,
|
||||
projectid=project_id,
|
||||
tenant_id=tenant_id,
|
||||
auth_url=auth_url,
|
||||
insecure=insecure,
|
||||
timeout=timeout,
|
||||
auth_system=auth_system,
|
||||
auth_plugin=auth_plugin,
|
||||
auth_token=auth_token,
|
||||
proxy_token=proxy_token,
|
||||
proxy_tenant_id=proxy_tenant_id,
|
||||
region_name=region_name,
|
||||
endpoint_type=endpoint_type,
|
||||
service_type=service_type,
|
||||
service_name=service_name,
|
||||
volume_service_name=volume_service_name,
|
||||
timings=timings,
|
||||
bypass_url=bypass_url,
|
||||
os_cache=os_cache,
|
||||
http_log_debug=http_log_debug,
|
||||
cacert=cacert)
|
||||
self.client = client._construct_http_client(
|
||||
username=username,
|
||||
password=password,
|
||||
user_id=user_id,
|
||||
project_id=project_id,
|
||||
tenant_id=tenant_id,
|
||||
auth_url=auth_url,
|
||||
auth_token=auth_token,
|
||||
insecure=insecure,
|
||||
timeout=timeout,
|
||||
auth_system=auth_system,
|
||||
auth_plugin=auth_plugin,
|
||||
proxy_token=proxy_token,
|
||||
proxy_tenant_id=proxy_tenant_id,
|
||||
region_name=region_name,
|
||||
endpoint_type=endpoint_type,
|
||||
service_type=service_type,
|
||||
service_name=service_name,
|
||||
volume_service_name=volume_service_name,
|
||||
timings=timings,
|
||||
bypass_url=bypass_url,
|
||||
os_cache=self.os_cache,
|
||||
http_log_debug=http_log_debug,
|
||||
cacert=cacert,
|
||||
connection_pool=connection_pool,
|
||||
session=session,
|
||||
auth=auth)
|
||||
|
||||
self.completion_cache = completion_cache
|
||||
|
||||
def write_object_to_completion_cache(self, obj):
|
||||
if self.completion_cache:
|
||||
self.completion_cache.write_object(obj)
|
||||
|
||||
def clear_completion_cache_for_class(self, obj_class):
|
||||
if self.completion_cache:
|
||||
self.completion_cache.clear_class(obj_class)
|
||||
|
||||
@client._original_only
|
||||
def __enter__(self):
|
||||
self.client.open_session()
|
||||
return self
|
||||
|
||||
@client._original_only
|
||||
def __exit__(self, t, v, tb):
|
||||
self.client.close_session()
|
||||
|
||||
@client._original_only
|
||||
def set_management_url(self, url):
|
||||
self.client.set_management_url(url)
|
||||
|
||||
@client._original_only
|
||||
def get_timings(self):
|
||||
return self.client.get_timings()
|
||||
|
||||
@client._original_only
|
||||
def reset_timings(self):
|
||||
self.client.reset_timings()
|
||||
|
||||
@client._original_only
|
||||
def authenticate(self):
|
||||
"""
|
||||
Authenticate against the server.
|
||||
|
||||
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