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

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

View File

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

View File

@@ -20,17 +20,13 @@ Base utilities to build API operation managers and objects on top of.
""" """
import abc import abc
import contextlib
import hashlib
import inspect import inspect
import os
import threading
import six import six
from novaclient import exceptions from novaclient import exceptions
from novaclient.openstack.common.apiclient import base from novaclient.openstack.common.apiclient import base
from novaclient.openstack.common import cliutils from novaclient import utils
Resource = base.Resource Resource = base.Resource
@@ -46,17 +42,24 @@ def getid(obj):
return obj return obj
class Manager(base.HookableMixin): class Manager(utils.HookableMixin):
""" """
Managers interact with a particular type of API (servers, flavors, images, Managers interact with a particular type of API (servers, flavors, images,
etc.) and provide CRUD operations for them. etc.) and provide CRUD operations for them.
""" """
resource_class = None resource_class = None
cache_lock = threading.RLock()
def __init__(self, api): def __init__(self, api):
self.api = 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): def _list(self, url, response_key, obj_class=None, body=None):
if body: if body:
_resp, body = self.api.client.post(url, body=body) _resp, body = self.api.client.post(url, body=body)
@@ -75,86 +78,22 @@ class Manager(base.HookableMixin):
except KeyError: except KeyError:
pass pass
with self.completion_cache('human_id', obj_class, mode="w"): self._clear_completion_cache_for_class(obj_class)
with self.completion_cache('uuid', obj_class, mode="w"):
return [obj_class(self, res, loaded=True)
for res in data if res]
@contextlib.contextmanager objs = []
def alternate_service_type(self, service_type): for res in data:
original_service_type = self.api.client.service_type if res:
self.api.client.service_type = service_type obj = obj_class(self, res, loaded=True)
try: self._write_object_to_completion_cache(obj)
yield objs.append(obj)
finally:
self.api.client.service_type = original_service_type
@contextlib.contextmanager return objs
def completion_cache(self, cache_type, obj_class, mode):
"""
The completion cache store items that can be used for bash
autocompletion, like UUIDs or human-friendly IDs.
A resource listing will clear and repopulate the cache.
A resource create will append to the cache.
Delete is not handled because listings are assumed to be performed
often enough to keep the cache reasonably up-to-date.
"""
# NOTE(wryan): This lock protects read and write access to the
# completion caches
with self.cache_lock:
base_dir = cliutils.env('NOVACLIENT_UUID_CACHE_DIR',
default="~/.novaclient")
# NOTE(sirp): Keep separate UUID caches for each username +
# endpoint pair
username = cliutils.env('OS_USERNAME', 'NOVA_USERNAME')
url = cliutils.env('OS_URL', 'NOVA_URL')
uniqifier = hashlib.md5(username.encode('utf-8') +
url.encode('utf-8')).hexdigest()
cache_dir = os.path.expanduser(os.path.join(base_dir, uniqifier))
try:
os.makedirs(cache_dir, 0o755)
except OSError:
# NOTE(kiall): This is typically either permission denied while
# attempting to create the directory, or the
# directory already exists. Either way, don't
# fail.
pass
resource = obj_class.__name__.lower()
filename = "%s-%s-cache" % (resource, cache_type.replace('_', '-'))
path = os.path.join(cache_dir, filename)
cache_attr = "_%s_cache" % cache_type
try:
setattr(self, cache_attr, open(path, mode))
except IOError:
# NOTE(kiall): This is typically a permission denied while
# attempting to write the cache file.
pass
try:
yield
finally:
cache = getattr(self, cache_attr, None)
if cache:
cache.close()
delattr(self, cache_attr)
def write_to_completion_cache(self, cache_type, val):
cache = getattr(self, "_%s_cache" % cache_type, None)
if cache:
cache.write("%s\n" % val)
def _get(self, url, response_key): def _get(self, url, response_key):
_resp, body = self.api.client.get(url) _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): def _create(self, url, body, response_key, return_raw=False, **kwargs):
self.run_hooks('modify_body_for_create', body, **kwargs) self.run_hooks('modify_body_for_create', body, **kwargs)
@@ -162,9 +101,9 @@ class Manager(base.HookableMixin):
if return_raw: if return_raw:
return body[response_key] return body[response_key]
with self.completion_cache('human_id', self.resource_class, mode="a"): obj = self.resource_class(self, body[response_key])
with self.completion_cache('uuid', self.resource_class, mode="a"): self._write_object_to_completion_cache(obj)
return self.resource_class(self, body[response_key]) return obj
def _delete(self, url): def _delete(self, url):
_resp, _body = self.api.client.delete(url) _resp, _body = self.api.client.delete(url)
@@ -241,10 +180,6 @@ class ManagerWithFind(Manager):
list_kwargs['search_opts'] = {"name": kwargs["name"]} list_kwargs['search_opts'] = {"name": kwargs["name"]}
elif "display_name" in kwargs: elif "display_name" in kwargs:
list_kwargs['search_opts'] = {"name": kwargs["display_name"]} list_kwargs['search_opts'] = {"name": kwargs["display_name"]}
if "all_tenants" in kwargs:
all_tenants = kwargs['all_tenants']
list_kwargs['search_opts']['all_tenants'] = all_tenants
searches = [(k, v) for k, v in searches if k != 'all_tenants']
listing = self.list(**list_kwargs) listing = self.list(**list_kwargs)

View File

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

View File

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

View File

