summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/api/api.py19
-rw-r--r--openstackclient/api/object_store_v1.py74
-rw-r--r--openstackclient/common/clientmanager.py4
-rw-r--r--openstackclient/common/logs.py (renamed from openstackclient/common/context.py)0
-rw-r--r--openstackclient/common/parseractions.py15
-rw-r--r--openstackclient/common/quota.py28
-rw-r--r--openstackclient/common/session.py4
-rw-r--r--openstackclient/common/utils.py18
-rw-r--r--openstackclient/compute/v2/console.py2
-rw-r--r--openstackclient/compute/v2/fixedip.py12
-rw-r--r--openstackclient/compute/v2/flavor.py13
-rw-r--r--openstackclient/compute/v2/floatingip.py18
-rw-r--r--openstackclient/compute/v2/floatingippool.py2
-rw-r--r--openstackclient/compute/v2/security_group.py262
-rw-r--r--openstackclient/compute/v2/server.py303
-rw-r--r--openstackclient/compute/v2/service.py22
-rw-r--r--openstackclient/compute/v2/usage.py33
-rw-r--r--openstackclient/identity/client.py2
-rw-r--r--openstackclient/identity/v2_0/project.py6
-rw-r--r--openstackclient/identity/v2_0/role.py4
-rw-r--r--openstackclient/identity/v2_0/user.py6
-rw-r--r--openstackclient/identity/v3/credential.py31
-rw-r--r--openstackclient/identity/v3/domain.py4
-rw-r--r--openstackclient/identity/v3/group.py4
-rw-r--r--openstackclient/identity/v3/identity_provider.py23
-rw-r--r--openstackclient/identity/v3/project.py4
-rw-r--r--openstackclient/identity/v3/region.py22
-rw-r--r--openstackclient/identity/v3/role.py4
-rw-r--r--openstackclient/identity/v3/user.py4
-rw-r--r--openstackclient/image/v1/image.py9
-rw-r--r--openstackclient/image/v2/image.py14
-rw-r--r--openstackclient/network/client.py4
-rw-r--r--openstackclient/network/common.py6
-rw-r--r--openstackclient/object/v1/account.py2
-rw-r--r--openstackclient/object/v1/container.py2
-rw-r--r--openstackclient/object/v1/object.py2
-rw-r--r--openstackclient/shell.py20
-rw-r--r--openstackclient/tests/api/fakes.py2
-rw-r--r--openstackclient/tests/api/test_image_v1.py2
-rw-r--r--openstackclient/tests/api/test_image_v2.py2
-rw-r--r--openstackclient/tests/api/test_network_v2.py2
-rw-r--r--openstackclient/tests/api/test_object_store_v1.py11
-rw-r--r--openstackclient/tests/common/test_logs.py (renamed from openstackclient/tests/common/test_context.py)64
-rw-r--r--openstackclient/tests/common/test_parseractions.py55
-rw-r--r--openstackclient/tests/common/test_quota.py173
-rw-r--r--openstackclient/tests/common/test_utils.py40
-rw-r--r--openstackclient/tests/compute/v2/fakes.py215
-rw-r--r--openstackclient/tests/compute/v2/test_flavor.py259
-rw-r--r--openstackclient/tests/compute/v2/test_server.py619
-rw-r--r--openstackclient/tests/compute/v2/test_service.py151
-rw-r--r--openstackclient/tests/fakes.py37
-rw-r--r--openstackclient/tests/identity/v2_0/test_project.py4
-rw-r--r--openstackclient/tests/identity/v2_0/test_role.py4
-rw-r--r--openstackclient/tests/identity/v2_0/test_user.py5
-rw-r--r--openstackclient/tests/identity/v3/fakes.py7
-rw-r--r--openstackclient/tests/identity/v3/test_credential.py112
-rw-r--r--openstackclient/tests/identity/v3/test_identity_provider.py62
-rw-r--r--openstackclient/tests/identity/v3/test_region.py79
-rw-r--r--openstackclient/tests/identity/v3/test_role.py33
-rw-r--r--openstackclient/tests/image/v1/test_image.py25
-rw-r--r--openstackclient/tests/image/v2/fakes.py133
-rw-r--r--openstackclient/tests/image/v2/test_image.py30
-rw-r--r--openstackclient/tests/object/v1/fakes.py2
-rw-r--r--openstackclient/tests/object/v1/test_container_all.py3
-rw-r--r--openstackclient/tests/object/v1/test_object_all.py2
-rw-r--r--openstackclient/tests/volume/v1/test_volume.py182
-rw-r--r--openstackclient/tests/volume/v2/fakes.py88
-rw-r--r--openstackclient/tests/volume/v2/test_snapshot.py33
-rw-r--r--openstackclient/tests/volume/v2/test_type.py6
-rw-r--r--openstackclient/tests/volume/v2/test_volume.py204
-rw-r--r--openstackclient/volume/v1/snapshot.py13
-rw-r--r--openstackclient/volume/v1/volume.py12
-rw-r--r--openstackclient/volume/v2/snapshot.py13
-rw-r--r--openstackclient/volume/v2/volume.py62
-rw-r--r--openstackclient/volume/v2/volume_type.py4
75 files changed, 2929 insertions, 823 deletions
diff --git a/openstackclient/api/api.py b/openstackclient/api/api.py
index ba83ce4d..97eb7e4a 100644
--- a/openstackclient/api/api.py
+++ b/openstackclient/api/api.py
@@ -15,8 +15,9 @@
import simplejson as json
-from keystoneclient import exceptions as ksc_exceptions
-from keystoneclient import session as ksc_session
+from keystoneauth1 import exceptions as ks_exceptions
+from keystoneauth1 import session as ks_session
+
from openstackclient.common import exceptions
@@ -24,7 +25,7 @@ class KeystoneSession(object):
"""Wrapper for the Keystone Session
Restore some requests.session.Session compatibility;
- keystoneclient.session.Session.request() has the method and url
+ keystoneauth1.session.Session.request() has the method and url
arguments swapped from the rest of the requests-using world.
"""
@@ -70,7 +71,7 @@ class KeystoneSession(object):
if not session:
session = self.session
if not session:
- session = ksc_session.Session()
+ session = ks_session.Session()
if self.endpoint:
if url:
@@ -255,7 +256,7 @@ class BaseAPI(KeystoneSession):
return data[0]
if len(data) > 1:
msg = "Multiple %s exist with %s='%s'"
- raise ksc_exceptions.CommandError(
+ raise exceptions.CommandError(
msg % (resource, attr, value),
)
@@ -314,7 +315,7 @@ class BaseAPI(KeystoneSession):
num_bulk = len(bulk_list)
if num_bulk == 0:
msg = "none found"
- raise ksc_exceptions.NotFound(msg)
+ raise exceptions.NotFound(msg)
elif num_bulk > 1:
msg = "many found"
raise RuntimeError(msg)
@@ -338,12 +339,12 @@ class BaseAPI(KeystoneSession):
try:
ret = self._request('GET', "/%s/%s" % (path, value)).json()
- except ksc_exceptions.NotFound:
+ except ks_exceptions.NotFound:
kwargs = {attr: value}
try:
ret = self.find_one("/%s/detail" % (path), **kwargs)
- except ksc_exceptions.NotFound:
+ except ks_exceptions.NotFound:
msg = "%s not found" % value
- raise ksc_exceptions.NotFound(msg)
+ raise exceptions.NotFound(msg)
return ret
diff --git a/openstackclient/api/object_store_v1.py b/openstackclient/api/object_store_v1.py
index ae03ab7d..ab75a78c 100644
--- a/openstackclient/api/object_store_v1.py
+++ b/openstackclient/api/object_store_v1.py
@@ -14,7 +14,9 @@
"""Object Store v1 API Library"""
import io
+import logging
import os
+
import six
from six.moves import urllib
@@ -24,6 +26,7 @@ except ImportError:
from urlparse import urlparse # noqa
from openstackclient.api import api
+from openstackclient.common import utils
class APIv1(api.BaseAPI):
@@ -176,13 +179,24 @@ class APIv1(api.BaseAPI):
'x-container-object-count',
None,
),
- 'meta-owner': response.headers.get('x-container-meta-owner', None),
- 'bytes_used': response.headers.get('x-container-bytes-used', None),
- 'read_acl': response.headers.get('x-container-read', None),
- 'write_acl': response.headers.get('x-container-write', None),
- 'sync_to': response.headers.get('x-container-sync-to', None),
- 'sync_key': response.headers.get('x-container-sync-key', None),
+ 'bytes_used': response.headers.get('x-container-bytes-used', None)
}
+
+ if 'x-container-read' in response.headers:
+ data['read_acl'] = response.headers.get('x-container-read', None)
+ if 'x-container-write' in response.headers:
+ data['write_acl'] = response.headers.get('x-container-write', None)
+ if 'x-container-sync-to' in response.headers:
+ data['sync_to'] = response.headers.get('x-container-sync-to', None)
+ if 'x-container-sync-key' in response.headers:
+ data['sync_key'] = response.headers.get('x-container-sync-key',
+ None)
+
+ properties = self._get_properties(response.headers,
+ 'x-container-meta-')
+ if properties:
+ data['properties'] = properties
+
return data
def container_unset(
@@ -434,12 +448,12 @@ class APIv1(api.BaseAPI):
response = self._request('HEAD', "%s/%s" %
(urllib.parse.quote(container),
urllib.parse.quote(object)))
+
data = {
'account': self._find_account_id(),
'container': container,
'object': object,
'content-type': response.headers.get('content-type', None),
- 'meta-owner': response.headers.get('x-container-meta-owner', None),
}
if 'content-length' in response.headers:
data['content-length'] = response.headers.get(
@@ -455,19 +469,10 @@ class APIv1(api.BaseAPI):
'x-object-manifest',
None,
)
- for key, value in six.iteritems(response.headers):
- if key.startswith('x-object-meta-'):
- data[key[len('x-object-meta-'):].lower()] = value
- elif key not in (
- 'content-type',
- 'content-length',
- 'last-modified',
- 'etag',
- 'date',
- 'x-object-manifest',
- 'x-container-meta-owner',
- ):
- data[key.lower()] = value
+
+ properties = self._get_properties(response.headers, 'x-object-meta-')
+ if properties:
+ data['properties'] = properties
return data
@@ -495,12 +500,16 @@ class APIv1(api.BaseAPI):
# catalog should be enough.
response = self._request("HEAD", "")
data = {}
- for k, v in response.headers.iteritems():
- data[k] = v
+
+ properties = self._get_properties(response.headers, 'x-account-meta-')
+ if properties:
+ data['properties'] = properties
+
# Map containers, bytes and objects a bit nicer
- data['Containers'] = data.pop('x-account-container-count', None)
- data['Objects'] = data.pop('x-account-object-count', None)
- data['Bytes'] = data.pop('x-account-bytes-used', None)
+ data['Containers'] = response.headers.get('x-account-container-count',
+ None)
+ data['Objects'] = response.headers.get('x-account-object-count', None)
+ data['Bytes'] = response.headers.get('x-account-bytes-used', None)
# Add in Account info too
data['Account'] = self._find_account_id()
return data
@@ -544,8 +553,23 @@ class APIv1(api.BaseAPI):
# property we use: "X-Add-Container-Meta-Book: MobyDick", and the same
# logic applies for Object properties
+ log = logging.getLogger(__name__ + '._set_properties')
+
headers = {}
for k, v in properties.iteritems():
+ if not utils.is_ascii(k) or not utils.is_ascii(v):
+ log.error('Cannot set property %s to non-ascii value', k)
+ continue
+
header_name = header_tag % k
headers[header_name] = v
return headers
+
+ def _get_properties(self, headers, header_tag):
+ # Add in properties as a top level key, this is consistent with other
+ # OSC commands
+ properties = {}
+ for k, v in six.iteritems(headers):
+ if k.startswith(header_tag):
+ properties[k[len(header_tag):]] = v
+ return properties
diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py
index 55c6fe53..edabf65e 100644
--- a/openstackclient/common/clientmanager.py
+++ b/openstackclient/common/clientmanager.py
@@ -20,6 +20,7 @@ import logging
import pkg_resources
import sys
+from oslo_utils import strutils
import requests
from openstackclient.api import auth
@@ -167,7 +168,8 @@ class ClientManager(object):
self._project_name = self._auth_params['tenant_name']
LOG.info('Using auth plugin: %s' % self.auth_plugin_name)
- LOG.debug('Using parameters %s' % self._auth_params)
+ LOG.debug('Using parameters %s' %
+ strutils.mask_password(self._auth_params))
self.auth = auth_plugin.load_from_options(**self._auth_params)
# needed by SAML authentication
request_session = requests.session()
diff --git a/openstackclient/common/context.py b/openstackclient/common/logs.py
index 6d1aec13..6d1aec13 100644
--- a/openstackclient/common/context.py
+++ b/openstackclient/common/logs.py
diff --git a/openstackclient/common/parseractions.py b/openstackclient/common/parseractions.py
index 8f6008e2..fd90369a 100644
--- a/openstackclient/common/parseractions.py
+++ b/openstackclient/common/parseractions.py
@@ -65,3 +65,18 @@ class RangeAction(argparse.Action):
# Too many values
msg = "Invalid range, too many values"
raise argparse.ArgumentError(self, msg)
+
+
+class NonNegativeAction(argparse.Action):
+ """A custom action to check whether the value is non-negative or not
+
+ Ensures the value is >= 0.
+ """
+
+ def __call__(self, parser, namespace, values, option_string=None):
+ try:
+ assert(int(values) >= 0)
+ setattr(namespace, self.dest, values)
+ except Exception:
+ msg = "%s expected a non-negative integer" % (str(option_string))
+ raise argparse.ArgumentTypeError(self, msg)
diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py
index e092feff..8a9b910f 100644
--- a/openstackclient/common/quota.py
+++ b/openstackclient/common/quota.py
@@ -81,6 +81,7 @@ class SetQuota(command.Command):
parser.add_argument(
'--%s' % v,
metavar='<%s>' % v,
+ dest=k,
type=int,
help='New value for the %s quota' % v,
)
@@ -94,6 +95,7 @@ class SetQuota(command.Command):
@utils.log_method(log)
def take_action(self, parsed_args):
+ identity_client = self.app.client_manager.identity
compute_client = self.app.client_manager.compute
volume_client = self.app.client_manager.volume
@@ -115,23 +117,29 @@ class SetQuota(command.Command):
sys.stderr.write("No quotas updated")
return
+ if parsed_args.project:
+ project = utils.find_resource(
+ identity_client.projects,
+ parsed_args.project,
+ )
+
if parsed_args.quota_class:
if compute_kwargs:
compute_client.quota_classes.update(
- parsed_args.project,
+ project.id,
**compute_kwargs)
if volume_kwargs:
volume_client.quota_classes.update(
- parsed_args.project,
+ project.id,
**volume_kwargs)
else:
if compute_kwargs:
compute_client.quotas.update(
- parsed_args.project,
+ project.id,
**compute_kwargs)
if volume_kwargs:
volume_client.quotas.update(
- parsed_args.project,
+ project.id,
**volume_kwargs)
@@ -165,13 +173,19 @@ class ShowQuota(show.ShowOne):
return parser
def get_compute_volume_quota(self, client, parsed_args):
+ identity_client = self.app.client_manager.identity
+ project = utils.find_resource(
+ identity_client.projects,
+ parsed_args.project,
+ ).id
+
try:
if parsed_args.quota_class:
- quota = client.quota_classes.get(parsed_args.project)
+ quota = client.quota_classes.get(project)
elif parsed_args.default:
- quota = client.quotas.defaults(parsed_args.project)
+ quota = client.quotas.defaults(project)
else:
- quota = client.quotas.get(parsed_args.project)
+ quota = client.quotas.get(project)
except Exception as e:
if type(e).__name__ == 'EndpointNotFound':
return {}
diff --git a/openstackclient/common/session.py b/openstackclient/common/session.py
index dda1c417..9b19fd46 100644
--- a/openstackclient/common/session.py
+++ b/openstackclient/common/session.py
@@ -11,9 +11,9 @@
# under the License.
#
-"""Subclass of keystoneclient.session"""
+"""Subclass of keystoneauth1.session"""
-from keystoneclient import session
+from keystoneauth1 import session
class TimingSession(session.Session):
diff --git a/openstackclient/common/utils.py b/openstackclient/common/utils.py
index 51e2a2f9..42630d91 100644
--- a/openstackclient/common/utils.py
+++ b/openstackclient/common/utils.py
@@ -145,7 +145,6 @@ def format_dict(data):
"""Return a formatted string of key value pairs
:param data: a dict
- :param format: optional formatting hints
:rtype: a string formatted to key='value'
"""
@@ -298,14 +297,16 @@ def wait_for_status(status_f,
res_id,
status_field='status',
success_status=['active'],
+ error_status=['error'],
sleep_time=5,
callback=None):
"""Wait for status change on a resource during a long-running operation
:param status_f: a status function that takes a single id argument
:param res_id: the resource id to watch
- :param success_status: a list of status strings for successful completion
:param status_field: the status attribute in the returned resource object
+ :param success_status: a list of status strings for successful completion
+ :param error_status: a list of status strings for error
:param sleep_time: wait this long (seconds)
:param callback: called per sleep cycle, useful to display progress
:rtype: True on success
@@ -316,7 +317,7 @@ def wait_for_status(status_f,
if status in success_status:
retval = True
break
- elif status == 'error':
+ elif status in error_status:
retval = False
break
if callback:
@@ -334,6 +335,7 @@ def wait_for_delete(manager,
callback=None):
"""Wait for resource deletion
+ :param manager: the manager from which we can get the resource
:param res_id: the resource id to watch
:param status_field: the status attribute in the returned resource object,
this is used to check for error states while the resource is being
@@ -375,7 +377,7 @@ def wait_for_delete(manager,
def get_effective_log_level():
"""Returns the lowest logging level considered by logging handlers
- Retrieve an return the smallest log level set among the root
+ Retrieve and return the smallest log level set among the root
logger's handlers (in case of multiple handlers).
"""
root_log = logging.getLogger()
@@ -419,3 +421,11 @@ def build_kwargs_dict(arg_name, value):
if value:
kwargs[arg_name] = value
return kwargs
+
+
+def is_ascii(string):
+ try:
+ string.decode('ascii')
+ return True
+ except UnicodeDecodeError:
+ return False
diff --git a/openstackclient/compute/v2/console.py b/openstackclient/compute/v2/console.py
index bb0747b1..aafa5d44 100644
--- a/openstackclient/compute/v2/console.py
+++ b/openstackclient/compute/v2/console.py
@@ -22,6 +22,7 @@ import sys
from cliff import command
from cliff import show
+from openstackclient.common import parseractions
from openstackclient.common import utils
@@ -42,6 +43,7 @@ class ShowConsoleLog(command.Command):
metavar='<num-lines>',
type=int,
default=None,
+ action=parseractions.NonNegativeAction,
help='Number of lines to display from the end of the log '
'(default=all)',
)
diff --git a/openstackclient/compute/v2/fixedip.py b/openstackclient/compute/v2/fixedip.py
index d105e391..da9d85c3 100644
--- a/openstackclient/compute/v2/fixedip.py
+++ b/openstackclient/compute/v2/fixedip.py
@@ -23,7 +23,7 @@ from openstackclient.common import utils
class AddFixedIP(command.Command):
- """Add fixed-ip command"""
+ """Add fixed IP address to server"""
log = logging.getLogger(__name__ + ".AddFixedIP")
@@ -32,12 +32,12 @@ class AddFixedIP(command.Command):
parser.add_argument(
"network",
metavar="<network>",
- help="Name of the network to fetch an IP address from",
+ help="Network to fetch an IP address from (name or ID)",
)
parser.add_argument(
"server",
metavar="<server>",
- help="Name of the server to receive the IP address",
+ help="Server to receive the IP address (name or ID)",
)
return parser
@@ -56,7 +56,7 @@ class AddFixedIP(command.Command):
class RemoveFixedIP(command.Command):
- """Remove fixed-ip command"""
+ """Remove fixed IP address from server"""
log = logging.getLogger(__name__ + ".RemoveFixedIP")
@@ -65,12 +65,12 @@ class RemoveFixedIP(command.Command):
parser.add_argument(
"ip_address",
metavar="<ip-address>",
- help="IP address to remove from server",
+ help="IP address to remove from server (name only)",
)
parser.add_argument(
"server",
metavar="<server>",
- help="Name of the server to remove the IP address from",
+ help="Server to remove the IP address from (name or ID)",
)
return parser
diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py
index 3458cf79..7474580b 100644
--- a/openstackclient/compute/v2/flavor.py
+++ b/openstackclient/compute/v2/flavor.py
@@ -181,6 +181,15 @@ class ListFlavor(lister.Lister):
action='store_true',
default=False,
help='List additional fields in output')
+ parser.add_argument(
+ '--marker',
+ metavar="<marker>",
+ help='The last flavor ID of the previous page')
+ parser.add_argument(
+ '--limit',
+ type=int,
+ metavar="<limit>",
+ help='Maximum number of flavors to display')
return parser
def take_action(self, parsed_args):
@@ -202,7 +211,9 @@ class ListFlavor(lister.Lister):
# and flavors from their own projects only.
is_public = None if parsed_args.all else parsed_args.public
- data = compute_client.flavors.list(is_public=is_public)
+ data = compute_client.flavors.list(is_public=is_public,
+ marker=parsed_args.marker,
+ limit=parsed_args.limit)
if parsed_args.long:
columns = columns + (
diff --git a/openstackclient/compute/v2/floatingip.py b/openstackclient/compute/v2/floatingip.py
index c557c24b..65fe5910 100644
--- a/openstackclient/compute/v2/floatingip.py
+++ b/openstackclient/compute/v2/floatingip.py
@@ -26,7 +26,7 @@ from openstackclient.common import utils
class AddFloatingIP(command.Command):
- """Add floating-ip to server"""
+ """Add floating IP address to server"""
log = logging.getLogger(__name__ + ".AddFloatingIP")
@@ -35,7 +35,7 @@ class AddFloatingIP(command.Command):
parser.add_argument(
"ip_address",
metavar="<ip-address>",
- help="IP address to add to server",
+ help="IP address to add to server (name only)",
)
parser.add_argument(
"server",
@@ -56,7 +56,7 @@ class AddFloatingIP(command.Command):
class CreateFloatingIP(show.ShowOne):
- """Create new floating-ip"""
+ """Create new floating IP address"""
log = logging.getLogger(__name__ + '.CreateFloatingIP')
@@ -65,7 +65,7 @@ class CreateFloatingIP(show.ShowOne):
parser.add_argument(
'pool',
metavar='<pool>',
- help='Pool to fetch floating IP from',
+ help='Pool to fetch IP address from (name or ID)',
)
return parser
@@ -80,7 +80,7 @@ class CreateFloatingIP(show.ShowOne):
class DeleteFloatingIP(command.Command):
- """Delete a floating-ip"""
+ """Delete a floating IP address"""
log = logging.getLogger(__name__ + '.DeleteFloatingIP')
@@ -89,7 +89,7 @@ class DeleteFloatingIP(command.Command):
parser.add_argument(
"ip_address",
metavar="<ip-address>",
- help="IP address to delete",
+ help="IP address to delete (ID only)",
)
return parser
@@ -107,7 +107,7 @@ class DeleteFloatingIP(command.Command):
class ListFloatingIP(lister.Lister):
- """List floating-ips"""
+ """List floating IP addresses"""
log = logging.getLogger(__name__ + '.ListFloatingIP')
@@ -127,7 +127,7 @@ class ListFloatingIP(lister.Lister):
class RemoveFloatingIP(command.Command):
- """Remove floating-ip from server"""
+ """Remove floating IP address from server"""
log = logging.getLogger(__name__ + ".RemoveFloatingIP")
@@ -136,7 +136,7 @@ class RemoveFloatingIP(command.Command):
parser.add_argument(
"ip_address",
metavar="<ip-address>",
- help="IP address to remove from server",
+ help="IP address to remove from server (name only)",
)
parser.add_argument(
"server",
diff --git a/openstackclient/compute/v2/floatingippool.py b/openstackclient/compute/v2/floatingippool.py
index cc485552..39a2d8fe 100644
--- a/openstackclient/compute/v2/floatingippool.py
+++ b/openstackclient/compute/v2/floatingippool.py
@@ -23,7 +23,7 @@ from openstackclient.common import utils
class ListFloatingIPPool(lister.Lister):
- """List floating-ip-pools"""
+ """List pools of floating IP addresses"""
log = logging.getLogger(__name__ + '.ListFloatingIPPool')
diff --git a/openstackclient/compute/v2/security_group.py b/openstackclient/compute/v2/security_group.py
index 3dd0c49b..6d38195c 100644
--- a/openstackclient/compute/v2/security_group.py
+++ b/openstackclient/compute/v2/security_group.py
@@ -23,7 +23,7 @@ from cliff import command
from cliff import lister
from cliff import show
-from keystoneclient import exceptions as ksc_exc
+from keystoneauth1 import exceptions as ks_exc
try:
from novaclient.v2 import security_group_rules
@@ -93,6 +93,64 @@ class CreateSecurityGroup(show.ShowOne):
return zip(*sorted(six.iteritems(info)))
+class CreateSecurityGroupRule(show.ShowOne):
+ """Create a new security group rule"""
+
+ log = logging.getLogger(__name__ + ".CreateSecurityGroupRule")
+
+ def get_parser(self, prog_name):
+ parser = super(CreateSecurityGroupRule, self).get_parser(prog_name)
+ parser.add_argument(
+ 'group',
+ metavar='<group>',
+ help='Create rule in this security group (name or ID)',
+ )
+ parser.add_argument(
+ "--proto",
+ metavar="<proto>",
+ default="tcp",
+ help="IP protocol (icmp, tcp, udp; default: tcp)",
+ )
+ parser.add_argument(
+ "--src-ip",
+ metavar="<ip-address>",
+ default="0.0.0.0/0",
+ help="Source IP (may use CIDR notation; default: 0.0.0.0/0)",
+ )
+ parser.add_argument(
+ "--dst-port",
+ metavar="<port-range>",
+ default=(0, 0),
+ action=parseractions.RangeAction,
+ help="Destination port, may be a range: 137:139 (default: 0; "
+ "only required for proto tcp and udp)",
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug("take_action(%s)", parsed_args)
+
+ compute_client = self.app.client_manager.compute
+ group = utils.find_resource(
+ compute_client.security_groups,
+ parsed_args.group,
+ )
+ if parsed_args.proto.lower() == 'icmp':
+ from_port, to_port = -1, -1
+ else:
+ from_port, to_port = parsed_args.dst_port
+ data = compute_client.security_group_rules.create(
+ group.id,
+ parsed_args.proto,
+ from_port,
+ to_port,
+ parsed_args.src_ip,
+ )
+
+ info = _xform_security_group_rule(data._info)
+ return zip(*sorted(six.iteritems(info)))
+
+
class DeleteSecurityGroup(command.Command):
"""Delete a security group"""
@@ -103,7 +161,7 @@ class DeleteSecurityGroup(command.Command):
parser.add_argument(
'group',
metavar='<group>',
- help='Name or ID of security group to delete',
+ help='Security group to delete (name or ID)',
)
return parser
@@ -119,8 +177,30 @@ class DeleteSecurityGroup(command.Command):
return
+class DeleteSecurityGroupRule(command.Command):
+ """Delete a security group rule"""
+
+ log = logging.getLogger(__name__ + '.DeleteSecurityGroupRule')
+
+ def get_parser(self, prog_name):
+ parser = super(DeleteSecurityGroupRule, self).get_parser(prog_name)
+ parser.add_argument(
+ 'rule',
+ metavar='<rule>',
+ help='Security group rule to delete (ID only)',
+ )
+ return parser
+
+ @utils.log_method(log)
+ def take_action(self, parsed_args):
+
+ compute_client = self.app.client_manager.compute
+ compute_client.security_group_rules.delete(parsed_args.rule)
+ return
+
+
class ListSecurityGroup(lister.Lister):
- """List all security groups"""
+ """List security groups"""
log = logging.getLogger(__name__ + ".ListSecurityGroup")
@@ -161,7 +241,7 @@ class ListSecurityGroup(lister.Lister):
project_hash = {}
try:
projects = self.app.client_manager.identity.projects.list()
- except ksc_exc.ClientException:
+ except ks_exc.ClientException:
# This fails when the user is not an admin, just move along
pass
else:
@@ -175,6 +255,49 @@ class ListSecurityGroup(lister.Lister):
) for s in data))
+class ListSecurityGroupRule(lister.Lister):
+ """List security group rules"""
+
+ log = logging.getLogger(__name__ + ".ListSecurityGroupRule")
+
+ def get_parser(self, prog_name):
+ parser = super(ListSecurityGroupRule, self).get_parser(prog_name)
+ parser.add_argument(
+ 'group',
+ metavar='<group>',
+ help='List all rules in this security group (name or ID)',
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug("take_action(%s)", parsed_args)
+
+ compute_client = self.app.client_manager.compute
+ group = utils.find_resource(
+ compute_client.security_groups,
+ parsed_args.group,
+ )
+
+ # Argh, the rules are not Resources...
+ rules = []
+ for rule in group.rules:
+ rules.append(security_group_rules.SecurityGroupRule(
+ compute_client.security_group_rules,
+ _xform_security_group_rule(rule),
+ ))
+
+ columns = column_headers = (
+ "ID",
+ "IP Protocol",
+ "IP Range",
+ "Port Range",
+ )
+ return (column_headers,
+ (utils.get_item_properties(
+ s, columns,
+ ) for s in rules))
+
+
class SetSecurityGroup(show.ShowOne):
"""Set security group properties"""
@@ -185,7 +308,7 @@ class SetSecurityGroup(show.ShowOne):
parser.add_argument(
'group',
metavar='<group>',
- help='Name or ID of security group to change',
+ help='Security group to modify (name or ID)',
)
parser.add_argument(
'--name',
@@ -195,7 +318,7 @@ class SetSecurityGroup(show.ShowOne):
parser.add_argument(
"--description",
metavar="<description>",
- help="New security group name",
+ help="New security group description",
)
return parser
@@ -227,7 +350,7 @@ class SetSecurityGroup(show.ShowOne):
class ShowSecurityGroup(show.ShowOne):
- """Show a specific security group"""
+ """Display security group details"""
log = logging.getLogger(__name__ + '.ShowSecurityGroup')
@@ -236,7 +359,7 @@ class ShowSecurityGroup(show.ShowOne):
parser.add_argument(
'group',
metavar='<group>',
- help='Name or ID of security group to change',
+ help='Security group to display (name or ID)',
)
return parser
@@ -263,126 +386,3 @@ class ShowSecurityGroup(show.ShowOne):
)
return zip(*sorted(six.iteritems(info)))
-
-
-class CreateSecurityGroupRule(show.ShowOne):
- """Create a new security group rule"""
-
- log = logging.getLogger(__name__ + ".CreateSecurityGroupRule")
-
- def get_parser(self, prog_name):
- parser = super(CreateSecurityGroupRule, self).get_parser(prog_name)
- parser.add_argument(
- 'group',
- metavar='<group>',
- help='Create rule in this security group',
- )
- parser.add_argument(
- "--proto",
- metavar="<proto>",
- default="tcp",
- help="IP protocol (icmp, tcp, udp; default: tcp)",
- )
- parser.add_argument(
- "--src-ip",
- metavar="<ip-address>",
- default="0.0.0.0/0",
- help="Source IP (may use CIDR notation; default: 0.0.0.0/0)",
- )
- parser.add_argument(
- "--dst-port",
- metavar="<port-range>",
- default=(0, 0),
- action=parseractions.RangeAction,
- help="Destination port, may be a range: 137:139 (default: 0; "
- "only required for proto tcp and udp)",
- )
- return parser
-
- def take_action(self, parsed_args):
- self.log.debug("take_action(%s)", parsed_args)
-
- compute_client = self.app.client_manager.compute
- group = utils.find_resource(
- compute_client.security_groups,
- parsed_args.group,
- )
- if parsed_args.proto.lower() == 'icmp':
- from_port, to_port = -1, -1
- else:
- from_port, to_port = parsed_args.dst_port
- data = compute_client.security_group_rules.create(
- group.id,
- parsed_args.proto,
- from_port,
- to_port,
- parsed_args.src_ip,
- )
-
- info = _xform_security_group_rule(data._info)
- return zip(*sorted(six.iteritems(info)))
-
-
-class DeleteSecurityGroupRule(command.Command):
- """Delete a security group rule"""
-
- log = logging.getLogger(__name__ + '.DeleteSecurityGroupRule')
-
- def get_parser(self, prog_name):
- parser = super(DeleteSecurityGroupRule, self).get_parser(prog_name)
- parser.add_argument(
- 'rule',
- metavar='<rule>',
- help='Security group rule ID to delete',
- )
- return parser
-
- @utils.log_method(log)
- def take_action(self, parsed_args):
-
- compute_client = self.app.client_manager.compute
- compute_client.security_group_rules.delete(parsed_args.rule)
- return
-
-
-class ListSecurityGroupRule(lister.Lister):
- """List all security group rules"""
-
- log = logging.getLogger(__name__ + ".ListSecurityGroupRule")
-
- def get_parser(self, prog_name):
- parser = super(ListSecurityGroupRule, self).get_parser(prog_name)
- parser.add_argument(
- 'group',
- metavar='<group>',
- help='List all rules in this security group',
- )
- return parser
-
- def take_action(self, parsed_args):
- self.log.debug("take_action(%s)", parsed_args)
-
- compute_client = self.app.client_manager.compute
- group = utils.find_resource(
- compute_client.security_groups,
- parsed_args.group,
- )
-
- # Argh, the rules are not Resources...
- rules = []
- for rule in group.rules:
- rules.append(security_group_rules.SecurityGroupRule(
- compute_client.security_group_rules,
- _xform_security_group_rule(rule),
- ))
-
- columns = column_headers = (
- "ID",
- "IP Protocol",
- "IP Range",
- "Port Range",
- )
- return (column_headers,
- (utils.get_item_properties(
- s, columns,
- ) for s in rules))
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 661ce847..d58ebacd 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -43,7 +43,7 @@ from openstackclient.network import common
def _format_servers_list_networks(networks):
"""Return a formatted string of a server's networks
- :param server: a Server.networks field
+ :param networks: a Server.networks field
:rtype: a string of formatted network addresses
"""
output = []
@@ -56,6 +56,29 @@ def _format_servers_list_networks(networks):
return '; '.join(output)
+def _format_servers_list_power_state(state):
+ """Return a formatted string of a server's power state
+
+ :param state: the power state number of a server
+ :rtype: a string mapped to the power state number
+ """
+ power_states = [
+ 'NOSTATE', # 0x00
+ 'Running', # 0x01
+ '', # 0x02
+ 'Paused', # 0x03
+ 'Shutdown', # 0x04
+ '', # 0x05
+ 'Crashed', # 0x06
+ 'Suspended' # 0x07
+ ]
+
+ try:
+ return power_states[state]
+ except Exception:
+ return 'N/A'
+
+
def _get_ip_address(addresses, address_type, ip_address_family):
# Old style addresses
if address_type in addresses:
@@ -425,10 +448,17 @@ class CreateServer(show.ShowOne):
dev_key, dev_vol = dev_map.split('=', 1)
block_volume = None
if dev_vol:
- block_volume = utils.find_resource(
- volume_client.volumes,
- dev_vol,
- ).id
+ vol = dev_vol.split(':', 1)[0]
+ if vol:
+ vol_id = utils.find_resource(
+ volume_client.volumes,
+ vol,
+ ).id
+ block_volume = dev_vol.replace(vol, vol_id)
+ else:
+ msg = _("Volume name or ID must be specified if "
+ "--block-device-mapping is specified")
+ raise exceptions.CommandError(msg)
block_device_mapping.update({dev_key: block_volume})
nics = []
@@ -607,7 +637,7 @@ class DeleteServer(command.Command):
def get_parser(self, prog_name):
parser = super(DeleteServer, self).get_parser(prog_name)
parser.add_argument(
- 'servers',
+ 'server',
metavar='<server>',
nargs="+",
help=_('Server(s) to delete (name or ID)'),
@@ -622,7 +652,7 @@ class DeleteServer(command.Command):
@utils.log_method(log)
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
- for server in parsed_args.servers:
+ for server in parsed_args.server:
server_obj = utils.find_resource(
compute_client.servers, server)
compute_client.servers.delete(server_obj.id)
@@ -682,12 +712,12 @@ class ListServer(lister.Lister):
parser.add_argument(
'--flavor',
metavar='<flavor>',
- help=_('Search by flavor'),
+ help=_('Search by flavor (name or ID)'),
)
parser.add_argument(
'--image',
metavar='<image>',
- help=_('Search by image'),
+ help=_('Search by image (name or ID)'),
)
parser.add_argument(
'--host',
@@ -717,6 +747,24 @@ class ListServer(lister.Lister):
default=False,
help=_('List additional fields in output'),
)
+ parser.add_argument(
+ '--marker',
+ metavar='<marker>',
+ default=None,
+ help=('The last server (name or ID) of the previous page. Display'
+ ' list of servers after marker. Display all servers if not'
+ ' specified.')
+ )
+ parser.add_argument(
+ '--limit',
+ metavar='<limit>',
+ type=int,
+ default=None,
+ help=("Maximum number of servers to display. If limit equals -1,"
+ " all servers will be displayed. If limit is greater than"
+ " 'osapi_max_limit' option of Nova API,"
+ " 'osapi_max_limit' will be used instead."),
+ )
return parser
@utils.log_method(log)
@@ -735,12 +783,26 @@ class ListServer(lister.Lister):
user_id = None
if parsed_args.user:
- user_id = identity_common.find_project(
+ user_id = identity_common.find_user(
identity_client,
parsed_args.user,
parsed_args.user_domain,
).id
+ # Nova only supports list servers searching by flavor ID. So if a
+ # flavor name is given, map it to ID.
+ flavor_id = None
+ if parsed_args.flavor:
+ flavor_id = utils.find_resource(compute_client.flavors,
+ parsed_args.flavor).id
+
+ # Nova only supports list servers searching by image ID. So if a
+ # image name is given, map it to ID.
+ image_id = None
+ if parsed_args.image:
+ image_id = utils.find_resource(compute_client.images,
+ parsed_args.image).id
+
search_opts = {
'reservation_id': parsed_args.reservation_id,
'ip': parsed_args.ip,
@@ -748,8 +810,8 @@ class ListServer(lister.Lister):
'name': parsed_args.name,
'instance_name': parsed_args.instance_name,
'status': parsed_args.status,
- 'flavor': parsed_args.flavor,
- 'image': parsed_args.image,
+ 'flavor': flavor_id,
+ 'image': image_id,
'host': parsed_args.host,
'tenant_id': project_id,
'all_tenants': parsed_args.all_projects,
@@ -762,6 +824,8 @@ class ListServer(lister.Lister):
'ID',
'Name',
'Status',
+ 'OS-EXT-STS:task_state',
+ 'OS-EXT-STS:power_state',
'Networks',
'OS-EXT-AZ:availability_zone',
'OS-EXT-SRV-ATTR:host',
@@ -771,25 +835,49 @@ class ListServer(lister.Lister):
'ID',
'Name',
'Status',
+ 'Task State',
+ 'Power State',
'Networks',
'Availability Zone',
'Host',
'Properties',
)
mixed_case_fields = [
+ 'OS-EXT-STS:task_state',
+ 'OS-EXT-STS:power_state',
'OS-EXT-AZ:availability_zone',
'OS-EXT-SRV-ATTR:host',
]
else:
- columns = ('ID', 'Name', 'Status', 'Networks')
- column_headers = columns
+ columns = (
+ 'ID',
+ 'Name',
+ 'Status',
+ 'Networks',
+ )
+ column_headers = (
+ 'ID',
+ 'Name',
+ 'Status',
+ 'Networks',
+ )
mixed_case_fields = []
- data = compute_client.servers.list(search_opts=search_opts)
+
+ marker_id = None
+ if parsed_args.marker:
+ marker_id = utils.find_resource(compute_client.servers,
+ parsed_args.marker).id
+
+ data = compute_client.servers.list(search_opts=search_opts,
+ marker=marker_id,
+ limit=parsed_args.limit)
return (column_headers,
(utils.get_item_properties(
s, columns,
mixed_case_fields=mixed_case_fields,
formatters={
+ 'OS-EXT-STS:power_state':
+ _format_servers_list_power_state,
'Networks': _format_servers_list_networks,
'Metadata': utils.format_dict,
},
@@ -798,7 +886,7 @@ class ListServer(lister.Lister):
class LockServer(command.Command):
- """Lock a server. A non-admin user will not be able to execute actions"""
+ """Lock server(s). A non-admin user will not be able to execute actions"""
log = logging.getLogger(__name__ + '.LockServer')
@@ -807,7 +895,8 @@ class LockServer(command.Command):
parser.add_argument(
'server',
metavar='<server>',
- help=_('Server (name or ID)'),
+ nargs='+',
+ help=_('Server(s) to lock (name or ID)'),
)
return parser
@@ -815,10 +904,11 @@ class LockServer(command.Command):
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
- utils.find_resource(
- compute_client.servers,
- parsed_args.server,
- ).lock()
+ for server in parsed_args.server:
+ utils.find_resource(
+ compute_client.servers,
+ server,
+ ).lock()
# FIXME(dtroyer): Here is what I want, how with argparse/cliff?
@@ -915,7 +1005,7 @@ class MigrateServer(command.Command):
class PauseServer(command.Command):
- """Pause server"""
+ """Pause server(s)"""
log = logging.getLogger(__name__ + '.PauseServer')
@@ -924,18 +1014,19 @@ class PauseServer(command.Command):
parser.add_argument(
'server',
metavar='<server>',
- help=_('Server (name or ID)'),
+ nargs='+',
+ help=_('Server(s) to pause (name or ID)'),
)
return parser
@utils.log_method(log)
def take_action(self, parsed_args):
-
compute_client = self.app.client_manager.compute
- utils.find_resource(
- compute_client.servers,
- parsed_args.server,
- ).pause()
+ for server in parsed_args.server:
+ utils.find_resource(
+ compute_client.servers,
+ server
+ ).pause()
class RebootServer(command.Command):
@@ -1217,7 +1308,7 @@ class ResizeServer(command.Command):
class ResumeServer(command.Command):
- """Resume server"""
+ """Resume server(s)"""
log = logging.getLogger(__name__ + '.ResumeServer')
@@ -1226,7 +1317,8 @@ class ResumeServer(command.Command):
parser.add_argument(
'server',
metavar='<server>',
- help=_('Server (name or ID)'),
+ nargs='+',
+ help=_('Server(s) to resume (name or ID)'),
)
return parser
@@ -1234,10 +1326,11 @@ class ResumeServer(command.Command):
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
- utils.find_resource(
- compute_client.servers,
- parsed_args.server,
- ) .resume()
+ for server in parsed_args.server:
+ utils.find_resource(
+ compute_client.servers,
+ server,
+ ).resume()
class SetServer(command.Command):
@@ -1299,6 +1392,31 @@ class SetServer(command.Command):
raise exceptions.CommandError(msg)
+class ShelveServer(command.Command):
+ """Shelve server(s)"""
+
+ log = logging.getLogger(__name__ + '.ShelveServer')
+
+ def get_parser(self, prog_name):
+ parser = super(ShelveServer, self).get_parser(prog_name)
+ parser.add_argument(
+ 'server',
+ metavar='<server>',
+ nargs='+',
+ help=_('Server(s) to shelve (name or ID)'),
+ )
+ return parser
+
+ @utils.log_method(log)
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+ for server in parsed_args.server:
+ utils.find_resource(
+ compute_client.servers,
+ server,
+ ).shelve()
+
+
class ShowServer(show.ShowOne):
"""Show server details"""
@@ -1483,8 +1601,58 @@ class SshServer(command.Command):
os.system(cmd % (login, ip_address))
+class StartServer(command.Command):
+ """Start server(s)."""
+
+ log = logging.getLogger(__name__ + '.StartServer')
+
+ def get_parser(self, prog_name):
+ parser = super(StartServer, self).get_parser(prog_name)
+ parser.add_argument(
+ 'server',
+ metavar='<server>',
+ nargs="+",
+ help=_('Server(s) to start (name or ID)'),
+ )
+ return parser
+
+ @utils.log_method(log)
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+ for server in parsed_args.server:
+ utils.find_resource(
+ compute_client.servers,
+ server,
+ ).start()
+
+
+class StopServer(command.Command):
+ """Stop server(s)."""
+
+ log = logging.getLogger(__name__ + '.StopServer')
+
+ def get_parser(self, prog_name):
+ parser = super(StopServer, self).get_parser(prog_name)
+ parser.add_argument(
+ 'server',
+ metavar='<server>',
+ nargs="+",
+ help=_('Server(s) to stop (name or ID)'),
+ )
+ return parser
+
+ @utils.log_method(log)
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+ for server in parsed_args.server:
+ utils.find_resource(
+ compute_client.servers,
+ server,
+ ).stop()
+
+
class SuspendServer(command.Command):
- """Suspend server"""
+ """Suspend server(s)"""
log = logging.getLogger(__name__ + '.SuspendServer')
@@ -1493,7 +1661,8 @@ class SuspendServer(command.Command):
parser.add_argument(
'server',
metavar='<server>',
- help=_('Server (name or ID)'),
+ nargs='+',
+ help=_('Server(s) to suspend (name or ID)'),
)
return parser
@@ -1501,14 +1670,15 @@ class SuspendServer(command.Command):
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
- utils.find_resource(
- compute_client.servers,
- parsed_args.server,
- ).suspend()
+ for server in parsed_args.server:
+ utils.find_resource(
+ compute_client.servers,
+ server,
+ ).suspend()
class UnlockServer(command.Command):
- """Unlock server"""
+ """Unlock server(s)"""
log = logging.getLogger(__name__ + '.UnlockServer')
@@ -1517,7 +1687,8 @@ class UnlockServer(command.Command):
parser.add_argument(
'server',
metavar='<server>',
- help=_('Server (name or ID)'),
+ nargs='+',
+ help=_('Server(s) to unlock (name or ID)'),
)
return parser
@@ -1525,14 +1696,15 @@ class UnlockServer(command.Command):
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
- utils.find_resource(
- compute_client.servers,
- parsed_args.server,
- ).unlock()
+ for server in parsed_args.server:
+ utils.find_resource(
+ compute_client.servers,
+ server,
+ ).unlock()
class UnpauseServer(command.Command):
- """Unpause server"""
+ """Unpause server(s)"""
log = logging.getLogger(__name__ + '.UnpauseServer')
@@ -1541,7 +1713,8 @@ class UnpauseServer(command.Command):
parser.add_argument(
'server',
metavar='<server>',
- help=_('Server (name or ID)'),
+ nargs='+',
+ help=_('Server(s) to unpause (name or ID)'),
)
return parser
@@ -1549,10 +1722,11 @@ class UnpauseServer(command.Command):
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
- utils.find_resource(
- compute_client.servers,
- parsed_args.server,
- ).unpause()
+ for server in parsed_args.server:
+ utils.find_resource(
+ compute_client.servers,
+ server,
+ ).unpause()
class UnrescueServer(command.Command):
@@ -1614,3 +1788,28 @@ class UnsetServer(command.Command):
server,
parsed_args.property,
)
+
+
+class UnshelveServer(command.Command):
+ """Unshelve server(s)"""
+
+ log = logging.getLogger(__name__ + '.UnshelveServer')
+
+ def get_parser(self, prog_name):
+ parser = super(UnshelveServer, self).get_parser(prog_name)
+ parser.add_argument(
+ 'server',
+ metavar='<server>',
+ nargs='+',
+ help=_('Server(s) to unshelve (name or ID)'),
+ )
+ return parser
+
+ @utils.log_method(log)
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+ for server in parsed_args.server:
+ utils.find_resource(
+ compute_client.servers,
+ server,
+ ).unshelve()
diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py
index 3a031bff..c2d51c2a 100644
--- a/openstackclient/compute/v2/service.py
+++ b/openstackclient/compute/v2/service.py
@@ -17,11 +17,33 @@
import logging
+from cliff import command
from cliff import lister
from openstackclient.common import utils
+class DeleteService(command.Command):
+ """Delete service command"""
+
+ log = logging.getLogger(__name__ + ".DeleteService")
+
+ def get_parser(self, prog_name):
+ parser = super(DeleteService, self).get_parser(prog_name)
+ parser.add_argument(
+ "service",
+ metavar="<service>",
+ help="Compute service to delete (ID only)")
+ return parser
+
+ def take_action(self, parsed_args):
+ self.log.debug("take_action(%s)", parsed_args)
+ compute_client = self.app.client_manager.compute
+
+ compute_client.services.delete(parsed_args.service)
+ return
+
+
class ListService(lister.Lister):
"""List service command"""
diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py
index 31b90c22..4e7cf100 100644
--- a/openstackclient/compute/v2/usage.py
+++ b/openstackclient/compute/v2/usage.py
@@ -183,25 +183,16 @@ class ShowUsage(show.ShowOne):
))
info = {}
- info['Servers'] = len(usage.server_usages)
- info['RAM MB-Hours'] = float("%.2f" % usage.total_memory_mb_usage)
- info['CPU Hours'] = float("%.2f" % usage.total_vcpus_usage)
- info['Disk GB-Hours'] = float("%.2f" % usage.total_local_gb_usage)
+ info['Servers'] = (
+ len(usage.server_usages)
+ if hasattr(usage, "server_usages") else None)
+ info['RAM MB-Hours'] = (
+ float("%.2f" % usage.total_memory_mb_usage)
+ if hasattr(usage, "total_memory_mb_usage") else None)
+ info['CPU Hours'] = (
+ float("%.2f" % usage.total_vcpus_usage)
+ if hasattr(usage, "total_vcpus_usage") else None)
+ info['Disk GB-Hours'] = (
+ float("%.2f" % usage.total_local_gb_usage)
+ if hasattr(usage, "total_local_gb_usage") else None)
return zip(*sorted(six.iteritems(info)))
-
-
-# This is out of order due to the subclass, will eventually be removed
-
-class ListProjectUsage(ListUsage):
- """List resource usage per project"""
-
- deprecated = True
-
- log = logging.getLogger('DEPRECATED:')
-
- def take_action(self, parsed_args):
- self.log.warning(
- "%s is deprecated, use 'usage list'",
- getattr(self, 'cmd_name', 'this command'),
- )
- return super(ListProjectUsage, self).take_action(parsed_args)
diff --git a/openstackclient/identity/client.py b/openstackclient/identity/client.py
index b8bb33f4..bd882ce8 100644
--- a/openstackclient/identity/client.py
+++ b/openstackclient/identity/client.py
@@ -21,7 +21,7 @@ from openstackclient.common import utils
LOG = logging.getLogger(__name__)
-DEFAULT_API_VERSION = '2'
+DEFAULT_API_VERSION = '3'
API_VERSION_OPTION = 'os_identity_api_version'
API_NAME = 'identity'
API_VERSIONS = {
diff --git a/openstackclient/identity/v2_0/project.py b/openstackclient/identity/v2_0/project.py
index 97a95f28..065f0adf 100644
--- a/openstackclient/identity/v2_0/project.py
+++ b/openstackclient/identity/v2_0/project.py
@@ -21,7 +21,7 @@ import six
from cliff import command
from cliff import lister
from cliff import show
-from keystoneclient import exceptions as ksc_exc
+from keystoneauth1 import exceptions as ks_exc
from openstackclient.common import parseractions
from openstackclient.common import utils
@@ -88,7 +88,7 @@ class CreateProject(show.ShowOne):
enabled=enabled,
**kwargs
)
- except ksc_exc.Conflict as e:
+ except ks_exc.Conflict as e:
if parsed_args.or_show:
project = utils.find_resource(
identity_client.tenants,
@@ -264,7 +264,7 @@ class ShowProject(show.ShowOne):
parsed_args.project,
)
info.update(project._info)
- except ksc_exc.Forbidden as e:
+ except ks_exc.Forbidden as e:
auth_ref = self.app.client_manager.auth_ref
if (
parsed_args.project == auth_ref.project_id or
diff --git a/openstackclient/identity/v2_0/role.py b/openstackclient/identity/v2_0/role.py
index e98f8cb3..cab6b4a5 100644
--- a/openstackclient/identity/v2_0/role.py
+++ b/openstackclient/identity/v2_0/role.py
@@ -21,7 +21,7 @@ import six
from cliff import command
from cliff import lister
from cliff import show
-from keystoneclient import exceptions as ksc_exc
+from keystoneauth1 import exceptions as ks_exc
from openstackclient.common import exceptions
from openstackclient.common import utils
@@ -98,7 +98,7 @@ class CreateRole(show.ShowOne):
identity_client = self.app.client_manager.identity
try:
role = identity_client.roles.create(parsed_args.role_name)
- except ksc_exc.Conflict as e:
+ except ks_exc.Conflict as e:
if parsed_args.or_show:
role = utils.find_resource(
identity_client.roles,
diff --git a/openstackclient/identity/v2_0/user.py b/openstackclient/identity/v2_0/user.py
index 76902e69..e2b285bd 100644
--- a/openstackclient/identity/v2_0/user.py
+++ b/openstackclient/identity/v2_0/user.py
@@ -21,7 +21,7 @@ import six
from cliff import command
from cliff import lister
from cliff import show
-from keystoneclient import exceptions as ksc_exc
+from keystoneauth1 import exceptions as ks_exc
from openstackclient.common import utils
from openstackclient.i18n import _ # noqa
@@ -104,7 +104,7 @@ class CreateUser(show.ShowOne):
tenant_id=project_id,
enabled=enabled,
)
- except ksc_exc.Conflict as e:
+ except ks_exc.Conflict as e:
if parsed_args.or_show:
user = utils.find_resource(
identity_client.users,
@@ -373,7 +373,7 @@ class ShowUser(show.ShowOne):
parsed_args.user,
)
info.update(user._info)
- except ksc_exc.Forbidden as e:
+ except ks_exc.Forbidden as e:
auth_ref = self.app.client_manager.auth_ref
if (
parsed_args.user == auth_ref.user_id or
diff --git a/openstackclient/identity/v3/credential.py b/openstackclient/identity/v3/credential.py
index dbd73e2e..f22092d4 100644
--- a/openstackclient/identity/v3/credential.py
+++ b/openstackclient/identity/v3/credential.py
@@ -17,7 +17,6 @@
import logging
import six
-import sys
from cliff import command
from cliff import lister
@@ -130,17 +129,20 @@ class SetCredential(command.Command):
parser.add_argument(
'--user',
metavar='<user>',
+ required=True,
help='Name or ID of user that owns the credential',
)
parser.add_argument(
'--type',
metavar='<type>',
choices=['ec2', 'cert'],
+ required=True,
help='New credential type',
)
parser.add_argument(
'--data',
metavar='<data>',
+ required=True,
help='New credential data',
)
parser.add_argument(
@@ -153,25 +155,22 @@ class SetCredential(command.Command):
@utils.log_method(log)
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
- kwargs = {}
- if parsed_args.user:
- user_id = utils.find_resource(identity_client.users,
- parsed_args.user).id
- if user_id:
- kwargs['user'] = user_id
- if parsed_args.type:
- kwargs['type'] = parsed_args.type
- if parsed_args.data:
- kwargs['data'] = parsed_args.data
+
+ user_id = utils.find_resource(identity_client.users,
+ parsed_args.user).id
+
if parsed_args.project:
project = utils.find_resource(identity_client.projects,
parsed_args.project).id
- kwargs['project'] = project
+ else:
+ project = None
+
+ identity_client.credentials.update(parsed_args.credential,
+ user=user_id,
+ type=parsed_args.type,
+ blob=parsed_args.data,
+ project=project)
- if not kwargs:
- sys.stdout.write("Credential not updated, no arguments present")
- return
- identity_client.credentials.update(parsed_args.credential, **kwargs)
return
diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py
index aec530a8..8278a300 100644
--- a/openstackclient/identity/v3/domain.py
+++ b/openstackclient/identity/v3/domain.py
@@ -22,7 +22,7 @@ import sys
from cliff import command
from cliff import lister
from cliff import show
-from keystoneclient import exceptions as ksc_exc
+from keystoneauth1 import exceptions as ks_exc
from openstackclient.common import utils
from openstackclient.i18n import _ # noqa
@@ -77,7 +77,7 @@ class CreateDomain(show.ShowOne):
description=parsed_args.description,
enabled=enabled,
)
- except ksc_exc.Conflict as e:
+ except ks_exc.Conflict as e:
if parsed_args.or_show:
domain = utils.find_resource(identity_client.domains,
parsed_args.name)
diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py
index 82975065..d503a6b8 100644
--- a/openstackclient/identity/v3/group.py
+++ b/openstackclient/identity/v3/group.py
@@ -22,7 +22,7 @@ import sys
from cliff import command
from cliff import lister
from cliff import show
-from keystoneclient import exceptions as ksc_exc
+from keystoneauth1 import exceptions as ks_exc
from openstackclient.common import utils
from openstackclient.i18n import _ # noqa
@@ -156,7 +156,7 @@ class CreateGroup(show.ShowOne):
name=parsed_args.name,
domain=domain,
description=parsed_args.description)
- except ksc_exc.Conflict as e:
+ except ks_exc.Conflict as e:
if parsed_args.or_show:
group = utils.find_resource(identity_client.groups,
parsed_args.name,
diff --git a/openstackclient/identity/v3/identity_provider.py b/openstackclient/identity/v3/identity_provider.py
index 27982a9d..4e086b68 100644
--- a/openstackclient/identity/v3/identity_provider.py
+++ b/openstackclient/identity/v3/identity_provider.py
@@ -88,6 +88,8 @@ class CreateIdentityProvider(show.ShowOne):
enabled=parsed_args.enabled)
idp._info.pop('links', None)
+ remote_ids = utils.format_list(idp._info.pop('remote_ids', []))
+ idp._info['remote_ids'] = remote_ids
return zip(*sorted(six.iteritems(idp._info)))
@@ -142,6 +144,11 @@ class SetIdentityProvider(command.Command):
metavar='<identity-provider>',
help='Identity provider to modify',
)
+ parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help='Set identity provider description',
+ )
identity_remote_id_provider = parser.add_mutually_exclusive_group()
identity_remote_id_provider.add_argument(
'--remote-id',
@@ -174,8 +181,10 @@ class SetIdentityProvider(command.Command):
federation_client = self.app.client_manager.identity.federation
# Basic argument checking
- if (not parsed_args.enable and not parsed_args.disable and not
- parsed_args.remote_id and not parsed_args.remote_id_file):
+ if (not parsed_args.enable and not parsed_args.disable and
+ not parsed_args.remote_id and
+ not parsed_args.remote_id_file and
+ not parsed_args.description):
self.log.error('No changes requested')
return (None, None)
@@ -190,6 +199,8 @@ class SetIdentityProvider(command.Command):
# Setup keyword args for the client
kwargs = {}
+ if parsed_args.description:
+ kwargs['description'] = parsed_args.description
if parsed_args.enable:
kwargs['enabled'] = True
if parsed_args.disable:
@@ -221,9 +232,11 @@ class ShowIdentityProvider(show.ShowOne):
@utils.log_method(log)
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
- identity_provider = utils.find_resource(
+ idp = utils.find_resource(
identity_client.federation.identity_providers,
parsed_args.identity_provider)
- identity_provider._info.pop('links', None)
- return zip(*sorted(six.iteritems(identity_provider._info)))
+ idp._info.pop('links', None)
+ remote_ids = utils.format_list(idp._info.pop('remote_ids', []))
+ idp._info['remote_ids'] = remote_ids
+ return zip(*sorted(six.iteritems(idp._info)))
diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py
index 96d7f97b..f87105dd 100644
--- a/openstackclient/identity/v3/project.py
+++ b/openstackclient/identity/v3/project.py
@@ -21,7 +21,7 @@ import six
from cliff import command
from cliff import lister
from cliff import show
-from keystoneclient import exceptions as ksc_exc
+from keystoneauth1 import exceptions as ks_exc
from openstackclient.common import parseractions
from openstackclient.common import utils
@@ -113,7 +113,7 @@ class CreateProject(show.ShowOne):
enabled=enabled,
**kwargs
)
- except ksc_exc.Conflict as e:
+ except ks_exc.Conflict as e:
if parsed_args.or_show:
project = utils.find_resource(identity_client.projects,
parsed_args.name,
diff --git a/openstackclient/identity/v3/region.py b/openstackclient/identity/v3/region.py
index eb4c084c..1ff0b8c0 100644
--- a/openstackclient/identity/v3/region.py
+++ b/openstackclient/identity/v3/region.py
@@ -48,12 +48,6 @@ class CreateRegion(show.ShowOne):
metavar='<description>',
help=_('New region description'),
)
- parser.add_argument(
- '--url',
- metavar='<url>',
- help=_('New region url'),
- )
-
return parser
@utils.log_method(log)
@@ -62,7 +56,6 @@ class CreateRegion(show.ShowOne):
region = identity_client.regions.create(
id=parsed_args.region,
- url=parsed_args.url,
parent_region=parsed_args.parent_region,
description=parsed_args.description,
)
@@ -117,8 +110,8 @@ class ListRegion(lister.Lister):
if parsed_args.parent_region:
kwargs['parent_region_id'] = parsed_args.parent_region
- columns_headers = ('Region', 'Parent Region', 'Description', 'URL')
- columns = ('ID', 'Parent Region Id', 'Description', 'URL')
+ columns_headers = ('Region', 'Parent Region', 'Description')
+ columns = ('ID', 'Parent Region Id', 'Description')
data = identity_client.regions.list(**kwargs)
return (columns_headers,
@@ -150,25 +143,16 @@ class SetRegion(command.Command):
metavar='<description>',
help=_('New region description'),
)
- parser.add_argument(
- '--url',
- metavar='<url>',
- help=_('New region url'),
- )
return parser
@utils.log_method(log)
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
- if (not parsed_args.url
- and not parsed_args.parent_region
- and not parsed_args.description):
+ if not parsed_args.parent_region and not parsed_args.description:
return
kwargs = {}
- if parsed_args.url:
- kwargs['url'] = parsed_args.url
if parsed_args.description:
kwargs['description'] = parsed_args.description
if parsed_args.parent_region:
diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py
index 0e8c51ca..66f18964 100644
--- a/openstackclient/identity/v3/role.py
+++ b/openstackclient/identity/v3/role.py
@@ -22,7 +22,7 @@ import sys
from cliff import command
from cliff import lister
from cliff import show
-from keystoneclient import exceptions as ksc_exc
+from keystoneauth1 import exceptions as ks_exc
from openstackclient.common import utils
from openstackclient.i18n import _ # noqa
@@ -172,7 +172,7 @@ class CreateRole(show.ShowOne):
try:
role = identity_client.roles.create(name=parsed_args.name)
- except ksc_exc.Conflict as e:
+ except ks_exc.Conflict as e:
if parsed_args.or_show:
role = utils.find_resource(identity_client.roles,
parsed_args.name)
diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py
index 0e894544..eaef8f05 100644
--- a/openstackclient/identity/v3/user.py
+++ b/openstackclient/identity/v3/user.py
@@ -22,7 +22,7 @@ import six
from cliff import command
from cliff import lister
from cliff import show
-from keystoneclient import exceptions as ksc_exc
+from keystoneauth1 import exceptions as ks_exc
from openstackclient.common import utils
from openstackclient.i18n import _ # noqa
@@ -122,7 +122,7 @@ class CreateUser(show.ShowOne):
description=parsed_args.description,
enabled=enabled
)
- except ksc_exc.Conflict as e:
+ except ks_exc.Conflict as e:
if parsed_args.or_show:
user = utils.find_resource(identity_client.users,
parsed_args.name,
diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py
index 35e9ef43..4ebc8f93 100644
--- a/openstackclient/image/v1/image.py
+++ b/openstackclient/image/v1/image.py
@@ -354,7 +354,12 @@ class ListImage(lister.Lister):
kwargs['public'] = True
if parsed_args.private:
kwargs['private'] = True
- kwargs['detailed'] = bool(parsed_args.property or parsed_args.long)
+ # Note: We specifically need to do that below to get the 'status'
+ # column.
+ #
+ # Always set kwargs['detailed'] to True, and then filter the columns
+ # according to whether the --long option is specified or not.
+ kwargs['detailed'] = True
if parsed_args.long:
columns = (
@@ -382,7 +387,7 @@ class ListImage(lister.Lister):
'Properties',
)
else:
- columns = ("ID", "Name")
+ columns = ("ID", "Name", "Status")
column_headers = columns
# List of image data received
diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py
index 2b3f3bfd..a3c1a99d 100644
--- a/openstackclient/image/v2/image.py
+++ b/openstackclient/image/v2/image.py
@@ -105,21 +105,20 @@ class AddProjectToImage(show.ShowOne):
project_id,
)
- return zip(*sorted(six.iteritems(image_member._info)))
+ return zip(*sorted(six.iteritems(image_member)))
class CreateImage(show.ShowOne):
"""Create/upload an image"""
log = logging.getLogger(__name__ + ".CreateImage")
- deadopts = ('owner', 'size', 'location', 'copy-from', 'checksum', 'store')
+ deadopts = ('size', 'location', 'copy-from', 'checksum', 'store')
def get_parser(self, prog_name):
parser = super(CreateImage, self).get_parser(prog_name)
# TODO(bunting): There are additional arguments that v1 supported
# that v2 either doesn't support or supports weirdly.
# --checksum - could be faked clientside perhaps?
- # --owner - could be set as an update after the put?
# --location - maybe location add?
# --size - passing image size is actually broken in python-glanceclient
# --copy-from - does not exist in v2
@@ -149,6 +148,11 @@ class CreateImage(show.ShowOne):
"(default: %s)" % DEFAULT_DISK_FORMAT,
)
parser.add_argument(
+ "--owner",
+ metavar="<owner>",
+ help="Image owner project name or ID",
+ )
+ parser.add_argument(
"--min-disk",
metavar="<disk-gb>",
type=int,
@@ -241,7 +245,7 @@ class CreateImage(show.ShowOne):
copy_attrs = ('name', 'id',
'container_format', 'disk_format',
'min_disk', 'min_ram',
- 'tags')
+ 'tags', 'owner')
for attr in copy_attrs:
if attr in parsed_args:
val = getattr(parsed_args, attr, None)
@@ -449,7 +453,7 @@ class ListImage(lister.Lister):
'Tags',
)
else:
- columns = ("ID", "Name")
+ columns = ("ID", "Name", "Status")
column_headers = columns
# List of image data received
diff --git a/openstackclient/network/client.py b/openstackclient/network/client.py
index 5f72782b..69ed11fe 100644
--- a/openstackclient/network/client.py
+++ b/openstackclient/network/client.py
@@ -18,10 +18,11 @@ from openstackclient.common import utils
LOG = logging.getLogger(__name__)
-DEFAULT_API_VERSION = '2'
+DEFAULT_API_VERSION = '2.0'
API_VERSION_OPTION = 'os_network_api_version'
API_NAME = "network"
API_VERSIONS = {
+ "2.0": "neutronclient.v2_0.client.Client",
"2": "neutronclient.v2_0.client.Client",
}
# Translate our API version to auth plugin version prefix
@@ -32,6 +33,7 @@ API_VERSION_MAP = {
NETWORK_API_TYPE = 'network'
NETWORK_API_VERSIONS = {
+ '2.0': 'openstackclient.api.network_v2.APIv2',
'2': 'openstackclient.api.network_v2.APIv2',
}
diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py
index bd6203bd..31faef25 100644
--- a/openstackclient/network/common.py
+++ b/openstackclient/network/common.py
@@ -27,7 +27,8 @@ def find(client, resource, resources, name_or_id, name_attr='name'):
n = find(netclient, 'network', 'networks', 'matrix')
"""
list_method = getattr(client, "list_%s" % resources)
- # Search for by name
+
+ # Search by name
kwargs = {name_attr: name_or_id, 'fields': 'id'}
data = list_method(**kwargs)
info = data[resources]
@@ -36,7 +37,8 @@ def find(client, resource, resources, name_or_id, name_attr='name'):
if len(info) > 1:
msg = "More than one %s exists with the name '%s'."
raise exceptions.CommandError(msg % (resource, name_or_id))
- # Search for by id
+
+ # Search by id
data = list_method(id=name_or_id, fields='id')
info = data[resources]
if len(info) == 1:
diff --git a/openstackclient/object/v1/account.py b/openstackclient/object/v1/account.py
index 4ff890ce..aa94ff5c 100644
--- a/openstackclient/object/v1/account.py
+++ b/openstackclient/object/v1/account.py
@@ -55,6 +55,8 @@ class ShowAccount(show.ShowOne):
@utils.log_method(log)
def take_action(self, parsed_args):
data = self.app.client_manager.object_store.account_show()
+ if 'properties' in data:
+ data['properties'] = utils.format_dict(data.pop('properties'))
return zip(*sorted(six.iteritems(data)))
diff --git a/openstackclient/object/v1/container.py b/openstackclient/object/v1/container.py
index b8eb4c25..8c8844e2 100644
--- a/openstackclient/object/v1/container.py
+++ b/openstackclient/object/v1/container.py
@@ -229,6 +229,8 @@ class ShowContainer(show.ShowOne):
data = self.app.client_manager.object_store.container_show(
container=parsed_args.container,
)
+ if 'properties' in data:
+ data['properties'] = utils.format_dict(data.pop('properties'))
return zip(*sorted(six.iteritems(data)))
diff --git a/openstackclient/object/v1/object.py b/openstackclient/object/v1/object.py
index a023e3a0..4bd06124 100644
--- a/openstackclient/object/v1/object.py
+++ b/openstackclient/object/v1/object.py
@@ -284,6 +284,8 @@ class ShowObject(show.ShowOne):
container=parsed_args.container,
object=parsed_args.object,
)
+ if 'properties' in data:
+ data['properties'] = utils.format_dict(data.pop('properties'))
return zip(*sorted(six.iteritems(data)))
diff --git a/openstackclient/shell.py b/openstackclient/shell.py
index 5b36b8b2..5b4939a2 100644
--- a/openstackclient/shell.py
+++ b/openstackclient/shell.py
@@ -25,12 +25,13 @@ from cliff import app
from cliff import command
from cliff import complete
from cliff import help
+from oslo_utils import strutils
import openstackclient
from openstackclient.common import clientmanager
from openstackclient.common import commandmanager
-from openstackclient.common import context
from openstackclient.common import exceptions as exc
+from openstackclient.common import logs
from openstackclient.common import timing
from openstackclient.common import utils
@@ -102,7 +103,7 @@ class OpenStackShell(app.App):
def configure_logging(self):
"""Configure logging for the app."""
- self.log_configurator = context.LogConfigurator(self.options)
+ self.log_configurator = logs.LogConfigurator(self.options)
self.dump_stack_trace = self.log_configurator.dump_trace
def run(self, argv):
@@ -201,8 +202,10 @@ class OpenStackShell(app.App):
# Parent __init__ parses argv into self.options
super(OpenStackShell, self).initialize_app(argv)
- self.log.info("START with options: %s", self.command_options)
- self.log.debug("options: %s", self.options)
+ self.log.info("START with options: %s",
+ strutils.mask_password(self.command_options))
+ self.log.debug("options: %s",
+ strutils.mask_password(self.options))
# Set the default plugin to token_endpoint if url and token are given
if (self.options.url and self.options.token):
@@ -238,6 +241,10 @@ class OpenStackShell(app.App):
},
)
+ # TODO(thowe): Change cliff so the default value for debug
+ # can be set to None.
+ if not self.options.debug:
+ self.options.debug = None
self.cloud = cc.get_one_cloud(
cloud=self.options.cloud,
argparse=self.options,
@@ -246,7 +253,8 @@ class OpenStackShell(app.App):
self.log_configurator.configure(self.cloud)
self.dump_stack_trace = self.log_configurator.dump_trace
self.log.debug("defaults: %s", cc.defaults)
- self.log.debug("cloud cfg: %s", self.cloud.config)
+ self.log.debug("cloud cfg: %s",
+ strutils.mask_password(self.cloud.config))
# Set up client TLS
# NOTE(dtroyer): --insecure is the non-default condition that
@@ -272,7 +280,7 @@ class OpenStackShell(app.App):
for mod in clientmanager.PLUGIN_MODULES:
default_version = getattr(mod, 'DEFAULT_API_VERSION', None)
option = mod.API_VERSION_OPTION.replace('os_', '')
- version_opt = self.cloud.config.get(option, default_version)
+ version_opt = str(self.cloud.config.get(option, default_version))
if version_opt:
api = mod.API_NAME
self.api_version[api] = version_opt
diff --git a/openstackclient/tests/api/fakes.py b/openstackclient/tests/api/fakes.py
index 85617ab7..e285a61c 100644
--- a/openstackclient/tests/api/fakes.py
+++ b/openstackclient/tests/api/fakes.py
@@ -15,7 +15,7 @@
from requests_mock.contrib import fixture
-from keystoneclient import session
+from keystoneauth1 import session
from openstackclient.tests import utils
diff --git a/openstackclient/tests/api/test_image_v1.py b/openstackclient/tests/api/test_image_v1.py
index 34fcfca4..f3479756 100644
--- a/openstackclient/tests/api/test_image_v1.py
+++ b/openstackclient/tests/api/test_image_v1.py
@@ -15,7 +15,7 @@
from requests_mock.contrib import fixture
-from keystoneclient import session
+from keystoneauth1 import session
from openstackclient.api import image_v1
from openstackclient.tests import utils
diff --git a/openstackclient/tests/api/test_image_v2.py b/openstackclient/tests/api/test_image_v2.py
index ddb160ee..77063997 100644
--- a/openstackclient/tests/api/test_image_v2.py
+++ b/openstackclient/tests/api/test_image_v2.py
@@ -15,7 +15,7 @@
from requests_mock.contrib import fixture
-from keystoneclient import session
+from keystoneauth1 import session
from openstackclient.api import image_v2
from openstackclient.tests import utils
diff --git a/openstackclient/tests/api/test_network_v2.py b/openstackclient/tests/api/test_network_v2.py
index 13c5d6ea..80f1d9de 100644
--- a/openstackclient/tests/api/test_network_v2.py
+++ b/openstackclient/tests/api/test_network_v2.py
@@ -15,7 +15,7 @@
from requests_mock.contrib import fixture
-from keystoneclient import session
+from keystoneauth1 import session
from openstackclient.api import network_v2 as network
from openstackclient.tests import utils
diff --git a/openstackclient/tests/api/test_object_store_v1.py b/openstackclient/tests/api/test_object_store_v1.py
index 323bb8e0..992bf261 100644
--- a/openstackclient/tests/api/test_object_store_v1.py
+++ b/openstackclient/tests/api/test_object_store_v1.py
@@ -17,7 +17,7 @@ import mock
from requests_mock.contrib import fixture
-from keystoneclient import session
+from keystoneauth1 import session
from openstackclient.api import object_store_v1 as object_store
from openstackclient.tests import utils
@@ -157,11 +157,6 @@ class TestContainer(TestObjectAPIv1):
'container': 'qaz',
'object_count': '1',
'bytes_used': '577',
- 'meta-owner': FAKE_ACCOUNT,
- 'read_acl': None,
- 'write_acl': None,
- 'sync_to': None,
- 'sync_key': None,
}
self.requests_mock.register_uri(
'HEAD',
@@ -323,10 +318,8 @@ class TestObject(TestObjectAPIv1):
'content-type': 'text/alpha',
'content-length': '577',
'last-modified': '20130101',
- 'meta-owner': FAKE_ACCOUNT,
'etag': 'qaz',
- 'wife': 'Wilma',
- 'x-tra-header': 'yabba-dabba-do',
+ 'properties': {'wife': 'Wilma'},
}
self.requests_mock.register_uri(
'HEAD',
diff --git a/openstackclient/tests/common/test_context.py b/openstackclient/tests/common/test_logs.py
index 55e42851..fe054a3b 100644
--- a/openstackclient/tests/common/test_context.py
+++ b/openstackclient/tests/common/test_logs.py
@@ -14,7 +14,7 @@
import logging
import mock
-from openstackclient.common import context
+from openstackclient.common import logs
from openstackclient.tests import utils
@@ -23,51 +23,51 @@ class TestContext(utils.TestCase):
def test_log_level_from_options(self):
opts = mock.Mock()
opts.verbose_level = 0
- self.assertEqual(logging.ERROR, context.log_level_from_options(opts))
+ self.assertEqual(logging.ERROR, logs.log_level_from_options(opts))
opts.verbose_level = 1
- self.assertEqual(logging.WARNING, context.log_level_from_options(opts))
+ self.assertEqual(logging.WARNING, logs.log_level_from_options(opts))
opts.verbose_level = 2
- self.assertEqual(logging.INFO, context.log_level_from_options(opts))
+ self.assertEqual(logging.INFO, logs.log_level_from_options(opts))
opts.verbose_level = 3
- self.assertEqual(logging.DEBUG, context.log_level_from_options(opts))
+ self.assertEqual(logging.DEBUG, logs.log_level_from_options(opts))
def test_log_level_from_config(self):
cfg = {'verbose_level': 0}
- self.assertEqual(logging.ERROR, context.log_level_from_config(cfg))
+ self.assertEqual(logging.ERROR, logs.log_level_from_config(cfg))
cfg = {'verbose_level': 1}
- self.assertEqual(logging.WARNING, context.log_level_from_config(cfg))
+ self.assertEqual(logging.WARNING, logs.log_level_from_config(cfg))
cfg = {'verbose_level': 2}
- self.assertEqual(logging.INFO, context.log_level_from_config(cfg))
+ self.assertEqual(logging.INFO, logs.log_level_from_config(cfg))
cfg = {'verbose_level': 3}
- self.assertEqual(logging.DEBUG, context.log_level_from_config(cfg))
+ self.assertEqual(logging.DEBUG, logs.log_level_from_config(cfg))
cfg = {'verbose_level': 1, 'log_level': 'critical'}
- self.assertEqual(logging.CRITICAL, context.log_level_from_config(cfg))
+ self.assertEqual(logging.CRITICAL, logs.log_level_from_config(cfg))
cfg = {'verbose_level': 1, 'log_level': 'error'}
- self.assertEqual(logging.ERROR, context.log_level_from_config(cfg))
+ self.assertEqual(logging.ERROR, logs.log_level_from_config(cfg))
cfg = {'verbose_level': 1, 'log_level': 'warning'}
- self.assertEqual(logging.WARNING, context.log_level_from_config(cfg))
+ self.assertEqual(logging.WARNING, logs.log_level_from_config(cfg))
cfg = {'verbose_level': 1, 'log_level': 'info'}
- self.assertEqual(logging.INFO, context.log_level_from_config(cfg))
+ self.assertEqual(logging.INFO, logs.log_level_from_config(cfg))
cfg = {'verbose_level': 1, 'log_level': 'debug'}
- self.assertEqual(logging.DEBUG, context.log_level_from_config(cfg))
+ self.assertEqual(logging.DEBUG, logs.log_level_from_config(cfg))
cfg = {'verbose_level': 1, 'log_level': 'bogus'}
- self.assertEqual(logging.WARNING, context.log_level_from_config(cfg))
+ self.assertEqual(logging.WARNING, logs.log_level_from_config(cfg))
cfg = {'verbose_level': 1, 'log_level': 'info', 'debug': True}
- self.assertEqual(logging.DEBUG, context.log_level_from_config(cfg))
+ self.assertEqual(logging.DEBUG, logs.log_level_from_config(cfg))
@mock.patch('warnings.simplefilter')
def test_set_warning_filter(self, simplefilter):
- context.set_warning_filter(logging.ERROR)
+ logs.set_warning_filter(logging.ERROR)
simplefilter.assert_called_with("ignore")
- context.set_warning_filter(logging.WARNING)
+ logs.set_warning_filter(logging.WARNING)
simplefilter.assert_called_with("ignore")
- context.set_warning_filter(logging.INFO)
+ logs.set_warning_filter(logging.INFO)
simplefilter.assert_called_with("once")
class TestFileFormatter(utils.TestCase):
def test_nothing(self):
- formatter = context._FileFormatter()
+ formatter = logs._FileFormatter()
self.assertEqual(('%(asctime)s.%(msecs)03d %(process)d %(levelname)s '
'%(name)s %(message)s'), formatter.fmt)
@@ -77,7 +77,7 @@ class TestFileFormatter(utils.TestCase):
os_project_name = 'projecty'
username = 'usernamey'
options = Opts()
- formatter = context._FileFormatter(options=options)
+ formatter = logs._FileFormatter(options=options)
self.assertEqual(('%(asctime)s.%(msecs)03d %(process)d %(levelname)s '
'%(name)s [cloudy usernamey projecty] %(message)s'),
formatter.fmt)
@@ -86,7 +86,7 @@ class TestFileFormatter(utils.TestCase):
config = mock.Mock()
config.config = {'cloud': 'cloudy'}
config.auth = {'project_name': 'projecty', 'username': 'usernamey'}
- formatter = context._FileFormatter(config=config)
+ formatter = logs._FileFormatter(config=config)
self.assertEqual(('%(asctime)s.%(msecs)03d %(process)d %(levelname)s '
'%(name)s [cloudy usernamey projecty] %(message)s'),
formatter.fmt)
@@ -119,7 +119,7 @@ class TestLogConfigurator(utils.TestCase):
@mock.patch('logging.StreamHandler')
@mock.patch('logging.getLogger')
- @mock.patch('openstackclient.common.context.set_warning_filter')
+ @mock.patch('openstackclient.common.logs.set_warning_filter')
def test_init(self, warning_filter, getLogger, handle):
getLogger.side_effect = self.loggers
console_logger = mock.Mock()
@@ -127,7 +127,7 @@ class TestLogConfigurator(utils.TestCase):
console_logger.setLevel = mock.Mock()
handle.return_value = console_logger
- configurator = context.LogConfigurator(self.options)
+ configurator = logs.LogConfigurator(self.options)
getLogger.assert_called_with('iso8601') # last call
warning_filter.assert_called_with(logging.WARNING)
@@ -140,12 +140,12 @@ class TestLogConfigurator(utils.TestCase):
self.assertEqual(False, configurator.dump_trace)
@mock.patch('logging.getLogger')
- @mock.patch('openstackclient.common.context.set_warning_filter')
+ @mock.patch('openstackclient.common.logs.set_warning_filter')
def test_init_no_debug(self, warning_filter, getLogger):
getLogger.side_effect = self.loggers
self.options.debug = True
- configurator = context.LogConfigurator(self.options)
+ configurator = logs.LogConfigurator(self.options)
warning_filter.assert_called_with(logging.DEBUG)
self.requests_log.setLevel.assert_called_with(logging.DEBUG)
@@ -153,8 +153,8 @@ class TestLogConfigurator(utils.TestCase):
@mock.patch('logging.FileHandler')
@mock.patch('logging.getLogger')
- @mock.patch('openstackclient.common.context.set_warning_filter')
- @mock.patch('openstackclient.common.context._FileFormatter')
+ @mock.patch('openstackclient.common.logs.set_warning_filter')
+ @mock.patch('openstackclient.common.logs._FileFormatter')
def test_init_log_file(self, formatter, warning_filter, getLogger, handle):
getLogger.side_effect = self.loggers
self.options.log_file = '/tmp/log_file'
@@ -165,7 +165,7 @@ class TestLogConfigurator(utils.TestCase):
mock_formatter = mock.Mock()
formatter.return_value = mock_formatter
- context.LogConfigurator(self.options)
+ logs.LogConfigurator(self.options)
handle.assert_called_with(filename=self.options.log_file)
self.root_logger.addHandler.assert_called_with(file_logger)
@@ -174,11 +174,11 @@ class TestLogConfigurator(utils.TestCase):
@mock.patch('logging.FileHandler')
@mock.patch('logging.getLogger')
- @mock.patch('openstackclient.common.context.set_warning_filter')
- @mock.patch('openstackclient.common.context._FileFormatter')
+ @mock.patch('openstackclient.common.logs.set_warning_filter')
+ @mock.patch('openstackclient.common.logs._FileFormatter')
def test_configure(self, formatter, warning_filter, getLogger, handle):
getLogger.side_effect = self.loggers
- configurator = context.LogConfigurator(self.options)
+ configurator = logs.LogConfigurator(self.options)
cloud_config = mock.Mock()
config_log = '/tmp/config_log'
cloud_config.config = {
diff --git a/openstackclient/tests/common/test_parseractions.py b/openstackclient/tests/common/test_parseractions.py
index 8afcb632..b75c4814 100644
--- a/openstackclient/tests/common/test_parseractions.py
+++ b/openstackclient/tests/common/test_parseractions.py
@@ -102,3 +102,58 @@ class TestKeyValueAction(utils.TestCase):
expect = {'green': '100%'}
self.assertDictEqual(expect, actual)
self.assertEqual(None, failhere)
+
+
+class TestNonNegativeAction(utils.TestCase):
+ def test_negative_values(self):
+ parser = argparse.ArgumentParser()
+
+ # Set up our typical usage
+ parser.add_argument(
+ '--foo',
+ metavar='<foo>',
+ type=int,
+ action=parseractions.NonNegativeAction,
+ )
+
+ self.assertRaises(
+ argparse.ArgumentTypeError,
+ parser.parse_args,
+ "--foo -1".split()
+ )
+
+ def test_zero_values(self):
+ parser = argparse.ArgumentParser()
+
+ # Set up our typical usage
+ parser.add_argument(
+ '--foo',
+ metavar='<foo>',
+ type=int,
+ action=parseractions.NonNegativeAction,
+ )
+
+ results = parser.parse_args(
+ '--foo 0'.split()
+ )
+
+ actual = getattr(results, 'foo', None)
+ self.assertEqual(actual, 0)
+
+ def test_positive_values(self):
+ parser = argparse.ArgumentParser()
+
+ # Set up our typical usage
+ parser.add_argument(
+ '--foo',
+ metavar='<foo>',
+ type=int,
+ action=parseractions.NonNegativeAction,
+ )
+
+ results = parser.parse_args(
+ '--foo 1'.split()
+ )
+
+ actual = getattr(results, 'foo', None)
+ self.assertEqual(actual, 1)
diff --git a/openstackclient/tests/common/test_quota.py b/openstackclient/tests/common/test_quota.py
index b6ad1566..485b8a8b 100644
--- a/openstackclient/tests/common/test_quota.py
+++ b/openstackclient/tests/common/test_quota.py
@@ -17,6 +17,8 @@ import mock
from openstackclient.common import quota
from openstackclient.tests.compute.v2 import fakes as compute_fakes
from openstackclient.tests import fakes
+from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
+from openstackclient.tests.network.v2 import fakes as network_fakes
class FakeQuotaResource(fakes.FakeResource):
@@ -40,11 +42,23 @@ class TestQuota(compute_fakes.TestComputev2):
super(TestQuota, self).setUp()
self.quotas_mock = self.app.client_manager.compute.quotas
self.quotas_mock.reset_mock()
+ self.quotas_class_mock = self.app.client_manager.compute.quota_classes
+ self.quotas_class_mock.reset_mock()
volume_mock = mock.Mock()
volume_mock.quotas = mock.Mock()
self.app.client_manager.volume = volume_mock
self.volume_quotas_mock = volume_mock.quotas
self.volume_quotas_mock.reset_mock()
+ self.volume_quotas_class_mock = \
+ self.app.client_manager.volume.quota_classes
+ self.volume_quotas_class_mock.reset_mock()
+ self.projects_mock = self.app.client_manager.identity.projects
+ self.projects_mock.reset_mock()
+ self.app.client_manager.auth_ref = mock.Mock()
+ self.app.client_manager.auth_ref.service_catalog = mock.Mock()
+ self.service_catalog_mock = \
+ self.app.client_manager.auth_ref.service_catalog
+ self.service_catalog_mock.reset_mock()
class TestQuotaSet(TestQuota):
@@ -76,6 +90,12 @@ class TestQuotaSet(TestQuota):
loaded=True,
)
+ self.projects_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.PROJECT),
+ loaded=True,
+ )
+
self.cmd = quota.SetQuota(self.app, None)
def test_quota_set(self):
@@ -83,15 +103,32 @@ class TestQuotaSet(TestQuota):
'--floating-ips', str(compute_fakes.floating_ip_num),
'--fixed-ips', str(compute_fakes.fix_ip_num),
'--injected-files', str(compute_fakes.injected_file_num),
+ '--injected-file-size', str(compute_fakes.injected_file_size_num),
+ '--injected-path-size', str(compute_fakes.injected_path_size_num),
'--key-pairs', str(compute_fakes.key_pair_num),
- compute_fakes.project_name,
+ '--cores', str(compute_fakes.core_num),
+ '--ram', str(compute_fakes.ram_num),
+ '--instances', str(compute_fakes.instance_num),
+ '--properties', str(compute_fakes.property_num),
+ '--secgroup-rules', str(compute_fakes.secgroup_rule_num),
+ '--secgroups', str(compute_fakes.secgroup_num),
+ identity_fakes.project_name,
]
verifylist = [
('floating_ips', compute_fakes.floating_ip_num),
('fixed_ips', compute_fakes.fix_ip_num),
('injected_files', compute_fakes.injected_file_num),
+ ('injected_file_content_bytes',
+ compute_fakes.injected_file_size_num),
+ ('injected_file_path_bytes', compute_fakes.injected_path_size_num),
('key_pairs', compute_fakes.key_pair_num),
- ('project', compute_fakes.project_name),
+ ('cores', compute_fakes.core_num),
+ ('ram', compute_fakes.ram_num),
+ ('instances', compute_fakes.instance_num),
+ ('metadata_items', compute_fakes.property_num),
+ ('security_group_rules', compute_fakes.secgroup_rule_num),
+ ('security_groups', compute_fakes.secgroup_num),
+ ('project', identity_fakes.project_name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -102,17 +139,29 @@ class TestQuotaSet(TestQuota):
'floating_ips': compute_fakes.floating_ip_num,
'fixed_ips': compute_fakes.fix_ip_num,
'injected_files': compute_fakes.injected_file_num,
+ 'injected_file_content_bytes':
+ compute_fakes.injected_file_size_num,
+ 'injected_file_path_bytes': compute_fakes.injected_path_size_num,
'key_pairs': compute_fakes.key_pair_num,
+ 'cores': compute_fakes.core_num,
+ 'ram': compute_fakes.ram_num,
+ 'instances': compute_fakes.instance_num,
+ 'metadata_items': compute_fakes.property_num,
+ 'security_group_rules': compute_fakes.secgroup_rule_num,
+ 'security_groups': compute_fakes.secgroup_num,
}
- self.quotas_mock.update.assert_called_with('project_test', **kwargs)
+ self.quotas_mock.update.assert_called_with(
+ identity_fakes.project_id,
+ **kwargs
+ )
def test_quota_set_volume(self):
arglist = [
'--gigabytes', str(compute_fakes.floating_ip_num),
'--snapshots', str(compute_fakes.fix_ip_num),
'--volumes', str(compute_fakes.injected_file_num),
- compute_fakes.project_name,
+ identity_fakes.project_name,
]
verifylist = [
('gigabytes', compute_fakes.floating_ip_num),
@@ -130,5 +179,117 @@ class TestQuotaSet(TestQuota):
'volumes': compute_fakes.injected_file_num,
}
- self.volume_quotas_mock.update.assert_called_with('project_test',
- **kwargs)
+ self.volume_quotas_mock.update.assert_called_with(
+ identity_fakes.project_id,
+ **kwargs
+ )
+
+
+class TestQuotaShow(TestQuota):
+
+ def setUp(self):
+ super(TestQuotaShow, self).setUp()
+
+ self.quotas_mock.get.return_value = FakeQuotaResource(
+ None,
+ copy.deepcopy(compute_fakes.QUOTA),
+ loaded=True,
+ )
+
+ self.quotas_mock.defaults.return_value = FakeQuotaResource(
+ None,
+ copy.deepcopy(compute_fakes.QUOTA),
+ loaded=True,
+ )
+
+ self.volume_quotas_mock.get.return_value = FakeQuotaResource(
+ None,
+ copy.deepcopy(compute_fakes.QUOTA),
+ loaded=True,
+ )
+
+ self.volume_quotas_mock.defaults.return_value = FakeQuotaResource(
+ None,
+ copy.deepcopy(compute_fakes.QUOTA),
+ loaded=True,
+ )
+
+ self.service_catalog_mock.get_endpoints.return_value = [
+ fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.ENDPOINT),
+ loaded=True,
+ )
+ ]
+
+ self.quotas_class_mock.get.return_value = FakeQuotaResource(
+ None,
+ copy.deepcopy(compute_fakes.QUOTA),
+ loaded=True,
+ )
+
+ self.volume_quotas_class_mock.get.return_value = FakeQuotaResource(
+ None,
+ copy.deepcopy(compute_fakes.QUOTA),
+ loaded=True,
+ )
+
+ self.projects_mock.get.return_value = fakes.FakeResource(
+ None,
+ copy.deepcopy(identity_fakes.PROJECT),
+ loaded=True,
+ )
+
+ self.app.client_manager.network = network_fakes.FakeNetworkV2Client(
+ endpoint=fakes.AUTH_URL,
+ token=fakes.AUTH_TOKEN,
+ )
+
+ self.cmd = quota.ShowQuota(self.app, None)
+
+ def test_quota_show(self):
+ arglist = [
+ identity_fakes.project_name,
+ ]
+ verifylist = [
+ ('project', identity_fakes.project_name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ self.quotas_mock.get.assert_called_with(identity_fakes.project_id)
+
+ def test_quota_show_with_default(self):
+ arglist = [
+ '--default',
+ identity_fakes.project_name,
+ ]
+ verifylist = [
+ ('default', True),
+ ('project', identity_fakes.project_name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ self.quotas_mock.defaults.assert_called_with(identity_fakes.project_id)
+
+ def test_quota_show_with_class(self):
+ arglist = [
+ '--class',
+ identity_fakes.project_name,
+ ]
+ verifylist = [
+ ('quota_class', True),
+ ('project', identity_fakes.project_name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ self.quotas_class_mock.get.assert_called_with(
+ identity_fakes.project_id)
diff --git a/openstackclient/tests/common/test_utils.py b/openstackclient/tests/common/test_utils.py
index 373c0de4..b564ffab 100644
--- a/openstackclient/tests/common/test_utils.py
+++ b/openstackclient/tests/common/test_utils.py
@@ -137,6 +137,46 @@ class TestUtils(test_utils.TestCase):
items, sort_str)
@mock.patch.object(time, 'sleep')
+ def test_wait_for_status_ok(self, mock_sleep):
+ # Tests the normal flow that the resource is status=active
+ resource = mock.MagicMock(status='ACTIVE')
+ status_f = mock.Mock(return_value=resource)
+ res_id = str(uuid.uuid4())
+ self.assertTrue(utils.wait_for_status(status_f, res_id,))
+ self.assertFalse(mock_sleep.called)
+
+ @mock.patch.object(time, 'sleep')
+ def test_wait_for_status_ok__with_overrides(self, mock_sleep):
+ # Tests the normal flow that the resource is status=complete
+ resource = mock.MagicMock(my_status='COMPLETE')
+ status_f = mock.Mock(return_value=resource)
+ res_id = str(uuid.uuid4())
+ self.assertTrue(utils.wait_for_status(status_f, res_id,
+ status_field='my_status',
+ success_status=['complete']))
+ self.assertFalse(mock_sleep.called)
+
+ @mock.patch.object(time, 'sleep')
+ def test_wait_for_status_error(self, mock_sleep):
+ # Tests that we fail if the resource is status=error
+ resource = mock.MagicMock(status='ERROR')
+ status_f = mock.Mock(return_value=resource)
+ res_id = str(uuid.uuid4())
+ self.assertFalse(utils.wait_for_status(status_f, res_id))
+ self.assertFalse(mock_sleep.called)
+
+ @mock.patch.object(time, 'sleep')
+ def test_wait_for_status_error_with_overrides(self, mock_sleep):
+ # Tests that we fail if the resource is my_status=failed
+ resource = mock.MagicMock(my_status='FAILED')
+ status_f = mock.Mock(return_value=resource)
+ res_id = str(uuid.uuid4())
+ self.assertFalse(utils.wait_for_status(status_f, res_id,
+ status_field='my_status',
+ error_status=['failed']))
+ self.assertFalse(mock_sleep.called)
+
+ @mock.patch.object(time, 'sleep')
def test_wait_for_delete_ok(self, mock_sleep):
# Tests the normal flow that the resource is deleted with a 404 coming
# back on the 2nd iteration of the wait loop.
diff --git a/openstackclient/tests/compute/v2/fakes.py b/openstackclient/tests/compute/v2/fakes.py
index e798bd40..91cc2bd2 100644
--- a/openstackclient/tests/compute/v2/fakes.py
+++ b/openstackclient/tests/compute/v2/fakes.py
@@ -13,24 +13,18 @@
# under the License.
#
+import copy
import mock
+import uuid
from openstackclient.tests import fakes
from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
from openstackclient.tests.image.v2 import fakes as image_fakes
from openstackclient.tests.network.v2 import fakes as network_fakes
from openstackclient.tests import utils
+from openstackclient.tests.volume.v2 import fakes as volume_fakes
-server_id = 'serv1'
-server_name = 'waiter'
-
-SERVER = {
- 'id': server_id,
- 'name': server_name,
- 'metadata': {},
-}
-
extension_name = 'Multinic'
extension_namespace = 'http://docs.openstack.org/compute/ext/'\
'multinic/api/v1.1'
@@ -50,34 +44,49 @@ EXTENSION = {
'links': extension_links,
}
-flavor_id = 'm1.large'
-flavor_name = 'Large'
-flavor_ram = 8192
-flavor_vcpus = 4
-
-FLAVOR = {
- 'id': flavor_id,
- 'name': flavor_name,
- 'ram': flavor_ram,
- 'vcpus': flavor_vcpus,
-}
-
floating_ip_num = 100
fix_ip_num = 100
injected_file_num = 100
+injected_file_size_num = 10240
+injected_path_size_num = 255
key_pair_num = 100
+core_num = 20
+ram_num = 51200
+instance_num = 10
+property_num = 128
+secgroup_rule_num = 20
+secgroup_num = 10
project_name = 'project_test'
QUOTA = {
'project': project_name,
'floating-ips': floating_ip_num,
'fix-ips': fix_ip_num,
'injected-files': injected_file_num,
+ 'injected-file-size': injected_file_size_num,
+ 'injected-path-size': injected_path_size_num,
'key-pairs': key_pair_num,
+ 'cores': core_num,
+ 'ram': ram_num,
+ 'instances': instance_num,
+ 'properties': property_num,
+ 'secgroup_rules': secgroup_rule_num,
+ 'secgroups': secgroup_num,
}
QUOTA_columns = tuple(sorted(QUOTA))
QUOTA_data = tuple(QUOTA[x] for x in sorted(QUOTA))
+block_device_mapping = 'vda=' + volume_fakes.volume_name + ':::0'
+
+service_host = 'host_test'
+service_binary = 'compute_test'
+service_status = 'enabled'
+SERVICE = {
+ 'host': service_host,
+ 'binary': service_binary,
+ 'status': service_status,
+}
+
class FakeComputev2Client(object):
def __init__(self, **kwargs):
@@ -85,12 +94,18 @@ class FakeComputev2Client(object):
self.images.resource_class = fakes.FakeResource(None, {})
self.servers = mock.Mock()
self.servers.resource_class = fakes.FakeResource(None, {})
+ self.services = mock.Mock()
+ self.services.resource_class = fakes.FakeResource(None, {})
self.extensions = mock.Mock()
self.extensions.resource_class = fakes.FakeResource(None, {})
self.flavors = mock.Mock()
self.flavors.resource_class = fakes.FakeResource(None, {})
self.quotas = mock.Mock()
self.quotas.resource_class = fakes.FakeResource(None, {})
+ self.quota_classes = mock.Mock()
+ self.quota_classes.resource_class = fakes.FakeResource(None, {})
+ self.volumes = mock.Mock()
+ self.volumes.resource_class = fakes.FakeResource(None, {})
self.auth_token = kwargs['token']
self.management_url = kwargs['endpoint']
@@ -118,3 +133,161 @@ class TestComputev2(utils.TestCommand):
endpoint=fakes.AUTH_URL,
token=fakes.AUTH_TOKEN,
)
+
+ self.app.client_manager.volume = volume_fakes.FakeVolumeClient(
+ endpoint=fakes.AUTH_URL,
+ token=fakes.AUTH_TOKEN,
+ )
+
+
+class FakeServer(object):
+ """Fake one or more compute servers."""
+
+ @staticmethod
+ def create_one_server(attrs={}, methods={}):
+ """Create a fake server.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param Dictionary methods:
+ A dictionary with all methods
+ :return:
+ A FakeResource object, with id, name, metadata
+ """
+ # Set default attributes.
+ server_info = {
+ 'id': 'server-id-' + uuid.uuid4().hex,
+ 'name': 'server-name-' + uuid.uuid4().hex,
+ 'metadata': {},
+ }
+
+ # Overwrite default attributes.
+ server_info.update(attrs)
+
+ server = fakes.FakeResource(info=copy.deepcopy(server_info),
+ methods=methods,
+ loaded=True)
+ return server
+
+ @staticmethod
+ def create_servers(attrs={}, methods={}, count=2):
+ """Create multiple fake servers.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param Dictionary methods:
+ A dictionary with all methods
+ :param int count:
+ The number of servers to fake
+ :return:
+ A list of FakeResource objects faking the servers
+ """
+ servers = []
+ for i in range(0, count):
+ servers.append(FakeServer.create_one_server(attrs, methods))
+
+ return servers
+
+ @staticmethod
+ def get_servers(servers=None, count=2):
+ """Get an iterable MagicMock object with a list of faked servers.
+
+ If servers list is provided, then initialize the Mock object with the
+ list. Otherwise create one.
+
+ :param List servers:
+ A list of FakeResource objects faking servers
+ :param int count:
+ The number of servers to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ servers
+ """
+ if servers is None:
+ servers = FakeServer.create_servers(count)
+ return mock.MagicMock(side_effect=servers)
+
+
+class FakeFlavorResource(fakes.FakeResource):
+ """Fake flavor object's methods to help test.
+
+ The flavor object has three methods to get, set, unset its properties.
+ Need to fake them, otherwise the functions to be tested won't run properly.
+ """
+
+ # Fake properties.
+ _keys = {'property': 'value'}
+
+ def set_keys(self, args):
+ self._keys.update(args)
+
+ def unset_keys(self, keys):
+ for key in keys:
+ self._keys.pop(key, None)
+
+ def get_keys(self):
+ return self._keys
+
+
+class FakeFlavor(object):
+ """Fake one or more flavors."""
+
+ @staticmethod
+ def create_one_flavor(attrs={}):
+ """Create a fake flavor.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeFlavorResource object, with id, name, ram, vcpus, properties
+ """
+ # Set default attributes.
+ flavor_info = {
+ 'id': 'flavor-id-' + uuid.uuid4().hex,
+ 'name': 'flavor-name-' + uuid.uuid4().hex,
+ 'ram': 8192,
+ 'vcpus': 4,
+ }
+
+ # Overwrite default attributes.
+ flavor_info.update(attrs)
+
+ flavor = FakeFlavorResource(info=copy.deepcopy(flavor_info),
+ loaded=True)
+ return flavor
+
+ @staticmethod
+ def create_flavors(attrs={}, count=2):
+ """Create multiple fake flavors.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of flavors to fake
+ :return:
+ A list of FakeFlavorResource objects faking the flavors
+ """
+ flavors = []
+ for i in range(0, count):
+ flavors.append(FakeFlavor.create_one_flavor(attrs))
+
+ return flavors
+
+ @staticmethod
+ def get_flavors(flavors=None, count=2):
+ """Get an iterable MagicMock object with a list of faked flavors.
+
+ If flavors list is provided, then initialize the Mock object with the
+ list. Otherwise create one.
+
+ :param List flavors:
+ A list of FakeFlavorResource objects faking flavors
+ :param int count:
+ The number of flavors to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ flavors
+ """
+ if flavors is None:
+ flavors = FakeServer.create_flavors(count)
+ return mock.MagicMock(side_effect=flavors)
diff --git a/openstackclient/tests/compute/v2/test_flavor.py b/openstackclient/tests/compute/v2/test_flavor.py
index 19be8124..60356efa 100644
--- a/openstackclient/tests/compute/v2/test_flavor.py
+++ b/openstackclient/tests/compute/v2/test_flavor.py
@@ -13,26 +13,9 @@
# under the License.
#
-import copy
-
+from openstackclient.common import exceptions
from openstackclient.compute.v2 import flavor
from openstackclient.tests.compute.v2 import fakes as compute_fakes
-from openstackclient.tests import fakes
-
-
-class FakeFlavorResource(fakes.FakeResource):
-
- _keys = {'property': 'value'}
-
- def set_keys(self, args):
- self._keys.update(args)
-
- def unset_keys(self, keys):
- for key in keys:
- self._keys.pop(key, None)
-
- def get_keys(self):
- return self._keys
class TestFlavor(compute_fakes.TestComputev2):
@@ -45,18 +28,90 @@ class TestFlavor(compute_fakes.TestComputev2):
self.flavors_mock.reset_mock()
+class TestFlavorDelete(TestFlavor):
+
+ flavor = compute_fakes.FakeFlavor.create_one_flavor()
+
+ def setUp(self):
+ super(TestFlavorDelete, self).setUp()
+
+ self.flavors_mock.get.return_value = self.flavor
+ self.flavors_mock.delete.return_value = None
+
+ self.cmd = flavor.DeleteFlavor(self.app, None)
+
+ def test_flavor_delete(self):
+ arglist = [
+ self.flavor.id
+ ]
+ verifylist = [
+ ('flavor', self.flavor.id),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ self.flavors_mock.delete.assert_called_with(self.flavor.id)
+
+ def test_flavor_delete_with_unexist_flavor(self):
+ self.flavors_mock.get.side_effect = exceptions.NotFound(None)
+ self.flavors_mock.find.side_effect = exceptions.NotFound(None)
+
+ arglist = [
+ 'unexist_flavor'
+ ]
+ verifylist = [
+ ('flavor', 'unexist_flavor'),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.assertRaises(
+ exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+
+
class TestFlavorList(TestFlavor):
+ # Return value of self.flavors_mock.list().
+ flavors = compute_fakes.FakeFlavor.create_flavors(count=1)
+
+ columns = (
+ 'ID',
+ 'Name',
+ 'RAM',
+ 'Disk',
+ 'Ephemeral',
+ 'VCPUs',
+ 'Is Public',
+ )
+ columns_long = columns + (
+ 'Swap',
+ 'RXTX Factor',
+ 'Properties'
+ )
+
+ data = ((
+ flavors[0].id,
+ flavors[0].name,
+ flavors[0].ram,
+ '',
+ '',
+ flavors[0].vcpus,
+ ''
+ ), )
+ data_long = (data[0] + (
+ '',
+ '',
+ 'property=\'value\''
+ ), )
+
def setUp(self):
super(TestFlavorList, self).setUp()
- self.flavors_mock.list.return_value = [
- FakeFlavorResource(
- None,
- copy.deepcopy(compute_fakes.FLAVOR),
- loaded=True,
- ),
- ]
+ self.flavors_mock.list.return_value = self.flavors
# Get the command object to test
self.cmd = flavor.ListFlavor(self.app, None)
@@ -76,33 +131,17 @@ class TestFlavorList(TestFlavor):
# Set expected values
kwargs = {
- 'is_public': True
+ 'is_public': True,
+ 'limit': None,
+ 'marker': None
}
self.flavors_mock.list.assert_called_with(
**kwargs
)
- collist = (
- 'ID',
- 'Name',
- 'RAM',
- 'Disk',
- 'Ephemeral',
- 'VCPUs',
- 'Is Public',
- )
- self.assertEqual(collist, columns)
- datalist = ((
- compute_fakes.flavor_id,
- compute_fakes.flavor_name,
- compute_fakes.flavor_ram,
- '',
- '',
- compute_fakes.flavor_vcpus,
- ''
- ), )
- self.assertEqual(datalist, tuple(data))
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
def test_flavor_list_all_flavors(self):
arglist = [
@@ -119,33 +158,17 @@ class TestFlavorList(TestFlavor):
# Set expected values
kwargs = {
- 'is_public': None
+ 'is_public': None,
+ 'limit': None,
+ 'marker': None
}
self.flavors_mock.list.assert_called_with(
**kwargs
)
- collist = (
- 'ID',
- 'Name',
- 'RAM',
- 'Disk',
- 'Ephemeral',
- 'VCPUs',
- 'Is Public',
- )
- self.assertEqual(collist, columns)
- datalist = ((
- compute_fakes.flavor_id,
- compute_fakes.flavor_name,
- compute_fakes.flavor_ram,
- '',
- '',
- compute_fakes.flavor_vcpus,
- ''
- ), )
- self.assertEqual(datalist, tuple(data))
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
def test_flavor_list_private_flavors(self):
arglist = [
@@ -162,33 +185,17 @@ class TestFlavorList(TestFlavor):
# Set expected values
kwargs = {
- 'is_public': False
+ 'is_public': False,
+ 'limit': None,
+ 'marker': None
}
self.flavors_mock.list.assert_called_with(
**kwargs
)
- collist = (
- 'ID',
- 'Name',
- 'RAM',
- 'Disk',
- 'Ephemeral',
- 'VCPUs',
- 'Is Public',
- )
- self.assertEqual(collist, columns)
- datalist = ((
- compute_fakes.flavor_id,
- compute_fakes.flavor_name,
- compute_fakes.flavor_ram,
- '',
- '',
- compute_fakes.flavor_vcpus,
- ''
- ), )
- self.assertEqual(datalist, tuple(data))
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
def test_flavor_list_public_flavors(self):
arglist = [
@@ -205,33 +212,17 @@ class TestFlavorList(TestFlavor):
# Set expected values
kwargs = {
- 'is_public': True
+ 'is_public': True,
+ 'limit': None,
+ 'marker': None
}
self.flavors_mock.list.assert_called_with(
**kwargs
)
- collist = (
- 'ID',
- 'Name',
- 'RAM',
- 'Disk',
- 'Ephemeral',
- 'VCPUs',
- 'Is Public',
- )
- self.assertEqual(collist, columns)
- datalist = ((
- compute_fakes.flavor_id,
- compute_fakes.flavor_name,
- compute_fakes.flavor_ram,
- '',
- '',
- compute_fakes.flavor_vcpus,
- ''
- ), )
- self.assertEqual(datalist, tuple(data))
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
def test_flavor_list_long(self):
arglist = [
@@ -248,51 +239,28 @@ class TestFlavorList(TestFlavor):
# Set expected values
kwargs = {
- 'is_public': True
+ 'is_public': True,
+ 'limit': None,
+ 'marker': None
}
self.flavors_mock.list.assert_called_with(
**kwargs
)
- collist = (
- 'ID',
- 'Name',
- 'RAM',
- 'Disk',
- 'Ephemeral',
- 'VCPUs',
- 'Is Public',
- 'Swap',
- 'RXTX Factor',
- 'Properties'
- )
- self.assertEqual(collist, columns)
- datalist = ((
- compute_fakes.flavor_id,
- compute_fakes.flavor_name,
- compute_fakes.flavor_ram,
- '',
- '',
- compute_fakes.flavor_vcpus,
- '',
- '',
- '',
- 'property=\'value\''
- ), )
- self.assertEqual(datalist, tuple(data))
+ self.assertEqual(self.columns_long, columns)
+ self.assertEqual(tuple(self.data_long), tuple(data))
class TestFlavorSet(TestFlavor):
+ # Return value of self.flavors_mock.find().
+ flavor = compute_fakes.FakeFlavor.create_one_flavor()
+
def setUp(self):
super(TestFlavorSet, self).setUp()
- self.flavors_mock.find.return_value = FakeFlavorResource(
- None,
- copy.deepcopy(compute_fakes.FLAVOR),
- loaded=True,
- )
+ self.flavors_mock.find.return_value = self.flavor
self.cmd = flavor.SetFlavor(self.app, None)
@@ -318,14 +286,13 @@ class TestFlavorSet(TestFlavor):
class TestFlavorUnset(TestFlavor):
+ # Return value of self.flavors_mock.find().
+ flavor = compute_fakes.FakeFlavor.create_one_flavor()
+
def setUp(self):
super(TestFlavorUnset, self).setUp()
- self.flavors_mock.find.return_value = FakeFlavorResource(
- None,
- copy.deepcopy(compute_fakes.FLAVOR),
- loaded=True,
- )
+ self.flavors_mock.find.return_value = self.flavor
self.cmd = flavor.UnsetFlavor(self.app, None)
diff --git a/openstackclient/tests/compute/v2/test_server.py b/openstackclient/tests/compute/v2/test_server.py
index 1e99bcd0..359920f2 100644
--- a/openstackclient/tests/compute/v2/test_server.py
+++ b/openstackclient/tests/compute/v2/test_server.py
@@ -17,6 +17,7 @@ import copy
import mock
import testtools
+from mock import call
from openstackclient.common import exceptions
from openstackclient.common import utils as common_utils
from openstackclient.compute.v2 import server
@@ -24,6 +25,7 @@ from openstackclient.tests.compute.v2 import fakes as compute_fakes
from openstackclient.tests import fakes
from openstackclient.tests.image.v2 import fakes as image_fakes
from openstackclient.tests import utils
+from openstackclient.tests.volume.v2 import fakes as volume_fakes
class TestServer(compute_fakes.TestComputev2):
@@ -47,24 +49,63 @@ class TestServer(compute_fakes.TestComputev2):
self.images_mock = self.app.client_manager.image.images
self.images_mock.reset_mock()
+ # Get a shortcut to the VolumeManager Mock
+ self.volumes_mock = self.app.client_manager.volume.volumes
+ self.volumes_mock.reset_mock()
+
+ # Set object attributes to be tested. Could be overwriten in subclass.
+ self.attrs = {}
+
+ # Set object methods to be tested. Could be overwriten in subclass.
+ self.methods = {}
+
+ def setup_servers_mock(self, count):
+ servers = compute_fakes.FakeServer.create_servers(attrs=self.attrs,
+ methods=self.methods,
+ count=count)
+
+ # This is the return value for utils.find_resource()
+ self.servers_mock.get = compute_fakes.FakeServer.get_servers(servers,
+ 0)
+ return servers
+
+ def run_method_with_servers(self, method_name, server_count):
+ servers = self.setup_servers_mock(server_count)
+
+ arglist = []
+ verifylist = []
+
+ for s in servers:
+ arglist.append(s.id)
+ verifylist = [
+ ('server', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ self.cmd.take_action(parsed_args)
+
+ for s in servers:
+ method = getattr(s, method_name)
+ method.assert_called_with()
+
class TestServerCreate(TestServer):
def setUp(self):
super(TestServerCreate, self).setUp()
- self.servers_mock.create.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(compute_fakes.SERVER),
- loaded=True,
- )
- new_server = fakes.FakeResource(
- None,
- copy.deepcopy(compute_fakes.SERVER),
- loaded=True,
- )
- new_server.__dict__['networks'] = {}
- self.servers_mock.get.return_value = new_server
+ attrs = {
+ 'networks': {},
+ }
+ self.new_server = compute_fakes.FakeServer.create_one_server(
+ attrs=attrs)
+
+ # This is the return value for utils.find_resource().
+ # This is for testing --wait option.
+ self.servers_mock.get.return_value = self.new_server
+
+ self.servers_mock.create.return_value = self.new_server
self.image = fakes.FakeResource(
None,
@@ -73,22 +114,25 @@ class TestServerCreate(TestServer):
)
self.cimages_mock.get.return_value = self.image
- self.flavor = fakes.FakeResource(
+ self.flavor = compute_fakes.FakeFlavor.create_one_flavor()
+ self.flavors_mock.get.return_value = self.flavor
+
+ self.volume = fakes.FakeResource(
None,
- copy.deepcopy(compute_fakes.FLAVOR),
+ copy.deepcopy(volume_fakes.VOLUME),
loaded=True,
)
- self.flavors_mock.get.return_value = self.flavor
+ self.volumes_mock.get.return_value = self.volume
# Get the command object to test
self.cmd = server.CreateServer(self.app, None)
def test_server_create_no_options(self):
arglist = [
- compute_fakes.server_id,
+ self.new_server.name,
]
verifylist = [
- ('server_name', compute_fakes.server_id),
+ ('server_name', self.new_server.name),
]
try:
# Missing required args should bail here
@@ -100,13 +144,13 @@ class TestServerCreate(TestServer):
arglist = [
'--image', 'image1',
'--flavor', 'flavor1',
- compute_fakes.server_id,
+ self.new_server.name,
]
verifylist = [
('image', 'image1'),
('flavor', 'flavor1'),
('config_drive', False),
- ('server_name', compute_fakes.server_id),
+ ('server_name', self.new_server.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -131,19 +175,27 @@ class TestServerCreate(TestServer):
)
# ServerManager.create(name, image, flavor, **kwargs)
self.servers_mock.create.assert_called_with(
- compute_fakes.server_id,
+ self.new_server.name,
self.image,
self.flavor,
**kwargs
)
- collist = ('addresses', 'flavor', 'id', 'name', 'properties')
+ collist = (
+ 'addresses',
+ 'flavor',
+ 'id',
+ 'name',
+ 'networks',
+ 'properties',
+ )
self.assertEqual(collist, columns)
datalist = (
'',
- 'Large ()',
- compute_fakes.server_id,
- compute_fakes.server_name,
+ self.flavor.name + ' ()',
+ self.new_server.id,
+ self.new_server.name,
+ self.new_server.networks,
'',
)
self.assertEqual(datalist, data)
@@ -154,14 +206,14 @@ class TestServerCreate(TestServer):
'--flavor', 'flavor1',
'--nic', 'net-id=net1',
'--nic', 'port-id=port1',
- compute_fakes.server_id,
+ self.new_server.name,
]
verifylist = [
('image', 'image1'),
('flavor', 'flavor1'),
('nic', ['net-id=net1', 'port-id=port1']),
('config_drive', False),
- ('server_name', compute_fakes.server_id),
+ ('server_name', self.new_server.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -207,19 +259,27 @@ class TestServerCreate(TestServer):
)
# ServerManager.create(name, image, flavor, **kwargs)
self.servers_mock.create.assert_called_with(
- compute_fakes.server_id,
+ self.new_server.name,
self.image,
self.flavor,
**kwargs
)
- collist = ('addresses', 'flavor', 'id', 'name', 'properties')
+ collist = (
+ 'addresses',
+ 'flavor',
+ 'id',
+ 'name',
+ 'networks',
+ 'properties',
+ )
self.assertEqual(collist, columns)
datalist = (
'',
- 'Large ()',
- compute_fakes.server_id,
- compute_fakes.server_name,
+ self.flavor.name + ' ()',
+ self.new_server.id,
+ self.new_server.name,
+ self.new_server.networks,
'',
)
self.assertEqual(datalist, data)
@@ -234,14 +294,14 @@ class TestServerCreate(TestServer):
'--image', 'image1',
'--flavor', 'flavor1',
'--user-data', 'userdata.sh',
- compute_fakes.server_id,
+ self.new_server.name,
]
verifylist = [
('image', 'image1'),
('flavor', 'flavor1'),
('user_data', 'userdata.sh'),
('config_drive', False),
- ('server_name', compute_fakes.server_id),
+ ('server_name', self.new_server.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -272,19 +332,96 @@ class TestServerCreate(TestServer):
)
# ServerManager.create(name, image, flavor, **kwargs)
self.servers_mock.create.assert_called_with(
- compute_fakes.server_id,
+ self.new_server.name,
self.image,
self.flavor,
**kwargs
)
- collist = ('addresses', 'flavor', 'id', 'name', 'properties')
+ collist = (
+ 'addresses',
+ 'flavor',
+ 'id',
+ 'name',
+ 'networks',
+ 'properties',
+ )
self.assertEqual(collist, columns)
datalist = (
'',
- 'Large ()',
- compute_fakes.server_id,
- compute_fakes.server_name,
+ self.flavor.name + ' ()',
+ self.new_server.id,
+ self.new_server.name,
+ self.new_server.networks,
+ '',
+ )
+ self.assertEqual(datalist, data)
+
+ def test_server_create_with_block_device_mapping(self):
+ arglist = [
+ '--image', 'image1',
+ '--flavor', self.flavor.id,
+ '--block-device-mapping', compute_fakes.block_device_mapping,
+ self.new_server.name,
+ ]
+ verifylist = [
+ ('image', 'image1'),
+ ('flavor', self.flavor.id),
+ ('block_device_mapping', [compute_fakes.block_device_mapping]),
+ ('config_drive', False),
+ ('server_name', self.new_server.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # CreateServer.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ real_volume_mapping = (
+ (compute_fakes.block_device_mapping.split('=', 1)[1]).replace(
+ volume_fakes.volume_name,
+ volume_fakes.volume_id))
+
+ # Set expected values
+ kwargs = dict(
+ meta=None,
+ files={},
+ reservation_id=None,
+ min_count=1,
+ max_count=1,
+ security_groups=[],
+ userdata=None,
+ key_name=None,
+ availability_zone=None,
+ block_device_mapping={
+ 'vda': real_volume_mapping
+ },
+ nics=[],
+ scheduler_hints={},
+ config_drive=None,
+ )
+ # ServerManager.create(name, image, flavor, **kwargs)
+ self.servers_mock.create.assert_called_with(
+ self.new_server.name,
+ self.image,
+ self.flavor,
+ **kwargs
+ )
+
+ collist = (
+ 'addresses',
+ 'flavor',
+ 'id',
+ 'name',
+ 'networks',
+ 'properties',
+ )
+ self.assertEqual(collist, columns)
+ datalist = (
+ '',
+ self.flavor.name + ' ()',
+ self.new_server.id,
+ self.new_server.name,
+ self.new_server.networks,
'',
)
self.assertEqual(datalist, data)
@@ -295,23 +432,19 @@ class TestServerDelete(TestServer):
def setUp(self):
super(TestServerDelete, self).setUp()
- # This is the return value for utils.find_resource()
- self.servers_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(compute_fakes.SERVER),
- loaded=True,
- )
self.servers_mock.delete.return_value = None
# Get the command object to test
self.cmd = server.DeleteServer(self.app, None)
def test_server_delete_no_options(self):
+ servers = self.setup_servers_mock(count=1)
+
arglist = [
- compute_fakes.server_id,
+ servers[0].id,
]
verifylist = [
- ('servers', [compute_fakes.server_id]),
+ ('server', [servers[0].id]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -319,16 +452,39 @@ class TestServerDelete(TestServer):
self.cmd.take_action(parsed_args)
self.servers_mock.delete.assert_called_with(
- compute_fakes.server_id,
+ servers[0].id,
)
+ def test_server_delete_multi_servers(self):
+ servers = self.setup_servers_mock(count=3)
+
+ arglist = []
+ verifylist = []
+
+ for s in servers:
+ arglist.append(s.id)
+ verifylist = [
+ ('server', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ self.cmd.take_action(parsed_args)
+
+ calls = []
+ for s in servers:
+ calls.append(call(s.id))
+ self.servers_mock.delete.assert_has_calls(calls)
+
@mock.patch.object(common_utils, 'wait_for_delete', return_value=True)
def test_server_delete_wait_ok(self, mock_wait_for_delete):
+ servers = self.setup_servers_mock(count=1)
+
arglist = [
- compute_fakes.server_id, '--wait'
+ servers[0].id, '--wait'
]
verifylist = [
- ('servers', [compute_fakes.server_id]),
+ ('server', [servers[0].id]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -336,22 +492,24 @@ class TestServerDelete(TestServer):
self.cmd.take_action(parsed_args)
self.servers_mock.delete.assert_called_with(
- compute_fakes.server_id,
+ servers[0].id,
)
mock_wait_for_delete.assert_called_once_with(
self.servers_mock,
- compute_fakes.server_id,
+ servers[0].id,
callback=server._show_progress
)
@mock.patch.object(common_utils, 'wait_for_delete', return_value=False)
def test_server_delete_wait_fails(self, mock_wait_for_delete):
+ servers = self.setup_servers_mock(count=1)
+
arglist = [
- compute_fakes.server_id, '--wait'
+ servers[0].id, '--wait'
]
verifylist = [
- ('servers', [compute_fakes.server_id]),
+ ('server', [servers[0].id]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -359,12 +517,12 @@ class TestServerDelete(TestServer):
self.assertRaises(SystemExit, self.cmd.take_action, parsed_args)
self.servers_mock.delete.assert_called_with(
- compute_fakes.server_id,
+ servers[0].id,
)
mock_wait_for_delete.assert_called_once_with(
self.servers_mock,
- compute_fakes.server_id,
+ servers[0].id,
callback=server._show_progress
)
@@ -374,12 +532,10 @@ class TestServerImageCreate(TestServer):
def setUp(self):
super(TestServerImageCreate, self).setUp()
+ self.server = compute_fakes.FakeServer.create_one_server()
+
# This is the return value for utils.find_resource()
- self.servers_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(compute_fakes.SERVER),
- loaded=True,
- )
+ self.servers_mock.get.return_value = self.server
self.servers_mock.create_image.return_value = image_fakes.image_id
@@ -394,10 +550,10 @@ class TestServerImageCreate(TestServer):
def test_server_image_create_no_options(self):
arglist = [
- compute_fakes.server_id,
+ self.server.id,
]
verifylist = [
- ('server', compute_fakes.server_id),
+ ('server', self.server.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -407,7 +563,7 @@ class TestServerImageCreate(TestServer):
# ServerManager.create_image(server, image_name, metadata=)
self.servers_mock.create_image.assert_called_with(
self.servers_mock.get.return_value,
- compute_fakes.server_name,
+ self.server.name,
)
collist = ('id', 'name', 'owner', 'protected', 'tags', 'visibility')
@@ -425,11 +581,11 @@ class TestServerImageCreate(TestServer):
def test_server_image_create_name(self):
arglist = [
'--name', 'img-nam',
- compute_fakes.server_id,
+ self.server.id,
]
verifylist = [
('name', 'img-nam'),
- ('server', compute_fakes.server_id),
+ ('server', self.server.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -455,29 +611,144 @@ class TestServerImageCreate(TestServer):
self.assertEqual(datalist, data)
+class TestServerList(TestServer):
+
+ # Columns to be listed up.
+ columns = (
+ 'ID',
+ 'Name',
+ 'Status',
+ 'Networks',
+ )
+
+ # Data returned by corresponding Nova API. The elements in this list are
+ # tuples filled with server attributes.
+ data = []
+
+ # Default search options, in the case of no commandline option specified.
+ search_opts = {
+ 'reservation_id': None,
+ 'ip': None,
+ 'ip6': None,
+ 'name': None,
+ 'instance_name': None,
+ 'status': None,
+ 'flavor': None,
+ 'image': None,
+ 'host': None,
+ 'tenant_id': None,
+ 'all_tenants': False,
+ 'user_id': None,
+ }
+
+ # Default params of the core function of the command in the case of no
+ # commandline option specified.
+ kwargs = {
+ 'search_opts': search_opts,
+ 'marker': None,
+ 'limit': None,
+ }
+
+ def setUp(self):
+ super(TestServerList, self).setUp()
+
+ # The fake servers' attributes.
+ self.attrs = {
+ 'status': 'ACTIVE',
+ 'networks': {
+ u'public': [u'10.20.30.40', u'2001:db8::5']
+ },
+ }
+
+ # The servers to be listed.
+ self.servers = self.setup_servers_mock(3)
+
+ self.servers_mock.list.return_value = self.servers
+
+ # Get the command object to test
+ self.cmd = server.ListServer(self.app, None)
+
+ # Prepare data returned by fake Nova API.
+ for s in self.servers:
+ self.data.append((
+ s.id,
+ s.name,
+ s.status,
+ u'public=10.20.30.40, 2001:db8::5',
+ ))
+
+ def test_server_list_no_option(self):
+ arglist = []
+ verifylist = [
+ ('all_projects', False),
+ ('long', False),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.servers_mock.list.assert_called_with(**self.kwargs)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(tuple(self.data), tuple(data))
+
+
+class TestServerLock(TestServer):
+
+ def setUp(self):
+ super(TestServerLock, self).setUp()
+
+ # Get the command object to test
+ self.cmd = server.LockServer(self.app, None)
+
+ # Set methods to be tested.
+ self.methods = {
+ 'lock': None,
+ }
+
+ def test_server_lock_one_server(self):
+ self.run_method_with_servers('lock', 1)
+
+ def test_server_lock_multi_servers(self):
+ self.run_method_with_servers('lock', 3)
+
+
+class TestServerPause(TestServer):
+
+ def setUp(self):
+ super(TestServerPause, self).setUp()
+
+ # Get the command object to test
+ self.cmd = server.PauseServer(self.app, None)
+
+ # Set methods to be tested.
+ self.methods = {
+ 'pause': None,
+ }
+
+ def test_server_pause_one_server(self):
+ self.run_method_with_servers('pause', 1)
+
+ def test_server_pause_multi_servers(self):
+ self.run_method_with_servers('pause', 3)
+
+
class TestServerResize(TestServer):
def setUp(self):
super(TestServerResize, self).setUp()
+ self.server = compute_fakes.FakeServer.create_one_server()
+
# This is the return value for utils.find_resource()
- self.servers_get_return_value = fakes.FakeResource(
- None,
- copy.deepcopy(compute_fakes.SERVER),
- loaded=True,
- )
- self.servers_mock.get.return_value = self.servers_get_return_value
+ self.servers_mock.get.return_value = self.server
self.servers_mock.resize.return_value = None
self.servers_mock.confirm_resize.return_value = None
self.servers_mock.revert_resize.return_value = None
# This is the return value for utils.find_resource()
- self.flavors_get_return_value = fakes.FakeResource(
- None,
- copy.deepcopy(compute_fakes.FLAVOR),
- loaded=True,
- )
+ self.flavors_get_return_value = \
+ compute_fakes.FakeFlavor.create_one_flavor()
self.flavors_mock.get.return_value = self.flavors_get_return_value
# Get the command object to test
@@ -485,12 +756,12 @@ class TestServerResize(TestServer):
def test_server_resize_no_options(self):
arglist = [
- compute_fakes.server_id,
+ self.server.id,
]
verifylist = [
('confirm', False),
('revert', False),
- ('server', compute_fakes.server_id),
+ ('server', self.server.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -498,7 +769,7 @@ class TestServerResize(TestServer):
self.cmd.take_action(parsed_args)
self.servers_mock.get.assert_called_with(
- compute_fakes.server_id,
+ self.server.id,
)
self.assertNotCalled(self.servers_mock.resize)
@@ -507,14 +778,14 @@ class TestServerResize(TestServer):
def test_server_resize(self):
arglist = [
- '--flavor', compute_fakes.flavor_id,
- compute_fakes.server_id,
+ '--flavor', self.flavors_get_return_value.id,
+ self.server.id,
]
verifylist = [
- ('flavor', compute_fakes.flavor_id),
+ ('flavor', self.flavors_get_return_value.id),
('confirm', False),
('revert', False),
- ('server', compute_fakes.server_id),
+ ('server', self.server.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -522,14 +793,14 @@ class TestServerResize(TestServer):
self.cmd.take_action(parsed_args)
self.servers_mock.get.assert_called_with(
- compute_fakes.server_id,
+ self.server.id,
)
self.flavors_mock.get.assert_called_with(
- compute_fakes.flavor_id,
+ self.flavors_get_return_value.id,
)
self.servers_mock.resize.assert_called_with(
- self.servers_get_return_value,
+ self.server,
self.flavors_get_return_value,
)
self.assertNotCalled(self.servers_mock.confirm_resize)
@@ -538,12 +809,12 @@ class TestServerResize(TestServer):
def test_server_resize_confirm(self):
arglist = [
'--confirm',
- compute_fakes.server_id,
+ self.server.id,
]
verifylist = [
('confirm', True),
('revert', False),
- ('server', compute_fakes.server_id),
+ ('server', self.server.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -551,24 +822,24 @@ class TestServerResize(TestServer):
self.cmd.take_action(parsed_args)
self.servers_mock.get.assert_called_with(
- compute_fakes.server_id,
+ self.server.id,
)
self.assertNotCalled(self.servers_mock.resize)
self.servers_mock.confirm_resize.assert_called_with(
- self.servers_get_return_value,
+ self.server,
)
self.assertNotCalled(self.servers_mock.revert_resize)
def test_server_resize_revert(self):
arglist = [
'--revert',
- compute_fakes.server_id,
+ self.server.id,
]
verifylist = [
('confirm', False),
('revert', True),
- ('server', compute_fakes.server_id),
+ ('server', self.server.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -576,16 +847,176 @@ class TestServerResize(TestServer):
self.cmd.take_action(parsed_args)
self.servers_mock.get.assert_called_with(
- compute_fakes.server_id,
+ self.server.id,
)
self.assertNotCalled(self.servers_mock.resize)
self.assertNotCalled(self.servers_mock.confirm_resize)
self.servers_mock.revert_resize.assert_called_with(
- self.servers_get_return_value,
+ self.server,
)
+class TestServerResume(TestServer):
+
+ def setUp(self):
+ super(TestServerResume, self).setUp()
+
+ # Get the command object to test
+ self.cmd = server.ResumeServer(self.app, None)
+
+ # Set methods to be tested.
+ self.methods = {
+ 'resume': None,
+ }
+
+ def test_server_resume_one_server(self):
+ self.run_method_with_servers('resume', 1)
+
+ def test_server_resume_multi_servers(self):
+ self.run_method_with_servers('resume', 3)
+
+
+class TestServerShelve(TestServer):
+
+ def setUp(self):
+ super(TestServerShelve, self).setUp()
+
+ # Get the command object to test
+ self.cmd = server.ShelveServer(self.app, None)
+
+ # Set shelve method to be tested.
+ self.methods = {
+ 'shelve': None,
+ }
+
+ def test_shelve_one_server(self):
+ self.run_method_with_servers('shelve', 1)
+
+ def test_shelve_multi_servers(self):
+ self.run_method_with_servers('shelve', 3)
+
+
+class TestServerStart(TestServer):
+
+ def setUp(self):
+ super(TestServerStart, self).setUp()
+
+ # Get the command object to test
+ self.cmd = server.StartServer(self.app, None)
+
+ # Set methods to be tested.
+ self.methods = {
+ 'start': None,
+ }
+
+ def test_server_start_one_server(self):
+ self.run_method_with_servers('start', 1)
+
+ def test_server_start_multi_servers(self):
+ self.run_method_with_servers('start', 3)
+
+
+class TestServerStop(TestServer):
+
+ def setUp(self):
+ super(TestServerStop, self).setUp()
+
+ # Get the command object to test
+ self.cmd = server.StopServer(self.app, None)
+
+ # Set methods to be tested.
+ self.methods = {
+ 'stop': None,
+ }
+
+ def test_server_stop_one_server(self):
+ self.run_method_with_servers('stop', 1)
+
+ def test_server_stop_multi_servers(self):
+ self.run_method_with_servers('stop', 3)
+
+
+class TestServerSuspend(TestServer):
+
+ def setUp(self):
+ super(TestServerSuspend, self).setUp()
+
+ # Get the command object to test
+ self.cmd = server.SuspendServer(self.app, None)
+
+ # Set methods to be tested.
+ self.methods = {
+ 'suspend': None,
+ }
+
+ def test_server_suspend_one_server(self):
+ self.run_method_with_servers('suspend', 1)
+
+ def test_server_suspend_multi_servers(self):
+ self.run_method_with_servers('suspend', 3)
+
+
+class TestServerUnlock(TestServer):
+
+ def setUp(self):
+ super(TestServerUnlock, self).setUp()
+
+ # Get the command object to test
+ self.cmd = server.UnlockServer(self.app, None)
+
+ # Set methods to be tested.
+ self.methods = {
+ 'unlock': None,
+ }
+
+ def test_server_unlock_one_server(self):
+ self.run_method_with_servers('unlock', 1)
+
+ def test_server_unlock_multi_servers(self):
+ self.run_method_with_servers('unlock', 3)
+
+
+class TestServerUnpause(TestServer):
+
+ def setUp(self):
+ super(TestServerUnpause, self).setUp()
+
+ # Get the command object to test
+ self.cmd = server.UnpauseServer(self.app, None)
+
+ # Set methods to be tested.
+ self.methods = {
+ 'unpause': None,
+ }
+
+ def test_server_unpause_one_server(self):
+ self.run_method_with_servers('unpause', 1)
+
+ def test_server_unpause_multi_servers(self):
+ self.run_method_with_servers('unpause', 3)
+
+
+class TestServerUnshelve(TestServer):
+
+ def setUp(self):
+ super(TestServerUnshelve, self).setUp()
+
+ # Get the command object to test
+ self.cmd = server.UnshelveServer(self.app, None)
+
+ # Set unshelve method to be tested.
+ self.methods = {
+ 'unshelve': None,
+ }
+
+ def test_unshelve_one_server(self):
+ self.run_method_with_servers('unshelve', 1)
+
+ def test_unshelve_multi_servers(self):
+ self.run_method_with_servers('unshelve', 3)
+
+
class TestServerGeneral(testtools.TestCase):
OLD = {
'private': [
diff --git a/openstackclient/tests/compute/v2/test_service.py b/openstackclient/tests/compute/v2/test_service.py
new file mode 100644
index 00000000..380fbc4f
--- /dev/null
+++ b/openstackclient/tests/compute/v2/test_service.py
@@ -0,0 +1,151 @@
+# Copyright 2015 Mirantis, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+import copy
+
+from openstackclient.compute.v2 import service
+from openstackclient.tests.compute.v2 import fakes as compute_fakes
+from openstackclient.tests import fakes
+
+
+class TestService(compute_fakes.TestComputev2):
+
+ def setUp(self):
+ super(TestService, self).setUp()
+
+ # Get a shortcut to the ServiceManager Mock
+ self.service_mock = self.app.client_manager.compute.services
+ self.service_mock.reset_mock()
+
+
+class TestServiceDelete(TestService):
+
+ def setUp(self):
+ super(TestServiceDelete, self).setUp()
+
+ self.service_mock.delete.return_value = None
+
+ # Get the command object to test
+ self.cmd = service.DeleteService(self.app, None)
+
+ def test_service_delete_no_options(self):
+ arglist = [
+ compute_fakes.service_binary,
+ ]
+ verifylist = [
+ ('service', compute_fakes.service_binary),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ self.cmd.take_action(parsed_args)
+
+ self.service_mock.delete.assert_called_with(
+ compute_fakes.service_binary,
+ )
+
+
+class TestServiceList(TestService):
+
+ def setUp(self):
+ super(TestServiceList, self).setUp()
+
+ self.service_mock.list.return_value = [fakes.FakeResource(
+ None,
+ copy.deepcopy(compute_fakes.SERVICE),
+ loaded=True,
+ )]
+
+ # Get the command object to test
+ self.cmd = service.ListService(self.app, None)
+
+ def test_service_list(self):
+ arglist = [
+ '--host', compute_fakes.service_host,
+ '--service', compute_fakes.service_binary,
+ ]
+ verifylist = [
+ ('host', compute_fakes.service_host),
+ ('service', compute_fakes.service_binary),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ self.cmd.take_action(parsed_args)
+
+ self.service_mock.list.assert_called_with(
+ compute_fakes.service_host,
+ compute_fakes.service_binary,
+ )
+
+
+class TestServiceSet(TestService):
+
+ def setUp(self):
+ super(TestServiceSet, self).setUp()
+
+ self.service_mock.enable.return_value = [fakes.FakeResource(
+ None,
+ copy.deepcopy(compute_fakes.SERVICE),
+ loaded=True,
+ )]
+
+ self.service_mock.disable.return_value = [fakes.FakeResource(
+ None,
+ copy.deepcopy(compute_fakes.SERVICE),
+ loaded=True,
+ )]
+
+ self.cmd = service.SetService(self.app, None)
+
+ def test_service_set_enable(self):
+ arglist = [
+ compute_fakes.service_host,
+ compute_fakes.service_binary,
+ '--enable',
+ ]
+ verifylist = [
+ ('host', compute_fakes.service_host),
+ ('service', compute_fakes.service_binary),
+ ('enabled', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ self.service_mock.enable.assert_called_with(
+ compute_fakes.service_host,
+ compute_fakes.service_binary,
+ )
+
+ def test_service_set_disable(self):
+ arglist = [
+ compute_fakes.service_host,
+ compute_fakes.service_binary,
+ '--disable',
+ ]
+ verifylist = [
+ ('host', compute_fakes.service_host),
+ ('service', compute_fakes.service_binary),
+ ('enabled', False),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ self.service_mock.disable.assert_called_with(
+ compute_fakes.service_host,
+ compute_fakes.service_binary,
+ )
diff --git a/openstackclient/tests/fakes.py b/openstackclient/tests/fakes.py
index 979f9481..9f4dcc50 100644
--- a/openstackclient/tests/fakes.py
+++ b/openstackclient/tests/fakes.py
@@ -14,10 +14,11 @@
#
import json
+import mock
import six
import sys
-from keystoneclient import fixture
+from keystoneauth1 import fixture
import requests
@@ -122,17 +123,41 @@ class FakeModule(object):
class FakeResource(object):
- def __init__(self, manager, info, loaded=False):
+ def __init__(self, manager=None, info={}, loaded=False, methods={}):
+ """Set attributes and methods for a resource.
+
+ :param manager:
+ The resource manager
+ :param Dictionary info:
+ A dictionary with all attributes
+ :param bool loaded:
+ True if the resource is loaded in memory
+ :param Dictionary methods:
+ A dictionary with all methods
+ """
self.__name__ = type(self).__name__
self.manager = manager
self._info = info
self._add_details(info)
+ self._add_methods(methods)
self._loaded = loaded
def _add_details(self, info):
for (k, v) in six.iteritems(info):
setattr(self, k, v)
+ def _add_methods(self, methods):
+ """Fake methods with MagicMock objects.
+
+ For each <@key, @value> pairs in methods, add an callable MagicMock
+ object named @key as an attribute, and set the mock's return_value to
+ @value. When users access the attribute with (), @value will be
+ returned, which looks like a function call.
+ """
+ for (name, ret) in six.iteritems(methods):
+ method = mock.MagicMock(return_value=ret)
+ setattr(self, name, method)
+
def __repr__(self):
reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and
k != 'manager')
@@ -150,3 +175,11 @@ class FakeResponse(requests.Response):
self._content = json.dumps(data)
if not isinstance(self._content, six.binary_type):
self._content = self._content.encode()
+
+
+class FakeModel(dict):
+ def __getattr__(self, key):
+ try:
+ return self[key]
+ except KeyError:
+ raise AttributeError(key)
diff --git a/openstackclient/tests/identity/v2_0/test_project.py b/openstackclient/tests/identity/v2_0/test_project.py
index 18e862eb..16ab1957 100644
--- a/openstackclient/tests/identity/v2_0/test_project.py
+++ b/openstackclient/tests/identity/v2_0/test_project.py
@@ -15,7 +15,7 @@
import copy
-from keystoneclient import exceptions as ksc_exc
+from keystoneauth1 import exceptions as ks_exc
from openstackclient.identity.v2_0 import project
from openstackclient.tests import fakes
@@ -223,7 +223,7 @@ class TestProjectCreate(TestProject):
def test_project_create_or_show_exists(self):
def _raise_conflict(*args, **kwargs):
- raise ksc_exc.Conflict(None)
+ raise ks_exc.Conflict(None)
# need to make this throw an exception...
self.projects_mock.create.side_effect = _raise_conflict
diff --git a/openstackclient/tests/identity/v2_0/test_role.py b/openstackclient/tests/identity/v2_0/test_role.py
index 478a4ff2..ec484333 100644
--- a/openstackclient/tests/identity/v2_0/test_role.py
+++ b/openstackclient/tests/identity/v2_0/test_role.py
@@ -16,7 +16,7 @@
import copy
import mock
-from keystoneclient import exceptions as ksc_exc
+from keystoneauth1 import exceptions as ks_exc
from openstackclient.common import exceptions
from openstackclient.identity.v2_0 import role
@@ -146,7 +146,7 @@ class TestRoleCreate(TestRole):
def test_role_create_or_show_exists(self):
def _raise_conflict(*args, **kwargs):
- raise ksc_exc.Conflict(None)
+ raise ks_exc.Conflict(None)
# need to make this throw an exception...
self.roles_mock.create.side_effect = _raise_conflict
diff --git a/openstackclient/tests/identity/v2_0/test_user.py b/openstackclient/tests/identity/v2_0/test_user.py
index 6fe82f93..bf25681e 100644
--- a/openstackclient/tests/identity/v2_0/test_user.py
+++ b/openstackclient/tests/identity/v2_0/test_user.py
@@ -16,7 +16,8 @@
import copy
import mock
-from keystoneclient import exceptions as ksc_exc
+from keystoneauth1 import exceptions as ks_exc
+
from openstackclient.identity.v2_0 import user
from openstackclient.tests import fakes
from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
@@ -345,7 +346,7 @@ class TestUserCreate(TestUser):
def test_user_create_or_show_exists(self):
def _raise_conflict(*args, **kwargs):
- raise ksc_exc.Conflict(None)
+ raise ks_exc.Conflict(None)
# need to make this throw an exception...
self.users_mock.create.side_effect = _raise_conflict
diff --git a/openstackclient/tests/identity/v3/fakes.py b/openstackclient/tests/identity/v3/fakes.py
index 9c4de9cc..9fe341ed 100644
--- a/openstackclient/tests/identity/v3/fakes.py
+++ b/openstackclient/tests/identity/v3/fakes.py
@@ -123,13 +123,11 @@ PROJECT_2 = {
}
region_id = 'region_one'
-region_url = 'http://localhost:1111'
region_parent_region_id = 'region_two'
region_description = 'region one'
REGION = {
'id': region_id,
- 'url': region_url,
'description': region_description,
'parent_region_id': region_parent_region_id,
'links': base_url + 'regions/' + region_id,
@@ -195,6 +193,8 @@ SERVICE_WITHOUT_NAME = {
'links': base_url + 'services/' + service_id,
}
+credential_id = 'c-123'
+
endpoint_id = 'e-123'
endpoint_url = 'http://127.0.0.1:35357'
endpoint_region = 'RegionOne'
@@ -261,6 +261,7 @@ TOKEN_WITH_DOMAIN_ID = {
idp_id = 'test_idp'
idp_description = 'super exciting IdP description'
idp_remote_ids = ['entity1', 'entity2']
+formatted_idp_remote_ids = 'entity1, entity2'
IDENTITY_PROVIDER = {
'id': idp_id,
@@ -400,6 +401,8 @@ class FakeIdentityv3Client(object):
def __init__(self, **kwargs):
self.domains = mock.Mock()
self.domains.resource_class = fakes.FakeResource(None, {})
+ self.credentials = mock.Mock()
+ self.credentials.resource_class = fakes.FakeResource(None, {})
self.endpoints = mock.Mock()
self.endpoints.resource_class = fakes.FakeResource(None, {})
self.groups = mock.Mock()
diff --git a/openstackclient/tests/identity/v3/test_credential.py b/openstackclient/tests/identity/v3/test_credential.py
new file mode 100644
index 00000000..e2e690c3
--- /dev/null
+++ b/openstackclient/tests/identity/v3/test_credential.py
@@ -0,0 +1,112 @@
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import json
+
+from openstackclient.identity.v3 import credential
+from openstackclient.tests.identity.v3 import fakes as identity_fakes
+from openstackclient.tests import utils
+
+
+class TestCredential(identity_fakes.TestIdentityv3):
+ data = {
+ "access": "abc123",
+ "secret": "hidden-message",
+ "trust_id": None
+ }
+
+ def __init__(self, *args):
+ super(TestCredential, self).__init__(*args)
+
+ self.json_data = json.dumps(self.data)
+
+ def setUp(self):
+ super(TestCredential, self).setUp()
+
+ # Get a shortcut to the CredentialManager Mock
+ self.credentials_mock = self.app.client_manager.identity.credentials
+ self.credentials_mock.reset_mock()
+
+ # Get a shortcut to the UserManager Mock
+ self.users_mock = self.app.client_manager.identity.users
+ self.users_mock.reset_mock()
+
+ # Get a shortcut to the ProjectManager Mock
+ self.projects_mock = self.app.client_manager.identity.projects
+ self.projects_mock.reset_mock()
+
+
+class TestCredentialSet(TestCredential):
+ def setUp(self):
+ super(TestCredentialSet, self).setUp()
+ self.cmd = credential.SetCredential(self.app, None)
+
+ def test_credential_set_no_options(self):
+ arglist = [
+ identity_fakes.credential_id,
+ ]
+
+ self.assertRaises(utils.ParserException,
+ self.check_parser, self.cmd, arglist, [])
+
+ def test_credential_set_missing_user(self):
+ arglist = [
+ '--type', 'ec2',
+ '--data', self.json_data,
+ identity_fakes.credential_id,
+ ]
+
+ self.assertRaises(utils.ParserException,
+ self.check_parser, self.cmd, arglist, [])
+
+ def test_credential_set_missing_type(self):
+ arglist = [
+ '--user', identity_fakes.user_name,
+ '--data', self.json_data,
+ identity_fakes.credential_id,
+ ]
+
+ self.assertRaises(utils.ParserException,
+ self.check_parser, self.cmd, arglist, [])
+
+ def test_credential_set_missing_data(self):
+ arglist = [
+ '--user', identity_fakes.user_name,
+ '--type', 'ec2',
+ identity_fakes.credential_id,
+ ]
+
+ self.assertRaises(utils.ParserException,
+ self.check_parser, self.cmd, arglist, [])
+
+ def test_credential_set_valid(self):
+ arglist = [
+ '--user', identity_fakes.user_name,
+ '--type', 'ec2',
+ '--data', self.json_data,
+ identity_fakes.credential_id,
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
+
+ def test_credential_set_valid_with_project(self):
+ arglist = [
+ '--user', identity_fakes.user_name,
+ '--type', 'ec2',
+ '--data', self.json_data,
+ '--project', identity_fakes.project_name,
+ identity_fakes.credential_id,
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, [])
+ self.cmd.take_action(parsed_args)
diff --git a/openstackclient/tests/identity/v3/test_identity_provider.py b/openstackclient/tests/identity/v3/test_identity_provider.py
index cd328c1d..db097d5d 100644
--- a/openstackclient/tests/identity/v3/test_identity_provider.py
+++ b/openstackclient/tests/identity/v3/test_identity_provider.py
@@ -69,7 +69,7 @@ class TestIdentityProviderCreate(TestIdentityProvider):
identity_fakes.idp_description,
True,
identity_fakes.idp_id,
- identity_fakes.idp_remote_ids
+ identity_fakes.formatted_idp_remote_ids
)
self.assertEqual(datalist, data)
@@ -103,7 +103,7 @@ class TestIdentityProviderCreate(TestIdentityProvider):
identity_fakes.idp_description,
True,
identity_fakes.idp_id,
- identity_fakes.idp_remote_ids
+ identity_fakes.formatted_idp_remote_ids
)
self.assertEqual(datalist, data)
@@ -137,7 +137,7 @@ class TestIdentityProviderCreate(TestIdentityProvider):
identity_fakes.idp_description,
True,
identity_fakes.idp_id,
- identity_fakes.idp_remote_ids
+ identity_fakes.formatted_idp_remote_ids
)
self.assertEqual(datalist, data)
@@ -172,7 +172,7 @@ class TestIdentityProviderCreate(TestIdentityProvider):
identity_fakes.idp_description,
True,
identity_fakes.idp_id,
- identity_fakes.idp_remote_ids
+ identity_fakes.formatted_idp_remote_ids
)
self.assertEqual(datalist, data)
@@ -211,7 +211,7 @@ class TestIdentityProviderCreate(TestIdentityProvider):
identity_fakes.idp_description,
True,
identity_fakes.idp_id,
- identity_fakes.idp_remote_ids
+ identity_fakes.formatted_idp_remote_ids
)
self.assertEqual(datalist, data)
@@ -253,7 +253,7 @@ class TestIdentityProviderCreate(TestIdentityProvider):
None,
False,
identity_fakes.idp_id,
- identity_fakes.idp_remote_ids
+ identity_fakes.formatted_idp_remote_ids
)
self.assertEqual(datalist, data)
@@ -363,7 +363,7 @@ class TestIdentityProviderShow(TestIdentityProvider):
identity_fakes.idp_description,
True,
identity_fakes.idp_id,
- identity_fakes.idp_remote_ids
+ identity_fakes.formatted_idp_remote_ids
)
self.assertEqual(datalist, data)
@@ -374,6 +374,50 @@ class TestIdentityProviderSet(TestIdentityProvider):
super(TestIdentityProviderSet, self).setUp()
self.cmd = identity_provider.SetIdentityProvider(self.app, None)
+ def test_identity_provider_set_description(self):
+ """Set Identity Provider's description. """
+ def prepare(self):
+ """Prepare fake return objects before the test is executed"""
+ updated_idp = copy.deepcopy(identity_fakes.IDENTITY_PROVIDER)
+ updated_idp['enabled'] = False
+ resources = fakes.FakeResource(
+ None,
+ updated_idp,
+ loaded=True
+ )
+ self.identity_providers_mock.update.return_value = resources
+
+ prepare(self)
+ new_description = 'new desc'
+ arglist = [
+ '--description', new_description,
+ identity_fakes.idp_id
+ ]
+ verifylist = [
+ ('identity_provider', identity_fakes.idp_id),
+ ('description', new_description),
+ ('enable', False),
+ ('disable', False),
+ ('remote_id', None)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ self.identity_providers_mock.update.assert_called_with(
+ identity_fakes.idp_id,
+ description=new_description
+ )
+
+ collist = ('description', 'enabled', 'id', 'remote_ids')
+ self.assertEqual(collist, columns)
+ datalist = (
+ identity_fakes.idp_description,
+ False,
+ identity_fakes.idp_id,
+ identity_fakes.idp_remote_ids
+ )
+ self.assertEqual(datalist, data)
+
def test_identity_provider_disable(self):
"""Disable Identity Provider
@@ -398,6 +442,7 @@ class TestIdentityProviderSet(TestIdentityProvider):
]
verifylist = [
('identity_provider', identity_fakes.idp_id),
+ ('description', None),
('enable', False),
('disable', True),
('remote_id', identity_fakes.idp_remote_ids)
@@ -443,6 +488,7 @@ class TestIdentityProviderSet(TestIdentityProvider):
]
verifylist = [
('identity_provider', identity_fakes.idp_id),
+ ('description', None),
('enable', True),
('disable', False),
('remote_id', identity_fakes.idp_remote_ids)
@@ -488,6 +534,7 @@ class TestIdentityProviderSet(TestIdentityProvider):
]
verifylist = [
('identity_provider', identity_fakes.idp_id),
+ ('description', None),
('enable', True),
('disable', False),
('remote_id', [self.new_remote_id])
@@ -533,6 +580,7 @@ class TestIdentityProviderSet(TestIdentityProvider):
]
verifylist = [
('identity_provider', identity_fakes.idp_id),
+ ('description', None),
('enable', True),
('disable', False),
('remote_id_file', self.new_remote_id),
diff --git a/openstackclient/tests/identity/v3/test_region.py b/openstackclient/tests/identity/v3/test_region.py
index 7f6ced9f..0ebbbecf 100644
--- a/openstackclient/tests/identity/v3/test_region.py
+++ b/openstackclient/tests/identity/v3/test_region.py
@@ -61,19 +61,17 @@ class TestRegionCreate(TestRegion):
'description': identity_fakes.region_description,
'id': identity_fakes.region_id,
'parent_region': None,
- 'url': None,
}
self.regions_mock.create.assert_called_with(
**kwargs
)
- collist = ('description', 'parent_region', 'region', 'url')
+ collist = ('description', 'parent_region', 'region')
self.assertEqual(collist, columns)
datalist = (
identity_fakes.region_description,
identity_fakes.region_parent_region_id,
identity_fakes.region_id,
- identity_fakes.region_url,
)
self.assertEqual(datalist, data)
@@ -94,19 +92,17 @@ class TestRegionCreate(TestRegion):
'description': None,
'id': identity_fakes.region_id,
'parent_region': None,
- 'url': None,
}
self.regions_mock.create.assert_called_with(
**kwargs
)
- collist = ('description', 'parent_region', 'region', 'url')
+ collist = ('description', 'parent_region', 'region')
self.assertEqual(collist, columns)
datalist = (
identity_fakes.region_description,
identity_fakes.region_parent_region_id,
identity_fakes.region_id,
- identity_fakes.region_url,
)
self.assertEqual(datalist, data)
@@ -129,54 +125,17 @@ class TestRegionCreate(TestRegion):
'description': None,
'id': identity_fakes.region_id,
'parent_region': identity_fakes.region_parent_region_id,
- 'url': None,
}
self.regions_mock.create.assert_called_with(
**kwargs
)
- collist = ('description', 'parent_region', 'region', 'url')
+ collist = ('description', 'parent_region', 'region')
self.assertEqual(collist, columns)
datalist = (
identity_fakes.region_description,
identity_fakes.region_parent_region_id,
identity_fakes.region_id,
- identity_fakes.region_url,
- )
- self.assertEqual(datalist, data)
-
- def test_region_create_url(self):
- arglist = [
- identity_fakes.region_id,
- '--url', identity_fakes.region_url,
- ]
- verifylist = [
- ('region', identity_fakes.region_id),
- ('url', identity_fakes.region_url),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- # DisplayCommandBase.take_action() returns two tuples
- columns, data = self.cmd.take_action(parsed_args)
-
- # Set expected values
- kwargs = {
- 'description': None,
- 'id': identity_fakes.region_id,
- 'parent_region': None,
- 'url': identity_fakes.region_url,
- }
- self.regions_mock.create.assert_called_with(
- **kwargs
- )
-
- collist = ('description', 'parent_region', 'region', 'url')
- self.assertEqual(collist, columns)
- datalist = (
- identity_fakes.region_description,
- identity_fakes.region_parent_region_id,
- identity_fakes.region_id,
- identity_fakes.region_url,
)
self.assertEqual(datalist, data)
@@ -233,13 +192,12 @@ class TestRegionList(TestRegion):
columns, data = self.cmd.take_action(parsed_args)
self.regions_mock.list.assert_called_with()
- collist = ('Region', 'Parent Region', 'Description', 'URL')
+ collist = ('Region', 'Parent Region', 'Description')
self.assertEqual(collist, columns)
datalist = ((
identity_fakes.region_id,
identity_fakes.region_parent_region_id,
identity_fakes.region_description,
- identity_fakes.region_url,
), )
self.assertEqual(datalist, tuple(data))
@@ -257,13 +215,12 @@ class TestRegionList(TestRegion):
self.regions_mock.list.assert_called_with(
parent_region_id=identity_fakes.region_parent_region_id)
- collist = ('Region', 'Parent Region', 'Description', 'URL')
+ collist = ('Region', 'Parent Region', 'Description')
self.assertEqual(collist, columns)
datalist = ((
identity_fakes.region_id,
identity_fakes.region_parent_region_id,
identity_fakes.region_description,
- identity_fakes.region_url,
), )
self.assertEqual(datalist, tuple(data))
@@ -319,29 +276,6 @@ class TestRegionSet(TestRegion):
**kwargs
)
- def test_region_set_url(self):
- arglist = [
- '--url', 'new url',
- identity_fakes.region_id,
- ]
- verifylist = [
- ('url', 'new url'),
- ('region', identity_fakes.region_id),
- ]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- result = self.cmd.run(parsed_args)
- self.assertEqual(0, result)
-
- # Set expected values
- kwargs = {
- 'url': 'new url',
- }
- self.regions_mock.update.assert_called_with(
- identity_fakes.region_id,
- **kwargs
- )
-
def test_region_set_parent_region_id(self):
arglist = [
'--parent-region', 'new_parent',
@@ -395,12 +329,11 @@ class TestRegionShow(TestRegion):
identity_fakes.region_id,
)
- collist = ('description', 'parent_region', 'region', 'url')
+ collist = ('description', 'parent_region', 'region')
self.assertEqual(collist, columns)
datalist = (
identity_fakes.region_description,
identity_fakes.region_parent_region_id,
identity_fakes.region_id,
- identity_fakes.region_url,
)
self.assertEqual(datalist, data)
diff --git a/openstackclient/tests/identity/v3/test_role.py b/openstackclient/tests/identity/v3/test_role.py
index ee3dc946..8ad4b099 100644
--- a/openstackclient/tests/identity/v3/test_role.py
+++ b/openstackclient/tests/identity/v3/test_role.py
@@ -367,6 +367,39 @@ class TestRoleList(TestRole):
), )
self.assertEqual(datalist, tuple(data))
+ def test_user_list_inherited(self):
+ arglist = [
+ '--user', identity_fakes.user_id,
+ '--inherited',
+ ]
+ verifylist = [
+ ('user', identity_fakes.user_id),
+ ('inherited', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # DisplayCommandBase.take_action() returns two tuples
+ columns, data = self.cmd.take_action(parsed_args)
+
+ # Set expected values
+ kwargs = {
+ 'domain': 'default',
+ 'user': self.users_mock.get(),
+ 'os_inherit_extension_inherited': True,
+ }
+ # RoleManager.list(user=, group=, domain=, project=, **kwargs)
+ self.roles_mock.list.assert_called_with(
+ **kwargs
+ )
+
+ collist = ('ID', 'Name')
+ self.assertEqual(collist, columns)
+ datalist = ((
+ identity_fakes.role_id,
+ identity_fakes.role_name,
+ ), )
+ self.assertEqual(datalist, tuple(data))
+
def test_user_list_user(self):
arglist = [
'--user', identity_fakes.user_id,
diff --git a/openstackclient/tests/image/v1/test_image.py b/openstackclient/tests/image/v1/test_image.py
index d10d3b15..4d964bdb 100644
--- a/openstackclient/tests/image/v1/test_image.py
+++ b/openstackclient/tests/image/v1/test_image.py
@@ -261,16 +261,17 @@ class TestImageList(TestImage):
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.image_list.assert_called_with(
- detailed=False,
+ detailed=True,
marker=image_fakes.image_id,
)
- collist = ('ID', 'Name')
+ collist = ('ID', 'Name', 'Status')
self.assertEqual(collist, columns)
datalist = ((
image_fakes.image_id,
image_fakes.image_name,
+ '',
), )
self.assertEqual(datalist, tuple(data))
@@ -288,17 +289,18 @@ class TestImageList(TestImage):
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.image_list.assert_called_with(
- detailed=False,
+ detailed=True,
public=True,
marker=image_fakes.image_id,
)
- collist = ('ID', 'Name')
+ collist = ('ID', 'Name', 'Status')
self.assertEqual(collist, columns)
datalist = ((
image_fakes.image_id,
image_fakes.image_name,
+ '',
), )
self.assertEqual(datalist, tuple(data))
@@ -316,17 +318,18 @@ class TestImageList(TestImage):
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.image_list.assert_called_with(
- detailed=False,
+ detailed=True,
private=True,
marker=image_fakes.image_id,
)
- collist = ('ID', 'Name')
+ collist = ('ID', 'Name', 'Status')
self.assertEqual(collist, columns)
datalist = ((
image_fakes.image_id,
image_fakes.image_name,
+ '',
), )
self.assertEqual(datalist, tuple(data))
@@ -401,12 +404,13 @@ class TestImageList(TestImage):
property_field='properties',
)
- collist = ('ID', 'Name')
+ collist = ('ID', 'Name', 'Status')
self.assertEqual(columns, collist)
datalist = ((
image_fakes.image_id,
image_fakes.image_name,
+ '',
), )
self.assertEqual(datalist, tuple(data))
@@ -423,7 +427,7 @@ class TestImageList(TestImage):
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.image_list.assert_called_with(
- detailed=False,
+ detailed=True,
marker=image_fakes.image_id,
)
si_mock.assert_called_with(
@@ -431,12 +435,13 @@ class TestImageList(TestImage):
'name:asc'
)
- collist = ('ID', 'Name')
+ collist = ('ID', 'Name', 'Status')
self.assertEqual(collist, columns)
datalist = ((
image_fakes.image_id,
- image_fakes.image_name
+ image_fakes.image_name,
+ '',
), )
self.assertEqual(datalist, tuple(data))
diff --git a/openstackclient/tests/image/v2/fakes.py b/openstackclient/tests/image/v2/fakes.py
index 11ad455d..692ef104 100644
--- a/openstackclient/tests/image/v2/fakes.py
+++ b/openstackclient/tests/image/v2/fakes.py
@@ -15,7 +15,10 @@
import copy
import mock
+import random
+import uuid
+from openstackclient.common import utils as common_utils
from openstackclient.tests import fakes
from openstackclient.tests import utils
@@ -167,3 +170,133 @@ class TestImagev2(utils.TestCommand):
endpoint=fakes.AUTH_URL,
token=fakes.AUTH_TOKEN,
)
+
+
+class FakeImage(object):
+ """Fake one or more images.
+
+ TODO(xiexs): Currently, only image API v2 is supported by this class.
+ """
+
+ @staticmethod
+ def create_one_image(attrs={}):
+ """Create a fake image.
+
+ :param Dictionary attrs:
+ A dictionary with all attrbutes of image
+ :retrun:
+ A FakeResource object with id, name, owner, protected,
+ visibility and tags attrs
+ """
+ # Set default attribute
+ image_info = {
+ 'id': 'image-id' + uuid.uuid4().hex,
+ 'name': 'image-name' + uuid.uuid4().hex,
+ 'owner': 'image-owner' + uuid.uuid4().hex,
+ 'protected': bool(random.choice([0, 1])),
+ 'visibility': random.choice(['public', 'private']),
+ 'tags': [uuid.uuid4().hex for r in range(random.randint(1, 5))],
+ }
+
+ # Overwrite default attributes if there are some attributes set
+ image_info.update(attrs)
+
+ image = fakes.FakeResource(
+ None,
+ image_info,
+ loaded=True)
+ return image
+
+ @staticmethod
+ def create_images(attrs={}, count=2):
+ """Create multiple fake images.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes of image
+ :param Integer count:
+ The number of images to be faked
+ :return:
+ A list of FakeResource objects
+ """
+ images = []
+ for n in range(0, count):
+ images.append(FakeImage.create_one_image(attrs))
+
+ return images
+
+ @staticmethod
+ def get_images(images=None, count=2):
+ """Get an iterable MagicMock object with a list of faked images.
+
+ If images list is provided, then initialize the Mock object with the
+ list. Otherwise create one.
+
+ :param List images:
+ A list of FakeResource objects faking images
+ :param Integer count:
+ The number of images to be faked
+ :return
+ An iterable Mock object with side_effect set to a list of faked
+ images
+ """
+ if images is None:
+ images = FakeImage.create_images(count)
+
+ return mock.MagicMock(side_effect=images)
+
+ @staticmethod
+ def get_image_info(image=None):
+ """Get the image info from a faked image object.
+
+ :param image:
+ A FakeResource objects faking image
+ :return
+ A dictionary which includes the faked image info as follows:
+ {
+ 'id': image_id,
+ 'name': image_name,
+ 'owner': image_owner,
+ 'protected': image_protected,
+ 'visibility': image_visibility,
+ 'tags': image_tags
+ }
+ """
+ if image is not None:
+ return image._info
+ return {}
+
+ @staticmethod
+ def get_image_columns(image=None):
+ """Get the image columns from a faked image object.
+
+ :param image:
+ A FakeResource objects faking image
+ :return
+ A tuple which may include the following keys:
+ ('id', 'name', 'owner', 'protected', 'visibility', 'tags')
+ """
+ if image is not None:
+ return tuple(k for k in sorted(
+ FakeImage.get_image_info(image).keys()))
+ return tuple([])
+
+ @staticmethod
+ def get_image_data(image=None):
+ """Get the image data from a faked image object.
+
+ :param image:
+ A FakeResource objects faking image
+ :return
+ A tuple which may include the following values:
+ ('image-123', 'image-foo', 'admin', False, 'public', 'bar, baz')
+ """
+ data_list = []
+ if image is not None:
+ for x in sorted(FakeImage.get_image_info(image).keys()):
+ if x == 'tags':
+ # The 'tags' should be format_list
+ data_list.append(
+ common_utils.format_list(getattr(image, x)))
+ else:
+ data_list.append(getattr(image, x))
+ return tuple(data_list)
diff --git a/openstackclient/tests/image/v2/test_image.py b/openstackclient/tests/image/v2/test_image.py
index 4ce85475..be73c4ca 100644
--- a/openstackclient/tests/image/v2/test_image.py
+++ b/openstackclient/tests/image/v2/test_image.py
@@ -112,6 +112,7 @@ class TestImageCreate(TestImage):
'--disk-format', 'fs',
'--min-disk', '10',
'--min-ram', '4',
+ '--owner', '123456',
'--protected',
'--private',
image_fakes.image_name,
@@ -121,6 +122,7 @@ class TestImageCreate(TestImage):
('disk_format', 'fs'),
('min_disk', 10),
('min_ram', 4),
+ ('owner', '123456'),
('protected', True),
('unprotected', False),
('public', False),
@@ -139,6 +141,7 @@ class TestImageCreate(TestImage):
disk_format='fs',
min_disk=10,
min_ram=4,
+ owner='123456',
protected=True,
visibility='private',
)
@@ -213,11 +216,10 @@ class TestImageCreate(TestImage):
def test_image_create_dead_options(self):
arglist = [
- '--owner', 'nobody',
+ '--store', 'somewhere',
image_fakes.image_name,
]
verifylist = [
- ('owner', 'nobody'),
('name', image_fakes.image_name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -238,10 +240,8 @@ class TestAddProjectToImage(TestImage):
copy.deepcopy(image_fakes.IMAGE),
loaded=True,
)
- self.image_members_mock.create.return_value = fakes.FakeResource(
- None,
+ self.image_members_mock.create.return_value = fakes.FakeModel(
copy.deepcopy(image_fakes.MEMBER),
- loaded=True,
)
self.project_mock.get.return_value = fakes.FakeResource(
None,
@@ -374,12 +374,13 @@ class TestImageList(TestImage):
marker=image_fakes.image_id,
)
- collist = ('ID', 'Name')
+ collist = ('ID', 'Name', 'Status')
self.assertEqual(collist, columns)
datalist = ((
image_fakes.image_id,
image_fakes.image_name,
+ '',
), )
self.assertEqual(datalist, tuple(data))
@@ -402,12 +403,13 @@ class TestImageList(TestImage):
marker=image_fakes.image_id,
)
- collist = ('ID', 'Name')
+ collist = ('ID', 'Name', 'Status')
self.assertEqual(collist, columns)
datalist = ((
image_fakes.image_id,
image_fakes.image_name,
+ '',
), )
self.assertEqual(datalist, tuple(data))
@@ -430,12 +432,13 @@ class TestImageList(TestImage):
marker=image_fakes.image_id,
)
- collist = ('ID', 'Name')
+ collist = ('ID', 'Name', 'Status')
self.assertEqual(collist, columns)
datalist = ((
image_fakes.image_id,
image_fakes.image_name,
+ '',
), )
self.assertEqual(datalist, tuple(data))
@@ -458,12 +461,13 @@ class TestImageList(TestImage):
marker=image_fakes.image_id,
)
- collist = ('ID', 'Name')
+ collist = ('ID', 'Name', 'Status')
self.assertEqual(columns, collist)
datalist = ((
image_fakes.image_id,
image_fakes.image_name,
+ '',
), )
self.assertEqual(datalist, tuple(data))
@@ -536,12 +540,13 @@ class TestImageList(TestImage):
property_field='properties',
)
- collist = ('ID', 'Name')
+ collist = ('ID', 'Name', 'Status')
self.assertEqual(columns, collist)
datalist = ((
image_fakes.image_id,
image_fakes.image_name,
+ '',
), )
self.assertEqual(datalist, tuple(data))
@@ -565,12 +570,13 @@ class TestImageList(TestImage):
'name:asc'
)
- collist = ('ID', 'Name')
+ collist = ('ID', 'Name', 'Status')
self.assertEqual(collist, columns)
datalist = ((
image_fakes.image_id,
- image_fakes.image_name
+ image_fakes.image_name,
+ '',
), )
self.assertEqual(datalist, tuple(data))
diff --git a/openstackclient/tests/object/v1/fakes.py b/openstackclient/tests/object/v1/fakes.py
index 6aef05b1..986ab2f3 100644
--- a/openstackclient/tests/object/v1/fakes.py
+++ b/openstackclient/tests/object/v1/fakes.py
@@ -13,7 +13,7 @@
# under the License.
#
-from keystoneclient import session
+from keystoneauth1 import session
from openstackclient.api import object_store_v1 as object_store
from openstackclient.tests import utils
diff --git a/openstackclient/tests/object/v1/test_container_all.py b/openstackclient/tests/object/v1/test_container_all.py
index 4477f2e0..69fc0f39 100644
--- a/openstackclient/tests/object/v1/test_container_all.py
+++ b/openstackclient/tests/object/v1/test_container_all.py
@@ -286,7 +286,6 @@ class TestContainerShow(TestContainerAll):
def test_object_show_container(self):
headers = {
- 'x-container-meta-owner': object_fakes.ACCOUNT_ID,
'x-container-object-count': '42',
'x-container-bytes-used': '123',
'x-container-read': 'qaz',
@@ -316,7 +315,6 @@ class TestContainerShow(TestContainerAll):
'account',
'bytes_used',
'container',
- 'meta-owner',
'object_count',
'read_acl',
'sync_key',
@@ -328,7 +326,6 @@ class TestContainerShow(TestContainerAll):
object_fakes.ACCOUNT_ID,
'123',
'ernie',
- object_fakes.ACCOUNT_ID,
'42',
'qaz',
'rfv',
diff --git a/openstackclient/tests/object/v1/test_object_all.py b/openstackclient/tests/object/v1/test_object_all.py
index 41fe6324..7a76ab76 100644
--- a/openstackclient/tests/object/v1/test_object_all.py
+++ b/openstackclient/tests/object/v1/test_object_all.py
@@ -160,7 +160,6 @@ class TestObjectShow(TestObjectAll):
'content-type',
'etag',
'last-modified',
- 'meta-owner',
'object',
'x-object-manifest',
)
@@ -172,7 +171,6 @@ class TestObjectShow(TestObjectAll):
'text/plain',
'4c4e39a763d58392724bccf76a58783a',
'yesterday',
- object_fakes.ACCOUNT_ID,
object_fakes.object_name_1,
'manifest',
)
diff --git a/openstackclient/tests/volume/v1/test_volume.py b/openstackclient/tests/volume/v1/test_volume.py
index f73260e9..70ff50de 100644
--- a/openstackclient/tests/volume/v1/test_volume.py
+++ b/openstackclient/tests/volume/v1/test_volume.py
@@ -527,6 +527,188 @@ class TestVolumeCreate(TestVolume):
self.assertEqual(datalist, data)
+class TestVolumeList(TestVolume):
+
+ def setUp(self):
+ super(TestVolumeList, self).setUp()
+
+ self.volumes_mock.list.return_value = [
+ fakes.FakeResource(
+ None,
+ copy.deepcopy(volume_fakes.VOLUME),
+ loaded=True,
+ ),
+ ]
+
+ # Get the command object to test
+ self.cmd = volume.ListVolume(self.app, None)
+
+ def test_volume_list_no_options(self):
+ arglist = []
+ verifylist = [
+ ('long', False),
+ ('all_projects', False),
+ ('name', None),
+ ('status', None),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = (
+ 'ID',
+ 'Display Name',
+ 'Status',
+ 'Size',
+ 'Attached to',
+ )
+ self.assertEqual(collist, columns)
+
+ datalist = ((
+ volume_fakes.volume_id,
+ volume_fakes.volume_name,
+ volume_fakes.volume_status,
+ volume_fakes.volume_size,
+ '',
+ ), )
+ self.assertEqual(datalist, tuple(data))
+
+ def test_volume_list_name(self):
+ arglist = [
+ '--name', volume_fakes.volume_name,
+ ]
+ verifylist = [
+ ('long', False),
+ ('all_projects', False),
+ ('name', volume_fakes.volume_name),
+ ('status', None),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = (
+ 'ID',
+ 'Display Name',
+ 'Status',
+ 'Size',
+ 'Attached to',
+ )
+ self.assertEqual(collist, tuple(columns))
+
+ datalist = ((
+ volume_fakes.volume_id,
+ volume_fakes.volume_name,
+ volume_fakes.volume_status,
+ volume_fakes.volume_size,
+ '',
+ ), )
+ self.assertEqual(datalist, tuple(data))
+
+ def test_volume_list_status(self):
+ arglist = [
+ '--status', volume_fakes.volume_status,
+ ]
+ verifylist = [
+ ('long', False),
+ ('all_projects', False),
+ ('name', None),
+ ('status', volume_fakes.volume_status),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = (
+ 'ID',
+ 'Display Name',
+ 'Status',
+ 'Size',
+ 'Attached to',
+ )
+ self.assertEqual(collist, tuple(columns))
+
+ datalist = ((
+ volume_fakes.volume_id,
+ volume_fakes.volume_name,
+ volume_fakes.volume_status,
+ volume_fakes.volume_size,
+ '',
+ ), )
+ self.assertEqual(datalist, tuple(data))
+
+ def test_volume_list_all_projects(self):
+ arglist = [
+ '--all-projects',
+ ]
+ verifylist = [
+ ('long', False),
+ ('all_projects', True),
+ ('name', None),
+ ('status', None),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = (
+ 'ID',
+ 'Display Name',
+ 'Status',
+ 'Size',
+ 'Attached to',
+ )
+ self.assertEqual(collist, columns)
+
+ datalist = ((
+ volume_fakes.volume_id,
+ volume_fakes.volume_name,
+ volume_fakes.volume_status,
+ volume_fakes.volume_size,
+ '',
+ ), )
+ self.assertEqual(datalist, tuple(data))
+
+ def test_volume_list_long(self):
+ arglist = [
+ '--long',
+ ]
+ verifylist = [
+ ('long', True),
+ ('all_projects', False),
+ ('name', None),
+ ('status', None),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = (
+ 'ID',
+ 'Display Name',
+ 'Status',
+ 'Size',
+ 'Type',
+ 'Bootable',
+ 'Attached to',
+ 'Properties',
+ )
+ self.assertEqual(collist, columns)
+
+ datalist = ((
+ volume_fakes.volume_id,
+ volume_fakes.volume_name,
+ volume_fakes.volume_status,
+ volume_fakes.volume_size,
+ volume_fakes.volume_type,
+ '',
+ '',
+ "Alpha='a', Beta='b', Gamma='g'",
+ ), )
+ self.assertEqual(datalist, tuple(data))
+
+
class TestVolumeSet(TestVolume):
def setUp(self):
diff --git a/openstackclient/tests/volume/v2/fakes.py b/openstackclient/tests/volume/v2/fakes.py
index 7b7758a3..60cec335 100644
--- a/openstackclient/tests/volume/v2/fakes.py
+++ b/openstackclient/tests/volume/v2/fakes.py
@@ -14,9 +14,11 @@
import copy
import mock
+import random
+import uuid
from openstackclient.tests import fakes
-from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
+from openstackclient.tests.identity.v3 import fakes as identity_fakes
from openstackclient.tests.image.v2 import fakes as image_fakes
from openstackclient.tests import utils
@@ -212,7 +214,7 @@ class TestVolume(utils.TestCommand):
endpoint=fakes.AUTH_URL,
token=fakes.AUTH_TOKEN
)
- self.app.client_manager.identity = identity_fakes.FakeIdentityv2Client(
+ self.app.client_manager.identity = identity_fakes.FakeIdentityv3Client(
endpoint=fakes.AUTH_URL,
token=fakes.AUTH_TOKEN
)
@@ -220,3 +222,85 @@ class TestVolume(utils.TestCommand):
endpoint=fakes.AUTH_URL,
token=fakes.AUTH_TOKEN
)
+
+
+class FakeVolume(object):
+ """Fake one or more volumes.
+
+ TODO(xiexs): Currently, only volume API v2 is supported by this class.
+ """
+
+ @staticmethod
+ def create_one_volume(attrs={}):
+ """Create a fake volume.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes of volume
+ :retrun:
+ A FakeResource object with id, name, status, etc.
+ """
+ # Set default attribute
+ volume_info = {
+ 'id': 'volume-id' + uuid.uuid4().hex,
+ 'name': 'volume-name' + uuid.uuid4().hex,
+ 'description': 'description' + uuid.uuid4().hex,
+ 'status': random.choice(['available', 'in_use']),
+ 'size': random.randint(1, 20),
+ 'volume_type':
+ random.choice(['fake_lvmdriver-1', 'fake_lvmdriver-2']),
+ 'metadata': {
+ 'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex,
+ 'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex,
+ 'key' + uuid.uuid4().hex: 'val' + uuid.uuid4().hex},
+ 'snapshot_id': random.randint(1, 5),
+ 'availability_zone': 'zone' + uuid.uuid4().hex,
+ 'attachments': {
+ 'device': '/dev/' + uuid.uuid4().hex,
+ 'server_id': uuid.uuid4().hex},
+ }
+
+ # Overwrite default attributes if there are some attributes set
+ volume_info.update(attrs)
+
+ volume = fakes.FakeResource(
+ None,
+ volume_info,
+ loaded=True)
+ return volume
+
+ @staticmethod
+ def create_volumes(attrs={}, count=2):
+ """Create multiple fake volumes.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes of volume
+ :param Integer count:
+ The number of volumes to be faked
+ :return:
+ A list of FakeResource objects
+ """
+ volumes = []
+ for n in range(0, count):
+ volumes.append(FakeVolume.create_one_volume(attrs))
+
+ return volumes
+
+ @staticmethod
+ def get_volumes(volumes=None, count=2):
+ """Get an iterable MagicMock object with a list of faked volumes.
+
+ If volumes list is provided, then initialize the Mock object with the
+ list. Otherwise create one.
+
+ :param List volumes:
+ A list of FakeResource objects faking volumes
+ :param Integer count:
+ The number of volumes to be faked
+ :return
+ An iterable Mock object with side_effect set to a list of faked
+ volumes
+ """
+ if volumes is None:
+ volumes = FakeVolume.create_volumes(count)
+
+ return mock.MagicMock(side_effect=volumes)
diff --git a/openstackclient/tests/volume/v2/test_snapshot.py b/openstackclient/tests/volume/v2/test_snapshot.py
index 3ceb57fa..3b30d4ef 100644
--- a/openstackclient/tests/volume/v2/test_snapshot.py
+++ b/openstackclient/tests/volume/v2/test_snapshot.py
@@ -225,6 +225,7 @@ class TestSnapshotList(TestSnapshot):
def test_snapshot_list_without_options(self):
arglist = []
verifylist = [
+ ('all_projects', False),
("long", False)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -243,7 +244,7 @@ class TestSnapshotList(TestSnapshot):
def test_snapshot_list_with_options(self):
arglist = ["--long"]
- verifylist = [("long", True)]
+ verifylist = [("long", True), ('all_projects', False)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
@@ -263,3 +264,33 @@ class TestSnapshotList(TestSnapshot):
volume_fakes.EXPECTED_SNAPSHOT.get("properties")
),)
self.assertEqual(datalist, tuple(data))
+
+ def test_snapshot_list_all_projects(self):
+ arglist = [
+ '--all-projects',
+ ]
+ verifylist = [
+ ('long', False),
+ ('all_projects', True)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = [
+ "ID",
+ "Name",
+ "Description",
+ "Status",
+ "Size"
+ ]
+ self.assertEqual(collist, columns)
+
+ datalist = ((
+ volume_fakes.snapshot_id,
+ volume_fakes.snapshot_name,
+ volume_fakes.snapshot_description,
+ "available",
+ volume_fakes.snapshot_size
+ ), )
+ self.assertEqual(datalist, tuple(data))
diff --git a/openstackclient/tests/volume/v2/test_type.py b/openstackclient/tests/volume/v2/test_type.py
index 9a07263b..c63cd1fa 100644
--- a/openstackclient/tests/volume/v2/test_type.py
+++ b/openstackclient/tests/volume/v2/test_type.py
@@ -73,7 +73,7 @@ class TestTypeCreate(TestType):
self.types_mock.create.assert_called_with(
volume_fakes.type_name,
description=volume_fakes.type_description,
- public=True,
+ is_public=True,
)
collist = (
@@ -93,7 +93,7 @@ class TestTypeCreate(TestType):
arglist = [
volume_fakes.type_name,
"--description", volume_fakes.type_description,
- "--private"
+ "--private",
]
verifylist = [
("name", volume_fakes.type_name),
@@ -107,7 +107,7 @@ class TestTypeCreate(TestType):
self.types_mock.create.assert_called_with(
volume_fakes.type_name,
description=volume_fakes.type_description,
- private=True,
+ is_public=False,
)
collist = (
diff --git a/openstackclient/tests/volume/v2/test_volume.py b/openstackclient/tests/volume/v2/test_volume.py
index b15fd02f..50826c31 100644
--- a/openstackclient/tests/volume/v2/test_volume.py
+++ b/openstackclient/tests/volume/v2/test_volume.py
@@ -14,8 +14,10 @@
import copy
+from mock import call
+
from openstackclient.tests import fakes
-from openstackclient.tests.identity.v2_0 import fakes as identity_fakes
+from openstackclient.tests.identity.v3 import fakes as identity_fakes
from openstackclient.tests.volume.v2 import fakes as volume_fakes
from openstackclient.volume.v2 import volume
@@ -27,7 +29,7 @@ class TestVolume(volume_fakes.TestVolume):
self.volumes_mock = self.app.client_manager.volume.volumes
self.volumes_mock.reset_mock()
- self.projects_mock = self.app.client_manager.identity.tenants
+ self.projects_mock = self.app.client_manager.identity.projects
self.projects_mock.reset_mock()
self.users_mock = self.app.client_manager.identity.users
@@ -36,6 +38,14 @@ class TestVolume(volume_fakes.TestVolume):
self.images_mock = self.app.client_manager.image.images
self.images_mock.reset_mock()
+ def setup_volumes_mock(self, count):
+ volumes = volume_fakes.FakeVolume.create_volumes(count=count)
+
+ self.volumes_mock.get = volume_fakes.FakeVolume.get_volumes(
+ volumes,
+ 0)
+ return volumes
+
class TestVolumeCreate(TestVolume):
def setUp(self):
@@ -560,32 +570,145 @@ class TestVolumeList(TestVolume):
), )
self.assertEqual(datalist, tuple(data))
- def test_volume_list_all_projects_option(self):
+ def test_volume_list_project(self):
arglist = [
- '--all-projects',
+ '--project', identity_fakes.project_name,
]
verifylist = [
+ ('project', identity_fakes.project_name),
('long', False),
- ('all_projects', True),
- ('name', None),
+ ('all_projects', False),
('status', None),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- collist = [
+ collist = (
'ID',
'Display Name',
'Status',
'Size',
'Attached to',
+ )
+ self.assertEqual(collist, tuple(columns))
+
+ server = volume_fakes.volume_attachment_server['server_id']
+ device = volume_fakes.volume_attachment_server['device']
+ msg = 'Attached to %s on %s ' % (server, device)
+
+ datalist = ((
+ volume_fakes.volume_id,
+ volume_fakes.volume_name,
+ volume_fakes.volume_status,
+ volume_fakes.volume_size,
+ msg,
+ ), )
+ self.assertEqual(datalist, tuple(data))
+
+ def test_volume_list_project_domain(self):
+ arglist = [
+ '--project', identity_fakes.project_name,
+ '--project-domain', identity_fakes.domain_name,
]
- self.assertEqual(collist, columns)
+ verifylist = [
+ ('project', identity_fakes.project_name),
+ ('project_domain', identity_fakes.domain_name),
+ ('long', False),
+ ('all_projects', False),
+ ('status', None),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = (
+ 'ID',
+ 'Display Name',
+ 'Status',
+ 'Size',
+ 'Attached to',
+ )
+ self.assertEqual(collist, tuple(columns))
+
+ server = volume_fakes.volume_attachment_server['server_id']
+ device = volume_fakes.volume_attachment_server['device']
+ msg = 'Attached to %s on %s ' % (server, device)
+
+ datalist = ((
+ volume_fakes.volume_id,
+ volume_fakes.volume_name,
+ volume_fakes.volume_status,
+ volume_fakes.volume_size,
+ msg,
+ ), )
+ self.assertEqual(datalist, tuple(data))
+
+ def test_volume_list_user(self):
+ arglist = [
+ '--user', identity_fakes.user_name,
+ ]
+ verifylist = [
+ ('user', identity_fakes.user_name),
+ ('long', False),
+ ('all_projects', False),
+ ('status', None),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = (
+ 'ID',
+ 'Display Name',
+ 'Status',
+ 'Size',
+ 'Attached to',
+ )
+ self.assertEqual(collist, tuple(columns))
server = volume_fakes.volume_attachment_server['server_id']
device = volume_fakes.volume_attachment_server['device']
msg = 'Attached to %s on %s ' % (server, device)
+
+ datalist = ((
+ volume_fakes.volume_id,
+ volume_fakes.volume_name,
+ volume_fakes.volume_status,
+ volume_fakes.volume_size,
+ msg,
+ ), )
+ self.assertEqual(datalist, tuple(data))
+
+ def test_volume_list_user_domain(self):
+ arglist = [
+ '--user', identity_fakes.user_name,
+ '--user-domain', identity_fakes.domain_name,
+ ]
+ verifylist = [
+ ('user', identity_fakes.user_name),
+ ('user_domain', identity_fakes.domain_name),
+ ('long', False),
+ ('all_projects', False),
+ ('status', None),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = (
+ 'ID',
+ 'Display Name',
+ 'Status',
+ 'Size',
+ 'Attached to',
+ )
+ self.assertEqual(collist, tuple(columns))
+
+ server = volume_fakes.volume_attachment_server['server_id']
+ device = volume_fakes.volume_attachment_server['device']
+ msg = 'Attached to %s on %s ' % (server, device)
+
datalist = ((
volume_fakes.volume_id,
volume_fakes.volume_name,
@@ -666,6 +789,41 @@ class TestVolumeList(TestVolume):
), )
self.assertEqual(datalist, tuple(data))
+ def test_volume_list_all_projects(self):
+ arglist = [
+ '--all-projects',
+ ]
+ verifylist = [
+ ('long', False),
+ ('all_projects', True),
+ ('name', None),
+ ('status', None),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ collist = [
+ 'ID',
+ 'Display Name',
+ 'Status',
+ 'Size',
+ 'Attached to',
+ ]
+ self.assertEqual(collist, columns)
+
+ server = volume_fakes.volume_attachment_server['server_id']
+ device = volume_fakes.volume_attachment_server['device']
+ msg = 'Attached to %s on %s ' % (server, device)
+ datalist = ((
+ volume_fakes.volume_id,
+ volume_fakes.volume_name,
+ volume_fakes.volume_status,
+ volume_fakes.volume_size,
+ msg,
+ ), )
+ self.assertEqual(datalist, tuple(data))
+
def test_volume_list_long(self):
arglist = [
'--long',
@@ -740,24 +898,38 @@ class TestVolumeDelete(TestVolume):
def setUp(self):
super(TestVolumeDelete, self).setUp()
- self.volumes_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(volume_fakes.VOLUME),
- loaded=True)
self.volumes_mock.delete.return_value = None
# Get the command object to mock
self.cmd = volume.DeleteVolume(self.app, None)
- def test_volume_delete(self):
+ def test_volume_delete_one_volume(self):
+ volumes = self.setup_volumes_mock(count=1)
+
arglist = [
- volume_fakes.volume_id
+ volumes[0].id
]
verifylist = [
- ("volumes", [volume_fakes.volume_id])
+ ("volumes", [volumes[0].id])
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.cmd.take_action(parsed_args)
- self.volumes_mock.delete.assert_called_with(volume_fakes.volume_id)
+ self.volumes_mock.delete.assert_called_with(volumes[0].id)
+
+ def test_volume_delete_multi_volumes(self):
+ volumes = self.setup_volumes_mock(count=3)
+
+ arglist = [v.id for v in volumes]
+ verifylist = [
+ ('volumes', arglist),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ calls = [call(v.id) for v in volumes]
+
+ self.volumes_mock.delete.assert_has_calls(calls)
diff --git a/openstackclient/volume/v1/snapshot.py b/openstackclient/volume/v1/snapshot.py
index de7bb5b9..93e17eb8 100644
--- a/openstackclient/volume/v1/snapshot.py
+++ b/openstackclient/volume/v1/snapshot.py
@@ -111,6 +111,12 @@ class ListSnapshot(lister.Lister):
def get_parser(self, prog_name):
parser = super(ListSnapshot, self).get_parser(prog_name)
parser.add_argument(
+ '--all-projects',
+ action='store_true',
+ default=False,
+ help='Include all projects (admin only)',
+ )
+ parser.add_argument(
'--long',
action='store_true',
default=False,
@@ -157,7 +163,12 @@ class ListSnapshot(lister.Lister):
# Just forget it if there's any trouble
pass
- data = self.app.client_manager.volume.volume_snapshots.list()
+ search_opts = {
+ 'all_tenants': parsed_args.all_projects,
+ }
+
+ data = self.app.client_manager.volume.volume_snapshots.list(
+ search_opts=search_opts)
return (column_headers,
(utils.get_item_properties(
s, columns,
diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py
index 52b0eb2e..0691d884 100644
--- a/openstackclient/volume/v1/volume.py
+++ b/openstackclient/volume/v1/volume.py
@@ -207,16 +207,16 @@ class ListVolume(lister.Lister):
def get_parser(self, prog_name):
parser = super(ListVolume, self).get_parser(prog_name)
parser.add_argument(
+ '--name',
+ metavar='<name>',
+ help='Filter results by volume name',
+ )
+ parser.add_argument(
'--status',
metavar='<status>',
help='Filter results by status',
)
parser.add_argument(
- '--name',
- metavar='<name>',
- help='Filter results by name',
- )
- parser.add_argument(
'--all-projects',
action='store_true',
default=False,
@@ -239,7 +239,7 @@ class ListVolume(lister.Lister):
def _format_attach(attachments):
"""Return a formatted string of a volume's attached instances
- :param volume: a volume.attachments field
+ :param attachments: a volume.attachments field
:rtype: a string of formatted instances
"""
diff --git a/openstackclient/volume/v2/snapshot.py b/openstackclient/volume/v2/snapshot.py
index bbc92c48..aa7630ae 100644
--- a/openstackclient/volume/v2/snapshot.py
+++ b/openstackclient/volume/v2/snapshot.py
@@ -108,6 +108,12 @@ class ListSnapshot(lister.Lister):
def get_parser(self, prog_name):
parser = super(ListSnapshot, self).get_parser(prog_name)
parser.add_argument(
+ '--all-projects',
+ action='store_true',
+ default=False,
+ help='Include all projects (admin only)',
+ )
+ parser.add_argument(
'--long',
action='store_true',
default=False,
@@ -149,7 +155,12 @@ class ListSnapshot(lister.Lister):
# Just forget it if there's any trouble
pass
- data = self.app.client_manager.volume.volume_snapshots.list()
+ search_opts = {
+ 'all_tenants': parsed_args.all_projects,
+ }
+
+ data = self.app.client_manager.volume.volume_snapshots.list(
+ search_opts=search_opts)
return (column_headers,
(utils.get_item_properties(
s, columns,
diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py
index 758f312b..bbcceca6 100644
--- a/openstackclient/volume/v2/volume.py
+++ b/openstackclient/volume/v2/volume.py
@@ -16,7 +16,6 @@
import copy
import logging
-import os
from cliff import command
from cliff import lister
@@ -25,6 +24,7 @@ import six
from openstackclient.common import parseractions
from openstackclient.common import utils
+from openstackclient.identity import common as identity_common
class CreateVolume(show.ShowOne):
@@ -185,10 +185,10 @@ class DeleteVolume(command.Command):
for volume in parsed_args.volumes:
volume_obj = utils.find_resource(
volume_client.volumes, volume)
- if parsed_args.force:
- volume_client.volumes.force_delete(volume_obj.id)
- else:
- volume_client.volumes.delete(volume_obj.id)
+ if parsed_args.force:
+ volume_client.volumes.force_delete(volume_obj.id)
+ else:
+ volume_client.volumes.delete(volume_obj.id)
return
@@ -200,27 +200,39 @@ class ListVolume(lister.Lister):
def get_parser(self, prog_name):
parser = super(ListVolume, self).get_parser(prog_name)
parser.add_argument(
- '--all-projects',
- action='store_true',
- default=bool(int(os.environ.get("ALL_PROJECTS", 0))),
- help='Include all projects (admin only)',
+ '--project',
+ metavar='<project-id>',
+ help='Filter results by project (name or ID) (admin only)'
)
+ identity_common.add_project_domain_option_to_parser(parser)
parser.add_argument(
- '--long',
- action='store_true',
- default=False,
- help='List additional fields in output',
+ '--user',
+ metavar='<user-id>',
+ help='Filter results by user (name or ID) (admin only)'
)
+ identity_common.add_user_domain_option_to_parser(parser)
parser.add_argument(
'--name',
metavar='<name>',
- help='Filter results by name',
+ help='Filter results by volume name',
)
parser.add_argument(
'--status',
metavar='<status>',
help='Filter results by status',
)
+ parser.add_argument(
+ '--all-projects',
+ action='store_true',
+ default=False,
+ help='Include all projects (admin only)',
+ )
+ parser.add_argument(
+ '--long',
+ action='store_true',
+ default=False,
+ help='List additional fields in output',
+ )
return parser
@utils.log_method(log)
@@ -228,11 +240,12 @@ class ListVolume(lister.Lister):
volume_client = self.app.client_manager.volume
compute_client = self.app.client_manager.compute
+ identity_client = self.app.client_manager.identity
def _format_attach(attachments):
"""Return a formatted string of a volume's attached instances
- :param volume: a volume.attachments field
+ :param attachments: a volume.attachments field
:rtype: a string of formatted instances
"""
@@ -282,8 +295,23 @@ class ListVolume(lister.Lister):
# Just forget it if there's any trouble
pass
+ project_id = None
+ if parsed_args.project:
+ project_id = identity_common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain)
+
+ user_id = None
+ if parsed_args.user:
+ user_id = identity_common.find_user(identity_client,
+ parsed_args.user,
+ parsed_args.user_domain)
+
search_opts = {
- 'all_projects': parsed_args.all_projects,
+ 'all_tenants': parsed_args.all_projects,
+ 'project_id': project_id,
+ 'user_id': user_id,
'display_name': parsed_args.name,
'status': parsed_args.status,
}
@@ -298,7 +326,7 @@ class ListVolume(lister.Lister):
) for s in data))
-class SetVolume(show.ShowOne):
+class SetVolume(command.Command):
"""Set volume properties"""
log = logging.getLogger(__name__ + '.SetVolume')
diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py
index 8cca86f9..583e6ed9 100644
--- a/openstackclient/volume/v2/volume_type.py
+++ b/openstackclient/volume/v2/volume_type.py
@@ -73,9 +73,9 @@ class CreateVolumeType(show.ShowOne):
kwargs = {}
if parsed_args.public:
- kwargs['public'] = True
+ kwargs['is_public'] = True
if parsed_args.private:
- kwargs['private'] = True
+ kwargs['is_public'] = False
volume_type = volume_client.volume_types.create(
parsed_args.name,