@@ -0,0 +1,17 @@
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import six
six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox'))

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,11 +11,10 @@
# under the License. # under the License.
import fixtures import fixtures
import httpretty
from keystoneclient.auth.identity import v2 from keystoneclient.auth.identity import v2
from keystoneclient import fixture
from keystoneclient import session from keystoneclient import session
from novaclient.openstack.common import jsonutils
from novaclient.v1_1 import client as v1_1client from novaclient.v1_1 import client as v1_1client
from novaclient.v3 import client as v3client from novaclient.v3 import client as v3client
@@ -25,74 +24,31 @@ COMPUTE_URL = 'http://compute.host'
class V1(fixtures.Fixture): class V1(fixtures.Fixture):
def __init__(self, compute_url=COMPUTE_URL, identity_url=IDENTITY_URL): def __init__(self, requests,
compute_url=COMPUTE_URL, identity_url=IDENTITY_URL):
super(V1, self).__init__() super(V1, self).__init__()
self.identity_url = identity_url self.identity_url = identity_url
self.compute_url = compute_url self.compute_url = compute_url
self.client = None self.client = None
self.requests = requests
self.token = { self.token = fixture.V2Token()
'access': { self.token.set_scope()
"token": {
"id": "ab48a9efdfedb23ty3494", s = self.token.add_service('compute')
"expires": "2010-11-01T03:32:15-05:00", s.add_endpoint(self.compute_url)
"tenant": {
"id": "345", s = self.token.add_service('computev3')
"name": "My Project" s.add_endpoint(self.compute_url)
}
},
"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): def setUp(self):
super(V1, self).setUp() super(V1, self).setUp()
httpretty.enable()
self.addCleanup(httpretty.disable)
auth_url = '%s/tokens' % self.identity_url auth_url = '%s/tokens' % self.identity_url
httpretty.register_uri(httpretty.POST, auth_url, headers = {'X-Content-Type': 'application/json'}
body=jsonutils.dumps(self.token), self.requests.register_uri('POST', auth_url,
content_type='application/json') json=self.token,
headers=headers)
self.client = self.new_client() self.client = self.new_client()
def new_client(self): def new_client(self):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,612 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from novaclient.openstack.common import jsonutils
from novaclient.tests import fakes
from novaclient.tests.fixture_data import base
class Base(base.Fixture):
base_url = 'servers'
def setUp(self):
super(Base, self).setUp()
get_servers = {
"servers": [
{'id': 1234, 'name': 'sample-server'},
{'id': 5678, 'name': 'sample-server2'}
]
}
self.requests.register_uri('GET', self.url(),
json=get_servers,
headers=self.json_headers)
self.server_1234 = {
"id": 1234,
"name": "sample-server",
"image": {
"id": 2,
"name": "sample image",
},
"flavor": {
"id": 1,
"name": "256 MB Server",
},
"hostId": "e4d909c290d0fb1ca068ffaddf22cbd0",
"status": "BUILD",
"progress": 60,
"addresses": {
"public": [{
"version": 4,
"addr": "1.2.3.4",
},
{
"version": 4,
"addr": "5.6.7.8",
}],
"private": [{
"version": 4,
"addr": "10.11.12.13",
}],
},
"metadata": {
"Server Label": "Web Head 1",
"Image Version": "2.1"
},
"OS-EXT-SRV-ATTR:host": "computenode1",
"security_groups": [{
'id': 1, 'name': 'securitygroup1',
'description': 'FAKE_SECURITY_GROUP',
'tenant_id': '4ffc664c198e435e9853f2538fbcd7a7'
}],
"OS-EXT-MOD:some_thing": "mod_some_thing_value",
}
self.server_5678 = {
"id": 5678,
"name": "sample-server2",
"image": {
"id": 2,
"name": "sample image",
},
"flavor": {
"id": 1,
"name": "256 MB Server",
},
"hostId": "9e107d9d372bb6826bd81d3542a419d6",
"status": "ACTIVE",
"addresses": {
"public": [{
"version": 4,
"addr": "4.5.6.7",
},
{
"version": 4,
"addr": "5.6.9.8",
}],
"private": [{
"version": 4,
"addr": "10.13.12.13",
}],
},
"metadata": {
"Server Label": "DB 1"
},
"OS-EXT-SRV-ATTR:host": "computenode2",
"security_groups": [{
'id': 1, 'name': 'securitygroup1',
'description': 'FAKE_SECURITY_GROUP',
'tenant_id': '4ffc664c198e435e9853f2538fbcd7a7'
},
{
'id': 2, 'name': 'securitygroup2',
'description': 'ANOTHER_FAKE_SECURITY_GROUP',
'tenant_id': '4ffc664c198e435e9853f2538fbcd7a7'
}],
}
self.server_9012 = {
"id": 9012,
"name": "sample-server3",
"image": "",
"flavor": {
"id": 1,
"name": "256 MB Server",
},
"hostId": "9e107d9d372bb6826bd81d3542a419d6",
"status": "ACTIVE",
"addresses": {
"public": [{
"version": 4,
"addr": "4.5.6.7",
},
{
"version": 4,
"addr": "5.6.9.8",
}],
"private": [{
"version": 4,
"addr": "10.13.12.13",
}],
},
"metadata": {
"Server Label": "DB 1"
}
}
servers = [self.server_1234, self.server_5678, self.server_9012]
get_servers_detail = {"servers": servers}
self.requests.register_uri('GET', self.url('detail'),
json=get_servers_detail,
headers=self.json_headers)
self.server_1235 = self.server_1234.copy()
self.server_1235['id'] = 1235
self.server_1235['status'] = 'error'
self.server_1235['fault'] = {'message': 'something went wrong!'}
for s in servers + [self.server_1235]:
self.requests.register_uri('GET', self.url(s['id']),
json={'server': s},
headers=self.json_headers)
for s in (1234, 5678):
self.requests.register_uri('DELETE', self.url(s), status_code=202)
for k in ('test_key', 'key1', 'key2'):
self.requests.register_uri('DELETE',
self.url(1234, 'metadata', k),
status_code=204)
metadata1 = {'metadata': {'test_key': 'test_value'}}
self.requests.register_uri('POST', self.url(1234, 'metadata'),
json=metadata1,
headers=self.json_headers)
self.requests.register_uri('PUT',
self.url(1234, 'metadata', 'test_key'),
json=metadata1,
headers=self.json_headers)
self.diagnostic = {'data': 'Fake diagnostics'}
metadata2 = {'metadata': {'key1': 'val1'}}
for u in ('uuid1', 'uuid2', 'uuid3', 'uuid4'):
self.requests.register_uri('POST', self.url(u, 'metadata'),
json=metadata2, status_code=204)
self.requests.register_uri('DELETE',
self.url(u, 'metadata', 'key1'),
json=self.diagnostic,
headers=self.json_headers)
get_security_groups = {
"security_groups": [{
'id': 1,
'name': 'securitygroup1',
'description': 'FAKE_SECURITY_GROUP',
'tenant_id': '4ffc664c198e435e9853f2538fbcd7a7',
'rules': []}]
}
self.requests.register_uri('GET',
self.url('1234', 'os-security-groups'),
json=get_security_groups)
self.requests.register_uri('POST', self.url(),
json=self.post_servers,
headers=self.json_headers)
self.requests.register_uri('POST', self.url('1234', 'action'),
json=self.post_servers_1234_action,
headers=self.json_headers)
get_os_interface = {
"interfaceAttachments": [
{
"port_state": "ACTIVE",
"net_id": "net-id-1",
"port_id": "port-id-1",
"mac_address": "aa:bb:cc:dd:ee:ff",
"fixed_ips": [{"ip_address": "1.2.3.4"}],
},
{
"port_state": "ACTIVE",
"net_id": "net-id-1",
"port_id": "port-id-1",
"mac_address": "aa:bb:cc:dd:ee:ff",
"fixed_ips": [{"ip_address": "1.2.3.4"}],
}
]
}
self.requests.register_uri('GET',
self.url('1234', 'os-interface'),
json=get_os_interface,
headers=self.json_headers)
interface_data = {'interfaceAttachment': {}}
self.requests.register_uri('POST',
self.url('1234', 'os-interface'),
json=interface_data,
headers=self.json_headers)
def put_servers_1234(request, context):
body = jsonutils.loads(request.body)
assert list(body) == ['server']
fakes.assert_has_keys(body['server'],
optional=['name', 'adminPass'])
return request.body
self.requests.register_uri('PUT', self.url(1234),
text=put_servers_1234,
status_code=204,
headers=self.json_headers)
def post_os_volumes_boot(request, context):
body = jsonutils.loads(request.body)
assert (set(body.keys()) <=
set(['server', 'os:scheduler_hints']))
fakes.assert_has_keys(body['server'],
required=['name', 'flavorRef'],
optional=['imageRef'])
data = body['server']
# Require one, and only one, of the keys for bdm
if 'block_device_mapping' not in data:
if 'block_device_mapping_v2' not in data:
msg = "missing required keys: 'block_device_mapping'"
raise AssertionError(msg)
elif 'block_device_mapping_v2' in data:
msg = "found extra keys: 'block_device_mapping'"
raise AssertionError(msg)
return {'server': self.server_9012}
# NOTE(jamielennox): hack to make os_volumes mock go to the right place
base_url = self.base_url
self.base_url = None
self.requests.register_uri('POST', self.url('os-volumes_boot'),
json=post_os_volumes_boot,
status_code=202,
headers=self.json_headers)
self.base_url = base_url
#
# Server password
#
self.requests.register_uri('DELETE',
self.url(1234, 'os-server-password'),
status_code=202)
class V1(Base):
def setUp(self):
super(V1, self).setUp()
#
# Server Addresses
#
add = self.server_1234['addresses']
self.requests.register_uri('GET', self.url(1234, 'ips'),
json={'addresses': add},
headers=self.json_headers)
self.requests.register_uri('GET', self.url(1234, 'ips', 'public'),
json={'public': add['public']},
headers=self.json_headers)
self.requests.register_uri('GET', self.url(1234, 'ips', 'private'),
json={'private': add['private']},
headers=self.json_headers)
self.requests.register_uri('DELETE',
self.url(1234, 'ips', 'public', '1.2.3.4'),
status_code=202)
self.requests.register_uri('GET',
self.url('1234', 'diagnostics'),
json=self.diagnostic)
self.requests.register_uri('DELETE',
self.url('1234', 'os-interface', 'port-id'))
# Testing with the following password and key
#
# Clear password: FooBar123
#
# RSA Private Key: novaclient/tests/idfake.pem
#
# Encrypted password
# OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r
# qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho
# QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw
# /y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N
# tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk
# Hi/fmZZNQQqj1Ijq0caOIw==
get_server_password = {'password':
'OIuEuQttO8Rk93BcKlwHQsziDAnkAm/V6V8VPToA8ZeUaUBWwS0gwo2K6Y61Z96r'
'qG447iRz0uTEEYq3RAYJk1mh3mMIRVl27t8MtIecR5ggVVbz1S9AwXJQypDKl0ho'
'QFvhCBcMWPohyGewDJOhDbtuN1IoFI9G55ZvFwCm5y7m7B2aVcoLeIsJZE4PLsIw'
'/y5a6Z3/AoJZYGG7IH5WN88UROU3B9JZGFB2qtPLQTOvDMZLUhoPRIJeHiVSlo1N'
'tI2/++UsXVg3ow6ItqCJGgdNuGG5JB+bslDHWPxROpesEIHdczk46HCpHQN8f1sk'
'Hi/fmZZNQQqj1Ijq0caOIw=='}
self.requests.register_uri('GET',
self.url(1234, 'os-server-password'),
json=get_server_password)
def post_servers(self, request, context):
body = jsonutils.loads(request.body)
context.status_code = 202
assert (set(body.keys()) <=
set(['server', 'os:scheduler_hints']))
fakes.assert_has_keys(body['server'],
required=['name', 'imageRef', 'flavorRef'],
optional=['metadata', 'personality'])
if 'personality' in body['server']:
for pfile in body['server']['personality']:
fakes.assert_has_keys(pfile, required=['path', 'contents'])
if body['server']['name'] == 'some-bad-server':
body = self.server_1235
else:
body = self.server_1234
return {'server': body}
def post_servers_1234_action(self, request, context):
_body = ''
body = jsonutils.loads(request.body)
context.status_code = 202
assert len(body.keys()) == 1
action = list(body)[0]
if action == 'reboot':
assert list(body[action]) == ['type']
assert body[action]['type'] in ['HARD', 'SOFT']
elif action == 'rebuild':
body = body[action]
adminPass = body.get('adminPass', 'randompassword')
assert 'imageRef' in body
_body = self.server_1234.copy()
_body['adminPass'] = adminPass
elif action == 'resize':
keys = body[action].keys()
assert 'flavorRef' in keys
elif action == 'confirmResize':
assert body[action] is None
# This one method returns a different response code
context.status_code = 204
return None
elif action == 'revertResize':
assert body[action] is None
elif action == 'migrate':
assert body[action] is None
elif action == 'os-stop':
assert body[action] is None
elif action == 'os-start':
assert body[action] is None
elif action == 'forceDelete':
assert body[action] is None
elif action == 'restore':
assert body[action] is None
elif action == 'pause':
assert body[action] is None
elif action == 'unpause':
assert body[action] is None
elif action == 'lock':
assert body[action] is None
elif action == 'unlock':
assert body[action] is None
elif action == 'rescue':
assert body[action] is None
_body = {'Password': 'RescuePassword'}
elif action == 'unrescue':
assert body[action] is None
elif action == 'resume':
assert body[action] is None
elif action == 'suspend':
assert body[action] is None
elif action == 'lock':
assert body[action] is None
elif action == 'unlock':
assert body[action] is None
elif action == 'shelve':
assert body[action] is None
elif action == 'shelveOffload':
assert body[action] is None
elif action == 'unshelve':
assert body[action] is None
elif action == 'addFixedIp':
assert list(body[action]) == ['networkId']
elif action == 'removeFixedIp':
assert list(body[action]) == ['address']
elif action == 'addFloatingIp':
assert (list(body[action]) == ['address'] or
sorted(list(body[action])) == ['address',
'fixed_address'])
elif action == 'removeFloatingIp':
assert list(body[action]) == ['address']
elif action == 'createImage':
assert set(body[action].keys()) == set(['name', 'metadata'])
context.headers['location'] = "http://blah/images/456"
elif action == 'changePassword':
assert list(body[action]) == ['adminPass']
elif action == 'os-getConsoleOutput':
assert list(body[action]) == ['length']
context.status_code = 202
return {'output': 'foo'}
elif action == 'os-getVNCConsole':
assert list(body[action]) == ['type']
elif action == 'os-getSPICEConsole':
assert list(body[action]) == ['type']
elif action == 'os-getRDPConsole':
assert list(body[action]) == ['type']
elif action == 'os-getSerialConsole':
assert list(body[action]) == ['type']
elif action == 'os-migrateLive':
assert set(body[action].keys()) == set(['host',
'block_migration',
'disk_over_commit'])
elif action == 'os-resetState':
assert list(body[action]) == ['state']
elif action == 'resetNetwork':
assert body[action] is None
elif action == 'addSecurityGroup':
assert list(body[action]) == ['name']
elif action == 'removeSecurityGroup':
assert list(body[action]) == ['name']
elif action == 'createBackup':
assert set(body[action]) == set(['name',
'backup_type',
'rotation'])
elif action == 'evacuate':
keys = list(body[action])
if 'adminPass' in keys:
keys.remove('adminPass')
assert set(keys) == set(['host', 'onSharedStorage'])
else:
raise AssertionError("Unexpected server action: %s" % action)
return {'server': _body}
class V3(Base):
def setUp(self):
super(V3, self).setUp()
get_interfaces = {
"interface_attachments": [
{
"port_state": "ACTIVE",
"net_id": "net-id-1",
"port_id": "port-id-1",
"mac_address": "aa:bb:cc:dd:ee:ff",
"fixed_ips": [{"ip_address": "1.2.3.4"}],
},
{
"port_state": "ACTIVE",
"net_id": "net-id-1",
"port_id": "port-id-1",
"mac_address": "aa:bb:cc:dd:ee:ff",
"fixed_ips": [{"ip_address": "1.2.3.4"}],
}
]
}
self.requests.register_uri('GET',
self.url('1234', 'os-attach-interfaces'),
json=get_interfaces,
headers=self.json_headers)
attach_body = {'interface_attachment': {}}
self.requests.register_uri('POST',
self.url('1234', 'os-attach-interfaces'),
json=attach_body,
headers=self.json_headers)
self.requests.register_uri('GET',
self.url('1234', 'os-server-diagnostics'),
json=self.diagnostic)
url = self.url('1234', 'os-attach-interfaces', 'port-id')
self.requests.register_uri('DELETE', url)
self.requests.register_uri('GET',
self.url(1234, 'os-server-password'),
json={'password': ''})
def post_servers(self, request, context):
body = jsonutils.loads(request.body)
assert set(body.keys()) <= set(['server'])
fakes.assert_has_keys(body['server'],
required=['name', 'image_ref', 'flavor_ref'],
optional=['metadata', 'personality',
'os-scheduler-hints:scheduler_hints'])
if body['server']['name'] == 'some-bad-server':
body = self.server_1235
else:
body = self.server_1234
context.status_code = 202
return {'server': body}
def post_servers_1234_action(self, request, context):
context.status_code = 202
body_is_none_list = [
'revert_resize', 'migrate', 'stop', 'start', 'force_delete',
'restore', 'pause', 'unpause', 'lock', 'unlock', 'unrescue',
'resume', 'suspend', 'lock', 'unlock', 'shelve', 'shelve_offload',
'unshelve', 'reset_network', 'rescue', 'confirm_resize']
body_return_map = {
'rescue': {'admin_password': 'RescuePassword'},
'get_console_output': {'output': 'foo'},
'rebuild': {'server': self.server_1234},
}
body_param_check_exists = {
'rebuild': 'image_ref',
'resize': 'flavor_ref'}
body_params_check_exact = {
'reboot': ['type'],
'add_fixed_ip': ['network_id'],
'evacuate': ['host', 'on_shared_storage'],
'remove_fixed_ip': ['address'],
'change_password': ['admin_password'],
'get_console_output': ['length'],
'get_vnc_console': ['type'],
'get_spice_console': ['type'],
'get_serial_console': ['type'],
'reset_state': ['state'],
'create_image': ['name', 'metadata'],
'migrate_live': ['host', 'block_migration', 'disk_over_commit'],
'create_backup': ['name', 'backup_type', 'rotation'],
'attach': ['volume_id', 'device'],
'detach': ['volume_id'],
'swap_volume_attachment': ['old_volume_id', 'new_volume_id']}
body = jsonutils.loads(request.body)
assert len(body.keys()) == 1
action = list(body)[0]
_body = body_return_map.get(action, '')
if action in body_is_none_list:
assert body[action] is None
if action in body_param_check_exists:
assert body_param_check_exists[action] in body[action]
if action == 'evacuate':
body[action].pop('admin_password', None)
if action in body_params_check_exact:
assert set(body[action]) == set(body_params_check_exact[action])
if action == 'reboot':
assert body[action]['type'] in ['HARD', 'SOFT']
elif action == 'confirm_resize':
# This one method returns a different response code
context.status_code = 204
elif action == 'create_image':
context.headers['location'] = "http://blah/images/456"
if action not in set.union(set(body_is_none_list),
set(body_params_check_exact.keys()),
set(body_param_check_exists.keys())):
raise AssertionError("Unexpected server action: %s" % action)
return _body

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,7 +14,6 @@ from oslo.serialization import jsonutils
from novaclient.tests.unit import fakes from novaclient.tests.unit import fakes
from novaclient.tests.unit.fixture_data import base from novaclient.tests.unit.fixture_data import base
from novaclient.tests.unit.v2 import fakes as v2_fakes
class Base(base.Fixture): class Base(base.Fixture):
@@ -383,24 +382,43 @@ class V1(Base):
context.status_code = 202 context.status_code = 202
assert len(body.keys()) == 1 assert len(body.keys()) == 1
action = list(body)[0] action = list(body)[0]
if action == 'reboot':
if v2_fakes.FakeHTTPClient.check_server_actions(body): assert list(body[action]) == ['type']
# NOTE(snikitin): No need to do any operations here. This 'pass' assert body[action]['type'] in ['HARD', 'SOFT']
# is needed to avoid AssertionError in the last 'else' statement
# if we found 'action' in method check_server_actions and
# raise AssertionError if we didn't find 'action' at all.
pass
elif action == 'rebuild': elif action == 'rebuild':
body = body[action] body = body[action]
adminPass = body.get('adminPass', 'randompassword') adminPass = body.get('adminPass', 'randompassword')
assert 'imageRef' in body assert 'imageRef' in body
_body = self.server_1234.copy() _body = self.server_1234.copy()
_body['adminPass'] = adminPass _body['adminPass'] = adminPass
elif action == 'resize':
keys = body[action].keys()
assert 'flavorRef' in keys
elif action == 'confirmResize': elif action == 'confirmResize':
assert body[action] is None assert body[action] is None
# This one method returns a different response code # This one method returns a different response code
context.status_code = 204 context.status_code = 204
return None return None
elif action == 'revertResize':
assert body[action] is None
elif action == 'migrate':
assert body[action] is None
elif action == 'os-stop':
assert body[action] is None
elif action == 'os-start':
assert body[action] is None
elif action == 'forceDelete':
assert body[action] is None
elif action == 'restore':
assert body[action] is None
elif action == 'pause':
assert body[action] is None
elif action == 'unpause':
assert body[action] is None
elif action == 'lock':
assert body[action] is None
elif action == 'unlock':
assert body[action] is None
elif action == 'rescue': elif action == 'rescue':
if body[action]: if body[action]:
keys = set(body[action].keys()) keys = set(body[action].keys())
@@ -408,15 +426,65 @@ class V1(Base):
else: else:
assert body[action] is None assert body[action] is None
_body = {'adminPass': 'RescuePassword'} _body = {'adminPass': 'RescuePassword'}
elif action == 'unrescue':
assert body[action] is None
elif action == 'resume':
assert body[action] is None
elif action == 'suspend':
assert body[action] is None
elif action == 'lock':
assert body[action] is None
elif action == 'unlock':
assert body[action] is None
elif action == 'shelve':
assert body[action] is None
elif action == 'shelveOffload':
assert body[action] is None
elif action == 'unshelve':
assert body[action] is None
elif action == 'addFixedIp':
assert list(body[action]) == ['networkId']
elif action == 'removeFixedIp':
assert list(body[action]) == ['address']
elif action == 'addFloatingIp':
assert (list(body[action]) == ['address'] or
sorted(list(body[action])) == ['address',
'fixed_address'])
elif action == 'removeFloatingIp':
assert list(body[action]) == ['address']
elif action == 'createImage': elif action == 'createImage':
assert set(body[action].keys()) == set(['name', 'metadata']) assert set(body[action].keys()) == set(['name', 'metadata'])
context.headers['location'] = "http://blah/images/456" context.headers['location'] = "http://blah/images/456"
elif action == 'changePassword':
assert list(body[action]) == ['adminPass']
elif action == 'os-getConsoleOutput': elif action == 'os-getConsoleOutput':
assert list(body[action]) == ['length'] assert list(body[action]) == ['length']
context.status_code = 202 context.status_code = 202
return {'output': 'foo'} return {'output': 'foo'}
elif action == 'os-getVNCConsole':
assert list(body[action]) == ['type']
elif action == 'os-getSPICEConsole':
assert list(body[action]) == ['type']
elif action == 'os-getRDPConsole':
assert list(body[action]) == ['type']
elif action == 'os-getSerialConsole': elif action == 'os-getSerialConsole':
assert list(body[action]) == ['type'] assert list(body[action]) == ['type']
elif action == 'os-migrateLive':
assert set(body[action].keys()) == set(['host',
'block_migration',
'disk_over_commit'])
elif action == 'os-resetState':
assert list(body[action]) == ['state']
elif action == 'resetNetwork':
assert body[action] is None
elif action == 'addSecurityGroup':
assert list(body[action]) == ['name']
elif action == 'removeSecurityGroup':
assert list(body[action]) == ['name']
elif action == 'createBackup':
assert set(body[action]) == set(['name',
'backup_type',
'rotation'])
elif action == 'evacuate': elif action == 'evacuate':
keys = list(body[action]) keys = list(body[action])
if 'adminPass' in keys: if 'adminPass' in keys:

View File

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

View File

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

View File

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

View File

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

View File

@@ -258,7 +258,7 @@ class FakeHTTPClient(base_client.HTTPClient):
}, },
"flavor": { "flavor": {
"id": 1, "id": 1,
"name": "256 mb server", "name": "256 MB Server",
}, },
"hostId": "e4d909c290d0fb1ca068ffaddf22cbd0", "hostId": "e4d909c290d0fb1ca068ffaddf22cbd0",
"status": "BUILD", "status": "BUILD",
@@ -299,7 +299,7 @@ class FakeHTTPClient(base_client.HTTPClient):
}, },
"flavor": { "flavor": {
"id": 1, "id": 1,
"name": "256 mb server", "name": "256 MB Server",
}, },
"hostId": "9e107d9d372bb6826bd81d3542a419d6", "hostId": "9e107d9d372bb6826bd81d3542a419d6",
"status": "ACTIVE", "status": "ACTIVE",
@@ -340,7 +340,7 @@ class FakeHTTPClient(base_client.HTTPClient):
"image": "", "image": "",
"flavor": { "flavor": {
"id": 1, "id": 1,
"name": "256 mb server", "name": "256 MB Server",
}, },
"hostId": "9e107d9d372bb6826bd81d3542a419d6", "hostId": "9e107d9d372bb6826bd81d3542a419d6",
"status": "ACTIVE", "status": "ACTIVE",
@@ -542,73 +542,48 @@ class FakeHTTPClient(base_client.HTTPClient):
# Server actions # Server actions
# #
none_actions = ['revertResize', 'migrate', 'os-stop', 'os-start',
'forceDelete', 'restore', 'pause', 'unpause', 'unlock',
'unrescue', 'resume', 'suspend', 'lock', 'shelve',
'shelveOffload', 'unshelve', 'resetNetwork']
type_actions = ['os-getVNCConsole', 'os-getSPICEConsole',
'os-getRDPConsole']
@classmethod
def check_server_actions(cls, body):
action = list(body)[0]
if action == 'reboot':
assert list(body[action]) == ['type']
assert body[action]['type'] in ['HARD', 'SOFT']
elif action == 'resize':
assert 'flavorRef' in body[action]
elif action in cls.none_actions:
assert body[action] is None
elif action == 'addFixedIp':
assert list(body[action]) == ['networkId']
elif action in ['removeFixedIp', 'removeFloatingIp']:
assert list(body[action]) == ['address']
elif action == 'addFloatingIp':
assert (list(body[action]) == ['address'] or
sorted(list(body[action])) == ['address', 'fixed_address'])
elif action == 'changePassword':
assert list(body[action]) == ['adminPass']
elif action in cls.type_actions:
assert list(body[action]) == ['type']
elif action == 'os-migrateLive':
assert set(body[action].keys()) == set(['host', 'block_migration',
'disk_over_commit'])
elif action == 'os-resetState':
assert list(body[action]) == ['state']
elif action == 'resetNetwork':
assert body[action] is None
elif action in ['addSecurityGroup', 'removeSecurityGroup']:
assert list(body[action]) == ['name']
elif action == 'createBackup':
assert set(body[action]) == set(['name', 'backup_type',
'rotation'])
else:
return False
return True
def post_servers_1234_action(self, body, **kw): def post_servers_1234_action(self, body, **kw):
_headers = None _headers = None
_body = None _body = None
resp = 202 resp = 202
assert len(body.keys()) == 1 assert len(body.keys()) == 1
action = list(body)[0] action = list(body)[0]
if action == 'reboot':
if self.check_server_actions(body): assert list(body[action]) == ['type']
# NOTE(snikitin): No need to do any operations here. This 'pass' assert body[action]['type'] in ['HARD', 'SOFT']
# is needed to avoid AssertionError in the last 'else' statement
# if we found 'action' in method check_server_actions and
# raise AssertionError if we didn't find 'action' at all.
pass
elif action == 'rebuild': elif action == 'rebuild':
body = body[action] body = body[action]
adminPass = body.get('adminPass', 'randompassword') adminPass = body.get('adminPass', 'randompassword')
assert 'imageRef' in body assert 'imageRef' in body
_body = self.get_servers_1234()[2] _body = self.get_servers_1234()[2]
_body['server']['adminPass'] = adminPass _body['server']['adminPass'] = adminPass
elif action == 'resize':
keys = body[action].keys()
assert 'flavorRef' in keys
elif action == 'confirmResize': elif action == 'confirmResize':
assert body[action] is None assert body[action] is None
# This one method returns a different response code # This one method returns a different response code
return (204, {}, None) return (204, {}, None)
elif action == 'revertResize':
assert body[action] is None
elif action == 'migrate':
assert body[action] is None
elif action == 'os-stop':
assert body[action] is None
elif action == 'os-start':
assert body[action] is None
elif action == 'forceDelete':
assert body[action] is None
elif action == 'restore':
assert body[action] is None
elif action == 'pause':
assert body[action] is None
elif action == 'unpause':
assert body[action] is None
elif action == 'lock':
assert body[action] is None
elif action == 'unlock':
assert body[action] is None
elif action == 'rescue': elif action == 'rescue':
if body[action]: if body[action]:
keys = set(body[action].keys()) keys = set(body[action].keys())
@@ -616,12 +591,62 @@ class FakeHTTPClient(base_client.HTTPClient):
else: else:
assert body[action] is None assert body[action] is None
_body = {'adminPass': 'RescuePassword'} _body = {'adminPass': 'RescuePassword'}
elif action == 'unrescue':
assert body[action] is None
elif action == 'resume':
assert body[action] is None
elif action == 'suspend':
assert body[action] is None
elif action == 'lock':
assert body[action] is None
elif action == 'unlock':
assert body[action] is None
elif action == 'shelve':
assert body[action] is None
elif action == 'shelveOffload':
assert body[action] is None
elif action == 'unshelve':
assert body[action] is None
elif action == 'addFixedIp':
assert list(body[action]) == ['networkId']
elif action == 'removeFixedIp':
assert list(body[action]) == ['address']
elif action == 'addFloatingIp':
assert (list(body[action]) == ['address'] or
sorted(list(body[action])) == ['address',
'fixed_address'])
elif action == 'removeFloatingIp':
assert list(body[action]) == ['address']
elif action == 'createImage': elif action == 'createImage':
assert set(body[action].keys()) == set(['name', 'metadata']) assert set(body[action].keys()) == set(['name', 'metadata'])
_headers = dict(location="http://blah/images/456") _headers = dict(location="http://blah/images/456")
elif action == 'changePassword':
assert list(body[action]) == ['adminPass']
elif action == 'os-getConsoleOutput': elif action == 'os-getConsoleOutput':
assert list(body[action]) == ['length'] assert list(body[action]) == ['length']
return (202, {}, {'output': 'foo'}) return (202, {}, {'output': 'foo'})
elif action == 'os-getVNCConsole':
assert list(body[action]) == ['type']
elif action == 'os-getSPICEConsole':
assert list(body[action]) == ['type']
elif action == 'os-getRDPConsole':
assert list(body[action]) == ['type']
elif action == 'os-migrateLive':
assert set(body[action].keys()) == set(['host',
'block_migration',
'disk_over_commit'])
elif action == 'os-resetState':
assert list(body[action]) == ['state']
elif action == 'resetNetwork':
assert body[action] is None
elif action == 'addSecurityGroup':
assert list(body[action]) == ['name']
elif action == 'removeSecurityGroup':
assert list(body[action]) == ['name']
elif action == 'createBackup':
assert set(body[action]) == set(['name',
'backup_type',
'rotation'])
elif action == 'evacuate': elif action == 'evacuate':
keys = list(body[action]) keys = list(body[action])
if 'adminPass' in keys: if 'adminPass' in keys:
@@ -672,19 +697,19 @@ class FakeHTTPClient(base_client.HTTPClient):
def get_flavors_detail(self, **kw): def get_flavors_detail(self, **kw):
flavors = {'flavors': [ flavors = {'flavors': [
{'id': 1, 'name': '256 mb server', 'ram': 256, 'disk': 10, {'id': 1, 'name': '256 MB Server', 'ram': 256, 'disk': 10,
'OS-FLV-EXT-DATA:ephemeral': 10, 'OS-FLV-EXT-DATA:ephemeral': 10,
'os-flavor-access:is_public': True, 'os-flavor-access:is_public': True,
'links': {}}, 'links': {}},
{'id': 2, 'name': '512 mb server', 'ram': 512, 'disk': 20, {'id': 2, 'name': '512 MB Server', 'ram': 512, 'disk': 20,
'OS-FLV-EXT-DATA:ephemeral': 20, 'OS-FLV-EXT-DATA:ephemeral': 20,
'os-flavor-access:is_public': False, 'os-flavor-access:is_public': False,
'links': {}}, 'links': {}},
{'id': 4, 'name': '1024 mb server', 'ram': 1024, 'disk': 10, {'id': 4, 'name': '1024 MB Server', 'ram': 1024, 'disk': 10,
'OS-FLV-EXT-DATA:ephemeral': 10, 'OS-FLV-EXT-DATA:ephemeral': 10,
'os-flavor-access:is_public': True, 'os-flavor-access:is_public': True,
'links': {}}, 'links': {}},
{'id': 'aa1', 'name': '128 mb server', 'ram': 128, 'disk': 0, {'id': 'aa1', 'name': '128 MB Server', 'ram': 128, 'disk': 0,
'OS-FLV-EXT-DATA:ephemeral': 0, 'OS-FLV-EXT-DATA:ephemeral': 0,
'os-flavor-access:is_public': True, 'os-flavor-access:is_public': True,
'links': {}} 'links': {}}
@@ -736,16 +761,16 @@ class FakeHTTPClient(base_client.HTTPClient):
{}, {},
{'flavor': { {'flavor': {
'id': 3, 'id': 3,
'name': '256 mb server', 'name': '256 MB Server',
'ram': 256, 'ram': 256,
'disk': 10, 'disk': 10,
}}, }},
) )
def get_flavors_512_mb_server(self, **kw): def get_flavors_512_MB_Server(self, **kw):
raise exceptions.NotFound('404') raise exceptions.NotFound('404')
def get_flavors_128_mb_server(self, **kw): def get_flavors_128_MB_Server(self, **kw):
raise exceptions.NotFound('404') raise exceptions.NotFound('404')
def get_flavors_aa1(self, **kw): def get_flavors_aa1(self, **kw):
@@ -2196,7 +2221,6 @@ class FakeSessionMockClient(base_client.SessionClient, FakeHTTPClient):
self.callstack = [] self.callstack = []
self.auth = mock.Mock() self.auth = mock.Mock()
self.session = mock.Mock() self.session = mock.Mock()
self.service_type = 'service_type'
self.auth.get_auth_ref.return_value.project_id = 'tenant_id' self.auth.get_auth_ref.return_value.project_id = 'tenant_id'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -12,11 +12,11 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from novaclient import extension
from novaclient.v1_1.contrib import instance_action
from novaclient import extension
from novaclient.tests import utils from novaclient.tests import utils
from novaclient.tests.v1_1.contrib import fakes from novaclient.tests.v1_1.contrib import fakes
from novaclient.v1_1.contrib import instance_action
extensions = [ extensions = [

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -58,7 +58,7 @@ class AvailabilityZoneTest(utils.FixturedTestCase):
z0 = self.shell._treeizeAvailabilityZone(zones[0]) z0 = self.shell._treeizeAvailabilityZone(zones[0])
z1 = self.shell._treeizeAvailabilityZone(zones[1]) z1 = self.shell._treeizeAvailabilityZone(zones[1])
self.assertEqual((len(z0), len(z1)), (1, 1)) self.assertEqual((1, 1), (len(z0), len(z1)))
self._assertZone(z0[0], l0[0], l0[1]) self._assertZone(z0[0], l0[0], l0[1])
self._assertZone(z1[0], l1[0], l1[1]) self._assertZone(z1[0], l1[0], l1[1])
@@ -89,7 +89,7 @@ class AvailabilityZoneTest(utils.FixturedTestCase):
z1 = self.shell._treeizeAvailabilityZone(zones[1]) z1 = self.shell._treeizeAvailabilityZone(zones[1])
z2 = self.shell._treeizeAvailabilityZone(zones[2]) z2 = self.shell._treeizeAvailabilityZone(zones[2])
self.assertEqual((len(z0), len(z1), len(z2)), (3, 5, 1)) self.assertEqual((3, 5, 1), (len(z0), len(z1), len(z2)))
self._assertZone(z0[0], l0[0], l0[1]) self._assertZone(z0[0], l0[0], l0[1])
self._assertZone(z0[1], l1[0], l1[1]) self._assertZone(z0[1], l1[0], l1[1])

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -21,14 +21,15 @@ Server interface.
import base64 import base64
from oslo.utils import encodeutils
import six import six
from six.moves.urllib import parse from six.moves.urllib import parse
from novaclient import base from novaclient import base
from novaclient import crypto from novaclient import crypto
from novaclient.openstack.common.gettextutils import _ from novaclient.openstack.common.gettextutils import _
from novaclient.openstack.common import strutils from novaclient.v1_1 import security_groups
from novaclient.v1_1.security_groups import SecurityGroup
REBOOT_SOFT, REBOOT_HARD = 'SOFT', 'HARD' REBOOT_SOFT, REBOOT_HARD = 'SOFT', 'HARD'
@@ -85,6 +86,14 @@ class Server(base.Resource):
""" """
return self.manager.get_rdp_console(self, console_type) return self.manager.get_rdp_console(self, console_type)
def get_serial_console(self, console_type):
"""
Get serial console for a Server.
:param console_type: Type of console ('serial')
"""
return self.manager.get_serial_console(self, console_type)
def get_password(self, private_key=None): def get_password(self, private_key=None):
""" """
Get password for a Server. Get password for a Server.
@@ -364,7 +373,7 @@ class Server(base.Resource):
""" """
return self.manager.list_security_group(self) return self.manager.list_security_group(self)
def evacuate(self, host, on_shared_storage, password=None): def evacuate(self, host=None, on_shared_storage=True, password=None):
""" """
Evacuate an instance from failed host to specified host. Evacuate an instance from failed host to specified host.
@@ -419,7 +428,7 @@ class ServerManager(base.BootingManagerWithFind):
file-like object). A maximum of five entries is allowed, file-like object). A maximum of five entries is allowed,
and each file must be 10k or less. and each file must be 10k or less.
:param reservation_id: a UUID for the set of servers being requested. :param reservation_id: a UUID for the set of servers being requested.
:param return_raw: If True, don't try to coearse the result into :param return_raw: If True, don't try to coerce the result into
a Resource object. a Resource object.
:param security_groups: list of security group names :param security_groups: list of security group names
:param key_name: (optional extension) name of keypair to inject into :param key_name: (optional extension) name of keypair to inject into
@@ -453,7 +462,7 @@ class ServerManager(base.BootingManagerWithFind):
if six.PY3: if six.PY3:
userdata = userdata.encode("utf-8") userdata = userdata.encode("utf-8")
else: else:
userdata = strutils.safe_encode(userdata) userdata = encodeutils.safe_encode(userdata)
userdata_b64 = base64.b64encode(userdata).decode('utf-8') userdata_b64 = base64.b64encode(userdata).decode('utf-8')
body["server"]["user_data"] = userdata_b64 body["server"]["user_data"] = userdata_b64
@@ -674,6 +683,17 @@ class ServerManager(base.BootingManagerWithFind):
return self._action('os-getRDPConsole', server, return self._action('os-getRDPConsole', server,
{'type': console_type})[1] {'type': console_type})[1]
def get_serial_console(self, server, console_type):
"""
Get a serial console for an instance
:param server: The :class:`Server` (or its ID) to add an IP to.
:param console_type: Type of serial console to get ('serial')
"""
return self._action('os-getSerialConsole', server,
{'type': console_type})[1]
def get_password(self, server, private_key=None): def get_password(self, server, private_key=None):
""" """
Get password for an instance Get password for an instance
@@ -922,7 +942,8 @@ class ServerManager(base.BootingManagerWithFind):
self._action('reboot', server, {'type': reboot_type}) self._action('reboot', server, {'type': reboot_type})
def rebuild(self, server, image, password=None, disk_config=None, def rebuild(self, server, image, password=None, disk_config=None,
preserve_ephemeral=False, **kwargs): preserve_ephemeral=False, name=None, meta=None, files=None,
**kwargs):
""" """
Rebuild -- shut down and then re-image -- a server. Rebuild -- shut down and then re-image -- a server.
@@ -933,6 +954,15 @@ class ServerManager(base.BootingManagerWithFind):
Valid values are 'AUTO' or 'MANUAL' Valid values are 'AUTO' or 'MANUAL'
:param preserve_ephemeral: If True, request that any ephemeral device :param preserve_ephemeral: If True, request that any ephemeral device
be preserved when rebuilding the instance. Defaults to False. be preserved when rebuilding the instance. Defaults to False.
:param name: Something to name the server.
:param meta: A dict of arbitrary key/value metadata to store for this
server. A maximum of five entries is allowed, and both
keys and values must be 255 characters or less.
:param files: A dict of files to overwrite on the server upon boot.
Keys are file names (i.e. ``/etc/passwd``) and values
are the file contents (either as a string or as a
file-like object). A maximum of five entries is allowed,
and each file must be 10k or less.
""" """
body = {'imageRef': base.getid(image)} body = {'imageRef': base.getid(image)}
if password is not None: if password is not None:
@@ -941,6 +971,24 @@ class ServerManager(base.BootingManagerWithFind):
body['OS-DCF:diskConfig'] = disk_config body['OS-DCF:diskConfig'] = disk_config
if preserve_ephemeral is not False: if preserve_ephemeral is not False:
body['preserve_ephemeral'] = True body['preserve_ephemeral'] = True
if name is not None:
body['name'] = name
if meta:
body['metadata'] = meta
if files:
personality = body['personality'] = []
for filepath, file_or_string in sorted(files.items(),
key=lambda x: x[0]):
if hasattr(file_or_string, 'read'):
data = file_or_string.read()
else:
data = file_or_string
cont = base64.b64encode(data.encode('utf-8')).decode('utf-8')
personality.append({
'path': filepath,
'contents': cont,
})
_resp, body = self._action('rebuild', server, body, **kwargs) _resp, body = self._action('rebuild', server, body, **kwargs)
return Server(self, body['server']) return Server(self, body['server'])
@@ -1119,9 +1167,11 @@ class ServerManager(base.BootingManagerWithFind):
""" """
return self._list('/servers/%s/os-security-groups' % return self._list('/servers/%s/os-security-groups' %
base.getid(server), 'security_groups', SecurityGroup) base.getid(server), 'security_groups',
security_groups.SecurityGroup)
def evacuate(self, server, host, on_shared_storage, password=None): def evacuate(self, server, host=None, on_shared_storage=True,
password=None):
""" """
Evacuate a server instance. Evacuate a server instance.
@@ -1131,10 +1181,10 @@ class ServerManager(base.BootingManagerWithFind):
on shared storage on shared storage
:param password: string to set as password on the evacuated server. :param password: string to set as password on the evacuated server.
""" """
body = {
'host': host, body = {'onSharedStorage': on_shared_storage}
'onSharedStorage': on_shared_storage, if host is not None:
} body['host'] = host
if password is not None: if password is not None:
body['adminPass'] = password body['adminPass'] = password

View File

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

View File

@@ -0,0 +1,35 @@
# Copyright 2014 NEC Corporation. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
version interface
"""
from novaclient import base
class Version(base.Resource):
"""
Compute REST API information
"""
def __repr__(self):
return "<Version>"
class VersionManager(base.ManagerWithFind):
resource_class = Version
def list(self):
"""List all versions."""
return self._list(None, "versions")

View File

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

View File

@@ -423,6 +423,40 @@ class ServerManager(base.BootingManagerWithFind):
config_drive=None, admin_pass=None, disk_config=None, **kwargs): config_drive=None, admin_pass=None, disk_config=None, **kwargs):
""" """
Create (boot) a new server. Create (boot) a new server.
:param name: Something to name the server.
:param image: The :class:`Image` to boot with.
:param flavor: The :class:`Flavor` to boot onto.
:param meta: A dict of arbitrary key/value metadata to store for this
server. A maximum of five entries is allowed, and both
keys and values must be 255 characters or less.
:param files: A dict of files to overwrite on the server upon boot.
Keys are file names (i.e. ``/etc/passwd``) and values
are the file contents (either as a string or as a
file-like object). A maximum of five entries is allowed,
and each file must be 10k or less.
:param reservation_id: a UUID for the set of servers being requested.
:param return_raw: If True, don't try to coerce the result into
a Resource object.
:param security_groups: list of security group names
:param key_name: (optional extension) name of keypair to inject into
the instance
:param availability_zone: Name of the availability zone for instance
placement.
:param block_device_mapping: A dict of block device mappings for this
server.
:param block_device_mapping_v2: A dict of block device mappings V2 for
this server.
:param nics: (optional extension) an ordered list of nics to be
added to this server, with information about
connected networks, fixed IPs, etc.
:param scheduler_hints: (optional extension) arbitrary key-value pairs
specified by the client to help boot an instance.
:param config_drive: (optional extension) If True, enable config drive
on the server.
:param admin_pass: admin password for the server.
:param disk_config: (optional extension) control how the disk is
partitioned when the server is created.
""" """
body = {"server": { body = {"server": {
"name": name, "name": name,
@@ -839,15 +873,10 @@ class ServerManager(base.BootingManagerWithFind):
are the file contents (either as a string or as a are the file contents (either as a string or as a
file-like object). A maximum of five entries is allowed, file-like object). A maximum of five entries is allowed,
and each file must be 10k or less. and each file must be 10k or less.
:param reservation_id: a UUID for the set of servers being requested.
:param min_count: (optional extension) The minimum number of
servers to launch.
:param max_count: (optional extension) The maximum number of
servers to launch.
:param security_groups: A list of security group names
:param userdata: user data to pass to be exposed by the metadata :param userdata: user data to pass to be exposed by the metadata
server this can be a file type object as well or a server this can be a file type object as well or a
string. string.
:param reservation_id: a UUID for the set of servers being requested.
:param key_name: (optional extension) name of previously created :param key_name: (optional extension) name of previously created
keypair to inject into the instance. keypair to inject into the instance.
:param availability_zone: Name of the availability zone for instance :param availability_zone: Name of the availability zone for instance

View File

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

View File

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

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