summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/common/clientmanager.py12
-rw-r--r--openstackclient/common/parseractions.py5
-rw-r--r--openstackclient/common/quota.py29
-rw-r--r--openstackclient/compute/v2/flavor.py29
-rw-r--r--openstackclient/compute/v2/server.py5
-rw-r--r--openstackclient/compute/v2/server_group.py182
-rw-r--r--openstackclient/identity/v3/service.py2
-rw-r--r--openstackclient/network/v2/port.py3
-rw-r--r--openstackclient/network/v2/router.py26
-rw-r--r--openstackclient/network/v2/subnet.py18
-rw-r--r--openstackclient/network/v2/subnet_pool.py50
-rw-r--r--openstackclient/tests/common/test_quota.py11
-rw-r--r--openstackclient/tests/compute/v2/fakes.py100
-rw-r--r--openstackclient/tests/compute/v2/test_flavor.py178
-rw-r--r--openstackclient/tests/compute/v2/test_server.py6
-rw-r--r--openstackclient/tests/compute/v2/test_server_group.py283
-rw-r--r--openstackclient/tests/fakes.py10
-rw-r--r--openstackclient/tests/image/v2/fakes.py6
-rw-r--r--openstackclient/tests/network/v2/fakes.py54
-rw-r--r--openstackclient/tests/network/v2/test_floating_ip.py2
-rw-r--r--openstackclient/tests/network/v2/test_network.py9
-rw-r--r--openstackclient/tests/network/v2/test_router.py25
-rw-r--r--openstackclient/tests/network/v2/test_security_group.py78
-rw-r--r--openstackclient/tests/network/v2/test_security_group_rule.py10
-rw-r--r--openstackclient/tests/network/v2/test_subnet.py2
-rw-r--r--openstackclient/tests/network/v2/test_subnet_pool.py128
-rw-r--r--openstackclient/tests/volume/v1/fakes.py91
-rw-r--r--openstackclient/tests/volume/v1/test_service.py141
-rw-r--r--openstackclient/tests/volume/v2/fakes.py103
-rw-r--r--openstackclient/tests/volume/v2/test_service.py141
-rw-r--r--openstackclient/volume/v1/service.py70
-rw-r--r--openstackclient/volume/v2/service.py70
32 files changed, 1691 insertions, 188 deletions
diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py
index 8b0fb921..9c2b320c 100644
--- a/openstackclient/common/clientmanager.py
+++ b/openstackclient/common/clientmanager.py
@@ -195,6 +195,18 @@ class ClientManager(object):
not self._auth_params.get('user_domain_name')):
self._auth_params['user_domain_id'] = default_domain
+ # NOTE(hieulq): If USER_DOMAIN_NAME, USER_DOMAIN_ID, PROJECT_DOMAIN_ID
+ # or PROJECT_DOMAIN_NAME is present and API_VERSION is 2.0, then
+ # ignore all domain related configs.
+ if (self._api_version.get('identity') == '2.0' and
+ self.auth_plugin_name.endswith('password')):
+ domain_props = ['project_domain_name', 'project_domain_id',
+ 'user_domain_name', 'user_domain_id']
+ for prop in domain_props:
+ if self._auth_params.pop(prop, None) is not None:
+ LOG.warning("Ignoring domain related configs " +
+ prop + " because identity API version is 2.0")
+
# For compatibility until all clients can be updated
if 'project_name' in self._auth_params:
self._project_name = self._auth_params['project_name']
diff --git a/openstackclient/common/parseractions.py b/openstackclient/common/parseractions.py
index c30058c8..77798f90 100644
--- a/openstackclient/common/parseractions.py
+++ b/openstackclient/common/parseractions.py
@@ -155,9 +155,8 @@ class NonNegativeAction(argparse.Action):
"""
def __call__(self, parser, namespace, values, option_string=None):
- try:
- assert(int(values) >= 0)
+ if int(values) >= 0:
setattr(namespace, self.dest, values)
- except Exception:
+ else:
msg = "%s expected a non-negative integer" % (str(option_string))
raise argparse.ArgumentTypeError(msg)
diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py
index b3d4c3b6..b497a44d 100644
--- a/openstackclient/common/quota.py
+++ b/openstackclient/common/quota.py
@@ -145,7 +145,8 @@ class ShowQuota(command.ShowOne):
parser.add_argument(
'project',
metavar='<project/class>',
- help='Show this project or class (name/ID)',
+ nargs='?',
+ help='Show quotas for this project or class (name or ID)',
)
type_group = parser.add_mutually_exclusive_group()
type_group.add_argument(
@@ -164,12 +165,22 @@ class ShowQuota(command.ShowOne):
)
return parser
+ def _get_project(self, parsed_args):
+ if parsed_args.project is not None:
+ identity_client = self.app.client_manager.identity
+ project = utils.find_resource(
+ identity_client.projects,
+ parsed_args.project,
+ ).id
+ elif self.app.client_manager.auth_ref:
+ # Get the project from the current auth
+ project = self.app.client_manager.auth_ref.project_id
+ else:
+ project = None
+ return project
+
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
+ project = self._get_project(parsed_args)
try:
if parsed_args.quota_class:
@@ -189,11 +200,7 @@ class ShowQuota(command.ShowOne):
if parsed_args.quota_class or parsed_args.default:
return {}
if self.app.client_manager.is_network_endpoint_enabled():
- identity_client = self.app.client_manager.identity
- project = utils.find_resource(
- identity_client.projects,
- parsed_args.project,
- ).id
+ project = self._get_project(parsed_args)
return self.app.client_manager.network.get_quota(project)
else:
return {}
diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py
index 29e0e9d4..4b918369 100644
--- a/openstackclient/compute/v2/flavor.py
+++ b/openstackclient/compute/v2/flavor.py
@@ -18,10 +18,29 @@
import six
from openstackclient.common import command
+from openstackclient.common import exceptions
from openstackclient.common import parseractions
from openstackclient.common import utils
+def _find_flavor(compute_client, flavor):
+ try:
+ return compute_client.flavors.get(flavor)
+ except Exception as ex:
+ if type(ex).__name__ == 'NotFound':
+ pass
+ else:
+ raise
+ try:
+ return compute_client.flavors.find(name=flavor, is_public=None)
+ except Exception as ex:
+ if type(ex).__name__ == 'NotFound':
+ msg = "No flavor with a name or ID of '%s' exists." % flavor
+ raise exceptions.CommandError(msg)
+ else:
+ raise
+
+
class CreateFlavor(command.ShowOne):
"""Create new flavor"""
@@ -132,8 +151,7 @@ class DeleteFlavor(command.Command):
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
- flavor = utils.find_resource(compute_client.flavors,
- parsed_args.flavor)
+ flavor = _find_flavor(compute_client, parsed_args.flavor)
compute_client.flavors.delete(flavor.id)
@@ -239,7 +257,7 @@ class SetFlavor(command.Command):
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
- flavor = compute_client.flavors.find(name=parsed_args.flavor)
+ flavor = _find_flavor(compute_client, parsed_args.flavor)
flavor.set_keys(parsed_args.property)
@@ -257,8 +275,7 @@ class ShowFlavor(command.ShowOne):
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
- resource_flavor = utils.find_resource(compute_client.flavors,
- parsed_args.flavor)
+ resource_flavor = _find_flavor(compute_client, parsed_args.flavor)
flavor = resource_flavor._info.copy()
flavor.pop("links", None)
@@ -289,5 +306,5 @@ class UnsetFlavor(command.Command):
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
- flavor = compute_client.flavors.find(name=parsed_args.flavor)
+ flavor = _find_flavor(compute_client, parsed_args.flavor)
flavor.unset_keys(parsed_args.property)
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index bf9f0985..8c533158 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -155,6 +155,11 @@ def _prep_server_detail(compute_client, server):
if 'tenant_id' in info:
info['project_id'] = info.pop('tenant_id')
+ # Map power state num to meanful string
+ if 'OS-EXT-STS:power_state' in info:
+ info['OS-EXT-STS:power_state'] = _format_servers_list_power_state(
+ info['OS-EXT-STS:power_state'])
+
# Remove values that are long and not too useful
info.pop('links', None)
diff --git a/openstackclient/compute/v2/server_group.py b/openstackclient/compute/v2/server_group.py
new file mode 100644
index 00000000..eb5745f5
--- /dev/null
+++ b/openstackclient/compute/v2/server_group.py
@@ -0,0 +1,182 @@
+# Copyright 2016 Huawei, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+"""Compute v2 Server Group action implementations"""
+
+from openstackclient.common import command
+from openstackclient.common import exceptions
+from openstackclient.common import utils
+
+
+_formatters = {
+ 'policies': utils.format_list,
+ 'members': utils.format_list,
+}
+
+
+def _get_columns(info):
+ columns = list(info.keys())
+ if 'metadata' in columns:
+ # NOTE(RuiChen): The metadata of server group is always empty since API
+ # compatible, so hide it in order to avoid confusion.
+ columns.remove('metadata')
+ return tuple(sorted(columns))
+
+
+class CreateServerGroup(command.ShowOne):
+ """Create a new server group."""
+
+ def get_parser(self, prog_name):
+ parser = super(CreateServerGroup, self).get_parser(prog_name)
+ parser.add_argument(
+ 'name',
+ metavar='<name>',
+ help='New server group name',
+ )
+ parser.add_argument(
+ '--policy',
+ metavar='<policy>',
+ action='append',
+ required=True,
+ help='Add a policy to <name> '
+ '(repeat option to add multiple policies)',
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+ info = {}
+ server_group = compute_client.server_groups.create(
+ name=parsed_args.name,
+ policies=parsed_args.policy)
+ info.update(server_group._info)
+
+ columns = _get_columns(info)
+ data = utils.get_dict_properties(info, columns,
+ formatters=_formatters)
+ return columns, data
+
+
+class DeleteServerGroup(command.Command):
+ """Delete an existing server group."""
+
+ def get_parser(self, prog_name):
+ parser = super(DeleteServerGroup, self).get_parser(prog_name)
+ parser.add_argument(
+ 'server_group',
+ metavar='<server-group>',
+ nargs='+',
+ help='server group(s) to delete (name or ID)',
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+ result = 0
+ for group in parsed_args.server_group:
+ try:
+ group_obj = utils.find_resource(compute_client.server_groups,
+ group)
+ compute_client.server_groups.delete(group_obj.id)
+ # Catch all exceptions in order to avoid to block the next deleting
+ except Exception as e:
+ result += 1
+ self.app.log.error(e)
+
+ if result > 0:
+ total = len(parsed_args.server_group)
+ msg = "%s of %s server groups failed to delete." % (result, total)
+ raise exceptions.CommandError(msg)
+
+
+class ListServerGroup(command.Lister):
+ """List all server groups."""
+
+ def get_parser(self, prog_name):
+ parser = super(ListServerGroup, self).get_parser(prog_name)
+ parser.add_argument(
+ '--all-projects',
+ action='store_true',
+ default=False,
+ help='Display information from all projects (admin only)',
+ )
+ parser.add_argument(
+ '--long',
+ action='store_true',
+ default=False,
+ help='List additional fields in output',
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+ data = compute_client.server_groups.list(parsed_args.all_projects)
+
+ if parsed_args.long:
+ column_headers = (
+ 'ID',
+ 'Name',
+ 'Policies',
+ 'Members',
+ 'Project Id',
+ 'User Id',
+ )
+ columns = (
+ 'ID',
+ 'Name',
+ 'Policies',
+ 'Members',
+ 'Project Id',
+ 'User Id',
+ )
+ else:
+ column_headers = columns = (
+ 'ID',
+ 'Name',
+ 'Policies',
+ )
+
+ return (column_headers,
+ (utils.get_item_properties(
+ s, columns,
+ formatters={
+ 'Policies': utils.format_list,
+ 'Members': utils.format_list,
+ }
+ ) for s in data))
+
+
+class ShowServerGroup(command.ShowOne):
+ """Display server group details."""
+
+ def get_parser(self, prog_name):
+ parser = super(ShowServerGroup, self).get_parser(prog_name)
+ parser.add_argument(
+ 'server_group',
+ metavar='<server-group>',
+ help='server group to display (name or ID)',
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ compute_client = self.app.client_manager.compute
+ group = utils.find_resource(compute_client.server_groups,
+ parsed_args.server_group)
+ info = {}
+ info.update(group._info)
+ columns = _get_columns(info)
+ data = utils.get_dict_properties(info, columns,
+ formatters=_formatters)
+ return columns, data
diff --git a/openstackclient/identity/v3/service.py b/openstackclient/identity/v3/service.py
index 355583cc..f43ada56 100644
--- a/openstackclient/identity/v3/service.py
+++ b/openstackclient/identity/v3/service.py
@@ -82,7 +82,7 @@ class DeleteService(command.Command):
parser.add_argument(
'service',
metavar='<service>',
- help='Service to delete (type or ID)',
+ help='Service to delete (type, name or ID)',
)
return parser
diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py
index fbfce4d3..9b6161fd 100644
--- a/openstackclient/network/v2/port.py
+++ b/openstackclient/network/v2/port.py
@@ -249,7 +249,8 @@ class CreatePort(command.ShowOne):
parser.add_argument(
'name',
metavar='<name>',
- help='Name of this port')
+ help=_("Name of this port")
+ )
# TODO(singhj): Add support for extended options:
# qos,security groups,dhcp, address pairs
return parser
diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py
index 56630a23..a32ab5ea 100644
--- a/openstackclient/network/v2/router.py
+++ b/openstackclient/network/v2/router.py
@@ -34,11 +34,20 @@ def _format_external_gateway_info(info):
return ''
+def _format_routes(routes):
+ # Map the route keys to match --route option.
+ for route in routes:
+ if 'nexthop' in route:
+ route['gateway'] = route.pop('nexthop')
+ return utils.format_list_of_dicts(routes)
+
+
_formatters = {
'admin_state_up': _format_admin_state,
'external_gateway_info': _format_external_gateway_info,
'availability_zones': utils.format_list,
'availability_zone_hints': utils.format_list,
+ 'routes': _format_routes,
}
@@ -67,11 +76,6 @@ def _get_attrs(client_manager, parsed_args):
and parsed_args.availability_zone_hints is not None):
attrs['availability_zone_hints'] = parsed_args.availability_zone_hints
- if 'clear_routes' in parsed_args and parsed_args.clear_routes:
- attrs['routes'] = []
- elif 'routes' in parsed_args and parsed_args.routes is not None:
- attrs['routes'] = parsed_args.routes
-
# "router set" command doesn't support setting project.
if 'project' in parsed_args and parsed_args.project is not None:
identity_client = client_manager.identity
@@ -393,7 +397,19 @@ class SetRouter(command.Command):
client = self.app.client_manager.network
obj = client.find_router(parsed_args.router, ignore_missing=False)
+ # Get the common attributes.
attrs = _get_attrs(self.app.client_manager, parsed_args)
+
+ # Get the route attributes.
+ if parsed_args.clear_routes:
+ attrs['routes'] = []
+ elif parsed_args.routes is not None:
+ # Map the route keys and append to the current routes.
+ # The REST API will handle route validation and duplicates.
+ for route in parsed_args.routes:
+ route['nexthop'] = route.pop('gateway')
+ attrs['routes'] = obj.routes + parsed_args.routes
+
if attrs == {}:
msg = "Nothing specified to be set"
raise exceptions.CommandError(msg)
diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py
index 715e6620..fb441cbf 100644
--- a/openstackclient/network/v2/subnet.py
+++ b/openstackclient/network/v2/subnet.py
@@ -14,8 +14,6 @@
"""Subnet action implementations"""
import copy
-from json.encoder import JSONEncoder
-
from openstackclient.common import command
from openstackclient.common import exceptions
from openstackclient.common import parseractions
@@ -31,10 +29,8 @@ def _format_allocation_pools(data):
def _format_host_routes(data):
- try:
- return '\n'.join([JSONEncoder().encode(route) for route in data])
- except (TypeError, KeyError):
- return ''
+ # Map the host route keys to match --host-route option.
+ return utils.format_list_of_dicts(convert_entries_to_gateway(data))
_formatters = {
@@ -89,8 +85,9 @@ def convert_entries_to_nexthop(entries):
# Change 'gateway' entry to 'nexthop'
changed_entries = copy.deepcopy(entries)
for entry in changed_entries:
- entry['nexthop'] = entry['gateway']
- del entry['gateway']
+ if 'gateway' in entry:
+ entry['nexthop'] = entry['gateway']
+ del entry['gateway']
return changed_entries
@@ -99,8 +96,9 @@ def convert_entries_to_gateway(entries):
# Change 'nexthop' entry to 'gateway'
changed_entries = copy.deepcopy(entries)
for entry in changed_entries:
- entry['gateway'] = entry['nexthop']
- del entry['nexthop']
+ if 'nexthop' in entry:
+ entry['gateway'] = entry['nexthop']
+ del entry['nexthop']
return changed_entries
diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py
index 482b5ecf..f1174dda 100644
--- a/openstackclient/network/v2/subnet_pool.py
+++ b/openstackclient/network/v2/subnet_pool.py
@@ -55,6 +55,16 @@ def _get_attrs(client_manager, parsed_args):
if 'no_address_scope' in parsed_args and parsed_args.no_address_scope:
attrs['address_scope_id'] = None
+ if parsed_args.default:
+ attrs['is_default'] = True
+ if parsed_args.no_default:
+ attrs['is_default'] = False
+
+ if 'share' in parsed_args and parsed_args.share:
+ attrs['shared'] = True
+ if 'no_share' in parsed_args and parsed_args.no_share:
+ attrs['shared'] = False
+
# "subnet pool set" command doesn't support setting project.
if 'project' in parsed_args and parsed_args.project is not None:
identity_client = client_manager.identity
@@ -68,18 +78,20 @@ def _get_attrs(client_manager, parsed_args):
return attrs
-def _add_prefix_options(parser):
+def _add_prefix_options(parser, for_create=False):
parser.add_argument(
'--pool-prefix',
metavar='<pool-prefix>',
dest='prefixes',
action='append',
+ required=for_create,
help=_("Set subnet pool prefixes (in CIDR notation) "
"(repeat option to set multiple prefixes)")
)
parser.add_argument(
'--default-prefix-length',
metavar='<default-prefix-length>',
+ type=int,
action=parseractions.NonNegativeAction,
help=_("Set subnet pool default prefix length")
)
@@ -87,16 +99,32 @@ def _add_prefix_options(parser):
'--min-prefix-length',
metavar='<min-prefix-length>',
action=parseractions.NonNegativeAction,
+ type=int,
help=_("Set subnet pool minimum prefix length")
)
parser.add_argument(
'--max-prefix-length',
metavar='<max-prefix-length>',
+ type=int,
action=parseractions.NonNegativeAction,
help=_("Set subnet pool maximum prefix length")
)
+def _add_default_options(parser):
+ default_group = parser.add_mutually_exclusive_group()
+ default_group.add_argument(
+ '--default',
+ action='store_true',
+ help=_("Set this as a default subnet pool"),
+ )
+ default_group.add_argument(
+ '--no-default',
+ action='store_true',
+ help=_("Set this as a non-default subnet pool"),
+ )
+
+
class CreateSubnetPool(command.ShowOne):
"""Create subnet pool"""
@@ -107,7 +135,7 @@ class CreateSubnetPool(command.ShowOne):
metavar='<name>',
help=_("Name of the new subnet pool")
)
- _add_prefix_options(parser)
+ _add_prefix_options(parser, for_create=True)
parser.add_argument(
'--project',
metavar='<project>',
@@ -121,6 +149,18 @@ class CreateSubnetPool(command.ShowOne):
"(name or ID), prefixes must be unique across address "
"scopes")
)
+ _add_default_options(parser)
+ shared_group = parser.add_mutually_exclusive_group()
+ shared_group.add_argument(
+ '--share',
+ action='store_true',
+ help=_("Set this subnet pool as shared"),
+ )
+ shared_group.add_argument(
+ '--no-share',
+ action='store_true',
+ help=_("Set this subnet pool as not shared"),
+ )
return parser
def take_action(self, parsed_args):
@@ -176,6 +216,8 @@ class ListSubnetPool(command.Lister):
'Prefixes',
'Default Prefix Length',
'Address Scope',
+ 'Default Subnet Pool',
+ 'Shared',
)
columns = (
'id',
@@ -183,6 +225,8 @@ class ListSubnetPool(command.Lister):
'prefixes',
'default_prefixlen',
'address_scope_id',
+ 'is_default',
+ 'shared',
)
else:
headers = (
@@ -232,6 +276,8 @@ class SetSubnetPool(command.Command):
action='store_true',
help=_("Remove address scope associated with the subnet pool")
)
+ _add_default_options(parser)
+
return parser
def take_action(self, parsed_args):
diff --git a/openstackclient/tests/common/test_quota.py b/openstackclient/tests/common/test_quota.py
index edf29c9b..ba7ee469 100644
--- a/openstackclient/tests/common/test_quota.py
+++ b/openstackclient/tests/common/test_quota.py
@@ -59,6 +59,7 @@ class TestQuota(compute_fakes.TestComputev2):
self.service_catalog_mock = \
self.app.client_manager.auth_ref.service_catalog
self.service_catalog_mock.reset_mock()
+ self.app.client_manager.auth_ref.project_id = identity_fakes.project_id
class TestQuotaSet(TestQuota):
@@ -304,3 +305,13 @@ class TestQuotaShow(TestQuota):
identity_fakes.project_id)
self.volume_quotas_class_mock.get.assert_called_with(
identity_fakes.project_id)
+
+ def test_quota_show_no_project(self):
+ parsed_args = self.check_parser(self.cmd, [], [])
+
+ self.cmd.take_action(parsed_args)
+
+ self.quotas_mock.get.assert_called_with(identity_fakes.project_id)
+ self.volume_quotas_mock.get.assert_called_with(
+ identity_fakes.project_id)
+ self.network.get_quota.assert_called_with(identity_fakes.project_id)
diff --git a/openstackclient/tests/compute/v2/fakes.py b/openstackclient/tests/compute/v2/fakes.py
index 7f39bad0..b3f3fb49 100644
--- a/openstackclient/tests/compute/v2/fakes.py
+++ b/openstackclient/tests/compute/v2/fakes.py
@@ -100,8 +100,7 @@ class FakeAggregate(object):
:return:
A FakeResource object, with id and other attributes
"""
- if attrs is None:
- attrs = {}
+ attrs = attrs or {}
# Set default attribute
aggregate_info = {
@@ -177,6 +176,9 @@ class FakeComputev2Client(object):
self.hosts = mock.Mock()
self.hosts.resource_class = fakes.FakeResource(None, {})
+ self.server_groups = mock.Mock()
+ self.server_groups.resource_class = fakes.FakeResource(None, {})
+
self.auth_token = kwargs['token']
self.management_url = kwargs['endpoint']
@@ -217,7 +219,7 @@ class FakeHypervisor(object):
"""Fake one or more hypervisor."""
@staticmethod
- def create_one_hypervisor(attrs={}):
+ def create_one_hypervisor(attrs=None):
"""Create a fake hypervisor.
:param Dictionary attrs:
@@ -225,6 +227,8 @@ class FakeHypervisor(object):
:return:
A FakeResource object, with id, hypervisor_hostname, and so on
"""
+ attrs = attrs or {}
+
# Set default attributes.
hypervisor_info = {
'id': 'hypervisor-id-' + uuid.uuid4().hex,
@@ -263,7 +267,7 @@ class FakeHypervisor(object):
return hypervisor
@staticmethod
- def create_hypervisors(attrs={}, count=2):
+ def create_hypervisors(attrs=None, count=2):
"""Create multiple fake hypervisors.
:param Dictionary attrs:
@@ -284,7 +288,7 @@ class FakeHypervisorStats(object):
"""Fake one or more hypervisor stats."""
@staticmethod
- def create_one_hypervisor_stats(attrs={}):
+ def create_one_hypervisor_stats(attrs=None):
"""Create a fake hypervisor stats.
:param Dictionary attrs:
@@ -292,6 +296,8 @@ class FakeHypervisorStats(object):
:return:
A FakeResource object, with id, hypervisor_hostname, and so on
"""
+ attrs = attrs or {}
+
# Set default attributes.
stats_info = {
'count': 2,
@@ -319,7 +325,7 @@ class FakeHypervisorStats(object):
return hypervisor_stats
@staticmethod
- def create_hypervisors_stats(attrs={}, count=2):
+ def create_hypervisors_stats(attrs=None, count=2):
"""Create multiple fake hypervisors stats.
:param Dictionary attrs:
@@ -349,8 +355,7 @@ class FakeSecurityGroup(object):
:return:
A FakeResource object, with id, name, etc.
"""
- if attrs is None:
- attrs = {}
+ attrs = attrs or {}
# Set default attributes.
security_group_attrs = {
@@ -400,8 +405,7 @@ class FakeSecurityGroupRule(object):
:return:
A FakeResource object, with id, etc.
"""
- if attrs is None:
- attrs = {}
+ attrs = attrs or {}
# Set default attributes.
security_group_rule_attrs = {
@@ -445,7 +449,7 @@ class FakeServer(object):
"""Fake one or more compute servers."""
@staticmethod
- def create_one_server(attrs={}, methods={}):
+ def create_one_server(attrs=None, methods=None):
"""Create a fake server.
:param Dictionary attrs:
@@ -455,6 +459,9 @@ class FakeServer(object):
:return:
A FakeResource object, with id, name, metadata
"""
+ attrs = attrs or {}
+ methods = methods or {}
+
# Set default attributes.
server_info = {
'id': 'server-id-' + uuid.uuid4().hex,
@@ -465,7 +472,8 @@ class FakeServer(object):
},
'flavor': {
'id': 'flavor-id-' + uuid.uuid4().hex,
- }
+ },
+ 'OS-EXT-STS:power_state': 1,
}
# Overwrite default attributes.
@@ -477,7 +485,7 @@ class FakeServer(object):
return server
@staticmethod
- def create_servers(attrs={}, methods={}, count=2):
+ def create_servers(attrs=None, methods=None, count=2):
"""Create multiple fake servers.
:param Dictionary attrs:
@@ -527,8 +535,7 @@ class FakeFlavor(object):
:return:
A FakeResource object, with id, name, ram, vcpus, properties
"""
- if attrs is None:
- attrs = {}
+ attrs = attrs or {}
# Set default attributes.
flavor_info = {
@@ -537,8 +544,8 @@ class FakeFlavor(object):
'ram': 8192,
'vcpus': 4,
'disk': 128,
- 'swap': '',
- 'rxtx_factor': '1.0',
+ 'swap': 0,
+ 'rxtx_factor': 1.0,
'OS-FLV-DISABLED:disabled': False,
'os-flavor-access:is_public': True,
'OS-FLV-EXT-DATA:ephemeral': 0,
@@ -566,7 +573,7 @@ class FakeFlavor(object):
return flavor
@staticmethod
- def create_flavors(attrs={}, count=2):
+ def create_flavors(attrs=None, count=2):
"""Create multiple fake flavors.
:param Dictionary attrs:
@@ -614,10 +621,9 @@ class FakeKeypair(object):
:return:
A FakeResource
"""
- # Set default attributes.
- if attrs is None:
- attrs = {}
+ attrs = attrs or {}
+ # Set default attributes.
keypair_info = {
'name': 'keypair-name-' + uuid.uuid4().hex,
'fingerprint': 'dummy',
@@ -658,7 +664,7 @@ class FakeAvailabilityZone(object):
"""Fake one or more compute availability zones (AZs)."""
@staticmethod
- def create_one_availability_zone(attrs={}):
+ def create_one_availability_zone(attrs=None):
"""Create a fake AZ.
:param Dictionary attrs:
@@ -666,6 +672,8 @@ class FakeAvailabilityZone(object):
:return:
A FakeResource object with zoneName, zoneState, etc.
"""
+ attrs = attrs or {}
+
# Set default attributes.
host_name = uuid.uuid4().hex
service_name = uuid.uuid4().hex
@@ -689,7 +697,7 @@ class FakeAvailabilityZone(object):
return availability_zone
@staticmethod
- def create_availability_zones(attrs={}, count=2):
+ def create_availability_zones(attrs=None, count=2):
"""Create multiple fake AZs.
:param Dictionary attrs:
@@ -712,7 +720,7 @@ class FakeFloatingIP(object):
"""Fake one or more floating ip."""
@staticmethod
- def create_one_floating_ip(attrs={}):
+ def create_one_floating_ip(attrs=None):
"""Create a fake floating ip.
:param Dictionary attrs:
@@ -720,6 +728,8 @@ class FakeFloatingIP(object):
:return:
A FakeResource object, with id, ip, and so on
"""
+ attrs = attrs or {}
+
# Set default attributes.
floating_ip_attrs = {
'id': 'floating-ip-id-' + uuid.uuid4().hex,
@@ -739,7 +749,7 @@ class FakeFloatingIP(object):
return floating_ip
@staticmethod
- def create_floating_ips(attrs={}, count=2):
+ def create_floating_ips(attrs=None, count=2):
"""Create multiple fake floating ips.
:param Dictionary attrs:
@@ -778,7 +788,7 @@ class FakeNetwork(object):
"""Fake one or more networks."""
@staticmethod
- def create_one_network(attrs={}):
+ def create_one_network(attrs=None):
"""Create a fake network.
:param Dictionary attrs:
@@ -786,6 +796,8 @@ class FakeNetwork(object):
:return:
A FakeResource object, with id, label, cidr and so on
"""
+ attrs = attrs or {}
+
# Set default attributes.
network_attrs = {
'bridge': 'br100',
@@ -831,7 +843,7 @@ class FakeNetwork(object):
return network
@staticmethod
- def create_networks(attrs={}, count=2):
+ def create_networks(attrs=None, count=2):
"""Create multiple fake networks.
:param Dictionary attrs:
@@ -860,8 +872,7 @@ class FakeHost(object):
:return:
A FakeResource object, with id and other attributes
"""
- if attrs is None:
- attrs = {}
+ attrs = attrs or {}
# Set default attributes.
host_info = {
@@ -899,3 +910,34 @@ class FakeHost(object):
info=copy.deepcopy(host_info),
loaded=True)
return host
+
+
+class FakeServerGroup(object):
+ """Fake one server group"""
+
+ @staticmethod
+ def create_one_server_group(attrs=None):
+ """Create a fake server group
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object, with id and other attributes
+ """
+ if attrs is None:
+ attrs = {}
+
+ server_group_info = {
+ 'id': 'server-group-id-' + uuid.uuid4().hex,
+ 'members': [],
+ 'metadata': {},
+ 'name': 'server-group-name-' + uuid.uuid4().hex,
+ 'policies': [],
+ 'project_id': 'server-group-project-id-' + uuid.uuid4().hex,
+ 'user_id': 'server-group-user-id-' + uuid.uuid4().hex,
+ }
+ server_group_info.update(attrs)
+ server_group = fakes.FakeResource(
+ info=copy.deepcopy(server_group_info),
+ loaded=True)
+ return server_group
diff --git a/openstackclient/tests/compute/v2/test_flavor.py b/openstackclient/tests/compute/v2/test_flavor.py
index 03ca8807..6f507b16 100644
--- a/openstackclient/tests/compute/v2/test_flavor.py
+++ b/openstackclient/tests/compute/v2/test_flavor.py
@@ -30,6 +30,166 @@ class TestFlavor(compute_fakes.TestComputev2):
self.flavors_mock.reset_mock()
+class TestFlavorCreate(TestFlavor):
+
+ flavor = compute_fakes.FakeFlavor.create_one_flavor(
+ attrs={'links': 'flavor-links'})
+
+ columns = (
+ 'OS-FLV-DISABLED:disabled',
+ 'OS-FLV-EXT-DATA:ephemeral',
+ 'disk',
+ 'id',
+ 'name',
+ 'os-flavor-access:is_public',
+ 'ram',
+ 'rxtx_factor',
+ 'swap',
+ 'vcpus',
+ )
+ data = (
+ flavor.disabled,
+ flavor.ephemeral,
+ flavor.disk,
+ flavor.id,
+ flavor.name,
+ flavor.is_public,
+ flavor.ram,
+ flavor.rxtx_factor,
+ flavor.swap,
+ flavor.vcpus,
+ )
+
+ def setUp(self):
+ super(TestFlavorCreate, self).setUp()
+
+ self.flavors_mock.create.return_value = self.flavor
+ self.cmd = flavor.CreateFlavor(self.app, None)
+
+ def test_flavor_create_default_options(self):
+
+ arglist = [
+ self.flavor.name
+ ]
+ verifylist = [
+ ('name', self.flavor.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ default_args = (
+ self.flavor.name,
+ 256,
+ 1,
+ 0,
+ 'auto',
+ 0,
+ 0,
+ 1.0,
+ True
+ )
+ columns, data = self.cmd.take_action(parsed_args)
+ self.flavors_mock.create.assert_called_once_with(*default_args)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_flavor_create_all_options(self):
+
+ arglist = [
+ self.flavor.name,
+ '--id', self.flavor.id,
+ '--ram', str(self.flavor.ram),
+ '--disk', str(self.flavor.disk),
+ '--ephemeral', str(self.flavor.ephemeral),
+ '--swap', str(self.flavor.swap),
+ '--vcpus', str(self.flavor.vcpus),
+ '--rxtx-factor', str(self.flavor.rxtx_factor),
+ '--public',
+ ]
+ verifylist = [
+ ('name', self.flavor.name),
+ ('id', self.flavor.id),
+ ('ram', self.flavor.ram),
+ ('disk', self.flavor.disk),
+ ('ephemeral', self.flavor.ephemeral),
+ ('swap', self.flavor.swap),
+ ('vcpus', self.flavor.vcpus),
+ ('rxtx_factor', self.flavor.rxtx_factor),
+ ('public', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ args = (
+ self.flavor.name,
+ self.flavor.ram,
+ self.flavor.vcpus,
+ self.flavor.disk,
+ self.flavor.id,
+ self.flavor.ephemeral,
+ self.flavor.swap,
+ self.flavor.rxtx_factor,
+ self.flavor.is_public,
+ )
+ columns, data = self.cmd.take_action(parsed_args)
+ self.flavors_mock.create.assert_called_once_with(*args)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_flavor_create_other_options(self):
+
+ self.flavor.is_public = False
+ arglist = [
+ self.flavor.name,
+ '--id', self.flavor.id,
+ '--ram', str(self.flavor.ram),
+ '--disk', str(self.flavor.disk),
+ '--ephemeral', str(self.flavor.ephemeral),
+ '--swap', str(self.flavor.swap),
+ '--vcpus', str(self.flavor.vcpus),
+ '--rxtx-factor', str(self.flavor.rxtx_factor),
+ '--private',
+ ]
+ verifylist = [
+ ('name', self.flavor.name),
+ ('id', self.flavor.id),
+ ('ram', self.flavor.ram),
+ ('disk', self.flavor.disk),
+ ('ephemeral', self.flavor.ephemeral),
+ ('swap', self.flavor.swap),
+ ('vcpus', self.flavor.vcpus),
+ ('rxtx_factor', self.flavor.rxtx_factor),
+ ('public', False),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ args = (
+ self.flavor.name,
+ self.flavor.ram,
+ self.flavor.vcpus,
+ self.flavor.disk,
+ self.flavor.id,
+ self.flavor.ephemeral,
+ self.flavor.swap,
+ self.flavor.rxtx_factor,
+ self.flavor.is_public,
+ )
+ columns, data = self.cmd.take_action(parsed_args)
+ self.flavors_mock.create.assert_called_once_with(*args)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_flavor_create_no_options(self):
+ arglist = []
+ verifylist = None
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser,
+ self.cmd,
+ arglist,
+ verifylist)
+
+
class TestFlavorDelete(TestFlavor):
flavor = compute_fakes.FakeFlavor.create_one_flavor()
@@ -273,7 +433,7 @@ class TestFlavorSet(TestFlavor):
super(TestFlavorSet, self).setUp()
self.flavors_mock.find.return_value = self.flavor
-
+ self.flavors_mock.get.side_effect = exceptions.NotFound(None)
self.cmd = flavor.SetFlavor(self.app, None)
def test_flavor_set(self):
@@ -288,8 +448,8 @@ class TestFlavorSet(TestFlavor):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
-
- self.flavors_mock.find.assert_called_with(name='baremetal')
+ self.flavors_mock.find.assert_called_with(name=parsed_args.flavor,
+ is_public=None)
self.assertIsNone(result)
@@ -329,9 +489,9 @@ class TestFlavorShow(TestFlavor):
def setUp(self):
super(TestFlavorShow, self).setUp()
- # Return value of utils.find_resource()
- self.flavors_mock.get.return_value = self.flavor
-
+ # Return value of _find_resource()
+ self.flavors_mock.find.return_value = self.flavor
+ self.flavors_mock.get.side_effect = exceptions.NotFound(None)
self.cmd = flavor.ShowFlavor(self.app, None)
def test_show_no_options(self):
@@ -367,7 +527,7 @@ class TestFlavorUnset(TestFlavor):
super(TestFlavorUnset, self).setUp()
self.flavors_mock.find.return_value = self.flavor
-
+ self.flavors_mock.get.side_effect = exceptions.NotFound(None)
self.cmd = flavor.UnsetFlavor(self.app, None)
def test_flavor_unset(self):
@@ -382,6 +542,6 @@ class TestFlavorUnset(TestFlavor):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
-
- self.flavors_mock.find.assert_called_with(name='baremetal')
+ self.flavors_mock.find.assert_called_with(name=parsed_args.flavor,
+ is_public=None)
self.assertIsNone(result)
diff --git a/openstackclient/tests/compute/v2/test_server.py b/openstackclient/tests/compute/v2/test_server.py
index 17681672..903d0bff 100644
--- a/openstackclient/tests/compute/v2/test_server.py
+++ b/openstackclient/tests/compute/v2/test_server.py
@@ -89,6 +89,7 @@ class TestServer(compute_fakes.TestComputev2):
class TestServerCreate(TestServer):
columns = (
+ 'OS-EXT-STS:power_state',
'addresses',
'flavor',
'id',
@@ -100,6 +101,8 @@ class TestServerCreate(TestServer):
def datalist(self):
datalist = (
+ server._format_servers_list_power_state(
+ getattr(self.new_server, 'OS-EXT-STS:power_state')),
'',
self.flavor.name + ' (' + self.new_server.flavor.get('id') + ')',
self.new_server.id,
@@ -146,7 +149,6 @@ class TestServerCreate(TestServer):
('server_name', self.new_server.name),
]
- # Missing required args should bail here
self.assertRaises(utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
@@ -1476,6 +1478,8 @@ class TestServerGeneral(TestServer):
'image': u'%s (%s)' % (_image.name, _image.id),
'project_id': u'tenant-id-xxx',
'properties': '',
+ 'OS-EXT-STS:power_state': server._format_servers_list_power_state(
+ getattr(_server, 'OS-EXT-STS:power_state')),
}
# Call _prep_server_detail().
diff --git a/openstackclient/tests/compute/v2/test_server_group.py b/openstackclient/tests/compute/v2/test_server_group.py
new file mode 100644
index 00000000..70ff23f9
--- /dev/null
+++ b/openstackclient/tests/compute/v2/test_server_group.py
@@ -0,0 +1,283 @@
+# Copyright 2016 Huawei, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+import mock
+
+from openstackclient.common import exceptions
+from openstackclient.common import utils
+from openstackclient.compute.v2 import server_group
+from openstackclient.tests.compute.v2 import fakes as compute_fakes
+from openstackclient.tests import utils as tests_utils
+
+
+class TestServerGroup(compute_fakes.TestComputev2):
+
+ fake_server_group = compute_fakes.FakeServerGroup.create_one_server_group()
+
+ columns = (
+ 'id',
+ 'members',
+ 'name',
+ 'policies',
+ 'project_id',
+ 'user_id',
+ )
+
+ data = (
+ fake_server_group.id,
+ utils.format_list(fake_server_group.members),
+ fake_server_group.name,
+ utils.format_list(fake_server_group.policies),
+ fake_server_group.project_id,
+ fake_server_group.user_id,
+ )
+
+ def setUp(self):
+ super(TestServerGroup, self).setUp()
+
+ # Get a shortcut to the ServerGroupsManager Mock
+ self.server_groups_mock = self.app.client_manager.compute.server_groups
+ self.server_groups_mock.reset_mock()
+
+
+class TestServerGroupCreate(TestServerGroup):
+
+ def setUp(self):
+ super(TestServerGroupCreate, self).setUp()
+
+ self.server_groups_mock.create.return_value = self.fake_server_group
+ self.cmd = server_group.CreateServerGroup(self.app, None)
+
+ def test_server_group_create(self):
+ arglist = [
+ '--policy', 'affinity',
+ 'affinity_group',
+ ]
+ verifylist = [
+ ('policy', ['affinity']),
+ ('name', 'affinity_group'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+ self.server_groups_mock.create.assert_called_once_with(
+ name=parsed_args.name,
+ policies=parsed_args.policy,
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_server_group_create_with_multiple_policies(self):
+ arglist = [
+ '--policy', 'affinity',
+ '--policy', 'soft-affinity',
+ 'affinity_group',
+ ]
+ verifylist = [
+ ('policy', ['affinity', 'soft-affinity']),
+ ('name', 'affinity_group'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+ self.server_groups_mock.create.assert_called_once_with(
+ name=parsed_args.name,
+ policies=parsed_args.policy,
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_server_group_create_no_policy(self):
+ arglist = [
+ 'affinity_group',
+ ]
+ verifylist = None
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser,
+ self.cmd,
+ arglist,
+ verifylist)
+
+
+class TestServerGroupDelete(TestServerGroup):
+
+ def setUp(self):
+ super(TestServerGroupDelete, self).setUp()
+
+ self.server_groups_mock.get.return_value = self.fake_server_group
+ self.cmd = server_group.DeleteServerGroup(self.app, None)
+
+ def test_server_group_delete(self):
+ arglist = [
+ 'affinity_group',
+ ]
+ verifylist = [
+ ('server_group', ['affinity_group']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ self.server_groups_mock.get.assert_called_once_with('affinity_group')
+ self.server_groups_mock.delete.assert_called_once_with(
+ self.fake_server_group.id
+ )
+ self.assertIsNone(result)
+
+ def test_server_group_multiple_delete(self):
+ arglist = [
+ 'affinity_group',
+ 'anti_affinity_group'
+ ]
+ verifylist = [
+ ('server_group', ['affinity_group', 'anti_affinity_group']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ self.server_groups_mock.get.assert_any_call('affinity_group')
+ self.server_groups_mock.get.assert_any_call('anti_affinity_group')
+ self.server_groups_mock.delete.assert_called_with(
+ self.fake_server_group.id
+ )
+ self.assertEqual(2, self.server_groups_mock.get.call_count)
+ self.assertEqual(2, self.server_groups_mock.delete.call_count)
+ self.assertIsNone(result)
+
+ def test_server_group_delete_no_input(self):
+ arglist = []
+ verifylist = None
+ self.assertRaises(tests_utils.ParserException,
+ self.check_parser,
+ self.cmd,
+ arglist,
+ verifylist)
+
+ def test_server_group_multiple_delete_with_exception(self):
+ arglist = [
+ 'affinity_group',
+ 'anti_affinity_group'
+ ]
+ verifylist = [
+ ('server_group', ['affinity_group', 'anti_affinity_group']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ find_mock_result = [self.fake_server_group, exceptions.CommandError]
+ with mock.patch.object(utils, 'find_resource',
+ side_effect=find_mock_result) as find_mock:
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 server groups failed to delete.',
+ str(e))
+
+ find_mock.assert_any_call(self.server_groups_mock,
+ 'affinity_group')
+ find_mock.assert_any_call(self.server_groups_mock,
+ 'anti_affinity_group')
+
+ self.assertEqual(2, find_mock.call_count)
+ self.server_groups_mock.delete.assert_called_once_with(
+ self.fake_server_group.id
+ )
+
+
+class TestServerGroupList(TestServerGroup):
+
+ list_columns = (
+ 'ID',
+ 'Name',
+ 'Policies',
+ )
+
+ list_columns_long = (
+ 'ID',
+ 'Name',
+ 'Policies',
+ 'Members',
+ 'Project Id',
+ 'User Id',
+ )
+
+ list_data = ((
+ TestServerGroup.fake_server_group.id,
+ TestServerGroup.fake_server_group.name,
+ utils.format_list(TestServerGroup.fake_server_group.policies),
+ ),)
+
+ list_data_long = ((
+ TestServerGroup.fake_server_group.id,
+ TestServerGroup.fake_server_group.name,
+ utils.format_list(TestServerGroup.fake_server_group.policies),
+ utils.format_list(TestServerGroup.fake_server_group.members),
+ TestServerGroup.fake_server_group.project_id,
+ TestServerGroup.fake_server_group.user_id,
+ ),)
+
+ def setUp(self):
+ super(TestServerGroupList, self).setUp()
+
+ self.server_groups_mock.list.return_value = [self.fake_server_group]
+ self.cmd = server_group.ListServerGroup(self.app, None)
+
+ def test_server_group_list(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.server_groups_mock.list.assert_called_once_with(False)
+
+ self.assertEqual(self.list_columns, columns)
+ self.assertEqual(self.list_data, tuple(data))
+
+ def test_server_group_list_with_all_projects_and_long(self):
+ arglist = [
+ '--all-projects',
+ '--long',
+ ]
+ verifylist = [
+ ('all_projects', True),
+ ('long', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+ self.server_groups_mock.list.assert_called_once_with(True)
+
+ self.assertEqual(self.list_columns_long, columns)
+ self.assertEqual(self.list_data_long, tuple(data))
+
+
+class TestServerGroupShow(TestServerGroup):
+
+ def setUp(self):
+ super(TestServerGroupShow, self).setUp()
+
+ self.server_groups_mock.get.return_value = self.fake_server_group
+ self.cmd = server_group.ShowServerGroup(self.app, None)
+
+ def test_server_group_show(self):
+ arglist = [
+ 'affinity_group',
+ ]
+ verifylist = [
+ ('server_group', 'affinity_group'),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
diff --git a/openstackclient/tests/fakes.py b/openstackclient/tests/fakes.py
index f0cebb06..46f983dc 100644
--- a/openstackclient/tests/fakes.py
+++ b/openstackclient/tests/fakes.py
@@ -142,7 +142,7 @@ class FakeModule(object):
class FakeResource(object):
- def __init__(self, manager=None, info={}, loaded=False, methods={}):
+ def __init__(self, manager=None, info=None, loaded=False, methods=None):
"""Set attributes and methods for a resource.
:param manager:
@@ -154,6 +154,9 @@ class FakeResource(object):
:param Dictionary methods:
A dictionary with all methods
"""
+ info = info or {}
+ methods = methods or {}
+
self.__name__ = type(self).__name__
self.manager = manager
self._info = info
@@ -189,9 +192,12 @@ class FakeResource(object):
class FakeResponse(requests.Response):
- def __init__(self, headers={}, status_code=200, data=None, encoding=None):
+ def __init__(self, headers=None, status_code=200,
+ data=None, encoding=None):
super(FakeResponse, self).__init__()
+ headers = headers or {}
+
self.status_code = status_code
self.headers.update(headers)
diff --git a/openstackclient/tests/image/v2/fakes.py b/openstackclient/tests/image/v2/fakes.py
index 3555d2d4..f90d846d 100644
--- a/openstackclient/tests/image/v2/fakes.py
+++ b/openstackclient/tests/image/v2/fakes.py
@@ -181,7 +181,7 @@ class FakeImage(object):
"""
@staticmethod
- def create_one_image(attrs={}):
+ def create_one_image(attrs=None):
"""Create a fake image.
:param Dictionary attrs:
@@ -190,6 +190,8 @@ class FakeImage(object):
A FakeResource object with id, name, owner, protected,
visibility and tags attrs
"""
+ attrs = attrs or {}
+
# Set default attribute
image_info = {
'id': 'image-id' + uuid.uuid4().hex,
@@ -210,7 +212,7 @@ class FakeImage(object):
return image
@staticmethod
- def create_images(attrs={}, count=2):
+ def create_images(attrs=None, count=2):
"""Create multiple fake images.
:param Dictionary attrs:
diff --git a/openstackclient/tests/network/v2/fakes.py b/openstackclient/tests/network/v2/fakes.py
index 413ec7d2..7c4604bd 100644
--- a/openstackclient/tests/network/v2/fakes.py
+++ b/openstackclient/tests/network/v2/fakes.py
@@ -83,8 +83,7 @@ class FakeAddressScope(object):
:return:
A FakeResource object with name, id, etc.
"""
- if attrs is None:
- attrs = {}
+ attrs = attrs or {}
# Set default attributes.
address_scope_attrs = {
@@ -130,7 +129,7 @@ class FakeAvailabilityZone(object):
"""Fake one or more network availability zones (AZs)."""
@staticmethod
- def create_one_availability_zone(attrs={}):
+ def create_one_availability_zone(attrs=None):
"""Create a fake AZ.
:param Dictionary attrs:
@@ -138,6 +137,8 @@ class FakeAvailabilityZone(object):
:return:
A FakeResource object with name, state, etc.
"""
+ attrs = attrs or {}
+
# Set default attributes.
availability_zone = {
'name': uuid.uuid4().hex,
@@ -154,7 +155,7 @@ class FakeAvailabilityZone(object):
return availability_zone
@staticmethod
- def create_availability_zones(attrs={}, count=2):
+ def create_availability_zones(attrs=None, count=2):
"""Create multiple fake AZs.
:param Dictionary attrs:
@@ -177,7 +178,7 @@ class FakeNetwork(object):
"""Fake one or more networks."""
@staticmethod
- def create_one_network(attrs={}):
+ def create_one_network(attrs=None):
"""Create a fake network.
:param Dictionary attrs:
@@ -186,6 +187,8 @@ class FakeNetwork(object):
A FakeResource object, with id, name, admin_state_up,
router_external, status, subnets, tenant_id
"""
+ attrs = attrs or {}
+
# Set default attributes.
network_attrs = {
'id': 'network-id-' + uuid.uuid4().hex,
@@ -214,7 +217,7 @@ class FakeNetwork(object):
return network
@staticmethod
- def create_networks(attrs={}, count=2):
+ def create_networks(attrs=None, count=2):
"""Create multiple fake networks.
:param Dictionary attrs:
@@ -254,16 +257,16 @@ class FakePort(object):
"""Fake one or more ports."""
@staticmethod
- def create_one_port(attrs={}):
+ def create_one_port(attrs=None):
"""Create a fake port.
:param Dictionary attrs:
A dictionary with all attributes
- :param Dictionary methods:
- A dictionary with all methods
:return:
A FakeResource object, with id, name, etc.
"""
+ attrs = attrs or {}
+
# Set default attributes.
port_attrs = {
'admin_state_up': True,
@@ -306,7 +309,7 @@ class FakePort(object):
return port
@staticmethod
- def create_ports(attrs={}, count=2):
+ def create_ports(attrs=None, count=2):
"""Create multiple fake ports.
:param Dictionary attrs:
@@ -346,7 +349,7 @@ class FakeRouter(object):
"""Fake one or more routers."""
@staticmethod
- def create_one_router(attrs={}):
+ def create_one_router(attrs=None):
"""Create a fake router.
:param Dictionary attrs:
@@ -355,6 +358,8 @@ class FakeRouter(object):
A FakeResource object, with id, name, admin_state_up,
status, tenant_id
"""
+ attrs = attrs or {}
+
# Set default attributes.
router_attrs = {
'id': 'router-id-' + uuid.uuid4().hex,
@@ -382,7 +387,7 @@ class FakeRouter(object):
return router
@staticmethod
- def create_routers(attrs={}, count=2):
+ def create_routers(attrs=None, count=2):
"""Create multiple fake routers.
:param Dictionary attrs:
@@ -430,8 +435,7 @@ class FakeSecurityGroup(object):
:return:
A FakeResource object, with id, name, etc.
"""
- if attrs is None:
- attrs = {}
+ attrs = attrs or {}
# Set default attributes.
security_group_attrs = {
@@ -485,8 +489,7 @@ class FakeSecurityGroupRule(object):
:return:
A FakeResource object, with id, etc.
"""
- if attrs is None:
- attrs = {}
+ attrs = attrs or {}
# Set default attributes.
security_group_rule_attrs = {
@@ -537,7 +540,7 @@ class FakeSubnet(object):
"""Fake one or more subnets."""
@staticmethod
- def create_one_subnet(attrs={}):
+ def create_one_subnet(attrs=None):
"""Create a fake subnet.
:param Dictionary attrs:
@@ -545,6 +548,8 @@ class FakeSubnet(object):
:return:
A FakeResource object faking the subnet
"""
+ attrs = attrs or {}
+
# Set default attributes.
project_id = 'project-id-' + uuid.uuid4().hex
subnet_attrs = {
@@ -569,13 +574,14 @@ class FakeSubnet(object):
subnet = fakes.FakeResource(info=copy.deepcopy(subnet_attrs),
loaded=True)
+
# Set attributes with special mappings in OpenStack SDK.
subnet.project_id = subnet_attrs['tenant_id']
return subnet
@staticmethod
- def create_subnets(attrs={}, count=2):
+ def create_subnets(attrs=None, count=2):
"""Create multiple fake subnets.
:param Dictionary attrs:
@@ -596,7 +602,7 @@ class FakeFloatingIP(object):
"""Fake one or more floating ip."""
@staticmethod
- def create_one_floating_ip(attrs={}):
+ def create_one_floating_ip(attrs=None):
"""Create a fake floating ip.
:param Dictionary attrs:
@@ -604,6 +610,8 @@ class FakeFloatingIP(object):
:return:
A FakeResource object, with id, ip, and so on
"""
+ attrs = attrs or {}
+
# Set default attributes.
floating_ip_attrs = {
'id': 'floating-ip-id-' + uuid.uuid4().hex,
@@ -632,7 +640,7 @@ class FakeFloatingIP(object):
return floating_ip
@staticmethod
- def create_floating_ips(attrs={}, count=2):
+ def create_floating_ips(attrs=None, count=2):
"""Create multiple fake floating ips.
:param Dictionary attrs:
@@ -671,7 +679,7 @@ class FakeSubnetPool(object):
"""Fake one or more subnet pools."""
@staticmethod
- def create_one_subnet_pool(attrs={}):
+ def create_one_subnet_pool(attrs=None):
"""Create a fake subnet pool.
:param Dictionary attrs:
@@ -679,6 +687,8 @@ class FakeSubnetPool(object):
:return:
A FakeResource object faking the subnet pool
"""
+ attrs = attrs or {}
+
# Set default attributes.
subnet_pool_attrs = {
'id': 'subnet-pool-id-' + uuid.uuid4().hex,
@@ -709,7 +719,7 @@ class FakeSubnetPool(object):
return subnet_pool
@staticmethod
- def create_subnet_pools(attrs={}, count=2):
+ def create_subnet_pools(attrs=None, count=2):
"""Create multiple fake subnet pools.
:param Dictionary attrs:
diff --git a/openstackclient/tests/network/v2/test_floating_ip.py b/openstackclient/tests/network/v2/test_floating_ip.py
index 3e261fb5..f9ccfe1c 100644
--- a/openstackclient/tests/network/v2/test_floating_ip.py
+++ b/openstackclient/tests/network/v2/test_floating_ip.py
@@ -88,7 +88,6 @@ class TestCreateFloatingIPNetwork(TestFloatingIPNetwork):
arglist = []
verifylist = []
- # Missing required args should bail here
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
@@ -314,7 +313,6 @@ class TestCreateFloatingIPCompute(TestFloatingIPCompute):
arglist = []
verifylist = []
- # Missing required args should bail here
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
diff --git a/openstackclient/tests/network/v2/test_network.py b/openstackclient/tests/network/v2/test_network.py
index a1b0aec9..1269b0a1 100644
--- a/openstackclient/tests/network/v2/test_network.py
+++ b/openstackclient/tests/network/v2/test_network.py
@@ -112,7 +112,6 @@ class TestCreateNetworkIdentityV3(TestNetwork):
arglist = []
verifylist = []
- # Missing required args should bail here
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
@@ -599,7 +598,6 @@ class TestShowNetwork(TestNetwork):
arglist = []
verifylist = []
- # Missing required args should bail here
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
@@ -617,8 +615,8 @@ class TestShowNetwork(TestNetwork):
self.network.find_network.assert_called_once_with(
self._network.name, ignore_missing=False)
- self.assertEqual(tuple(self.columns), columns)
- self.assertEqual(list(self.data), list(data))
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
# Tests for Nova network
@@ -916,7 +914,6 @@ class TestShowNetworkCompute(TestNetworkCompute):
arglist = []
verifylist = []
- # Missing required args should bail here
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
@@ -931,5 +928,5 @@ class TestShowNetworkCompute(TestNetworkCompute):
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.assertEqual(self.columns, tuple(columns))
+ self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
diff --git a/openstackclient/tests/network/v2/test_router.py b/openstackclient/tests/network/v2/test_router.py
index e8041498..655e86c9 100644
--- a/openstackclient/tests/network/v2/test_router.py
+++ b/openstackclient/tests/network/v2/test_router.py
@@ -47,7 +47,6 @@ class TestAddPortToRouter(TestRouter):
arglist = []
verifylist = []
- # Missing required args should bail here
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
@@ -88,7 +87,6 @@ class TestAddSubnetToRouter(TestRouter):
arglist = []
verifylist = []
- # Missing required args should bail here
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
@@ -138,7 +136,7 @@ class TestCreateRouter(TestRouter):
new_router.id,
new_router.name,
new_router.tenant_id,
- new_router.routes,
+ router._format_routes(new_router.routes),
new_router.status,
)
@@ -154,7 +152,6 @@ class TestCreateRouter(TestRouter):
arglist = []
verifylist = []
- # Missing required args should bail here
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
@@ -268,7 +265,7 @@ class TestListRouter(TestRouter):
r = routers[i]
data_long.append(
data[i] + (
- r.routes,
+ router._format_routes(r.routes),
router._format_external_gateway_info(r.external_gateway_info),
osc_utils.format_list(r.availability_zones),
)
@@ -335,7 +332,6 @@ class TestRemovePortFromRouter(TestRouter):
arglist = []
verifylist = []
- # Missing required args should bail here
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
@@ -375,7 +371,6 @@ class TestRemoveSubnetFromRouter(TestRouter):
arglist = []
verifylist = []
- # Missing required args should bail here
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
@@ -399,7 +394,10 @@ class TestRemoveSubnetFromRouter(TestRouter):
class TestSetRouter(TestRouter):
# The router to set.
- _router = network_fakes.FakeRouter.create_one_router()
+ _default_route = {'destination': '10.20.20.0/24', 'nexthop': '10.20.30.1'}
+ _router = network_fakes.FakeRouter.create_one_router(
+ attrs={'routes': [_default_route]}
+ )
def setUp(self):
super(TestSetRouter, self).setUp()
@@ -472,7 +470,6 @@ class TestSetRouter(TestRouter):
('distributed', False),
]
- # Missing required args should bail here
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
@@ -491,8 +488,8 @@ class TestSetRouter(TestRouter):
result = self.cmd.take_action(parsed_args)
attrs = {
- 'routes': [{'destination': '10.20.30.0/24',
- 'gateway': '10.20.30.1'}],
+ 'routes': self._router.routes + [{'destination': '10.20.30.0/24',
+ 'nexthop': '10.20.30.1'}],
}
self.network.update_router.assert_called_once_with(
self._router, **attrs)
@@ -531,7 +528,6 @@ class TestSetRouter(TestRouter):
('clear_routes', True),
]
- # Argument parse failing should bail here
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
@@ -572,7 +568,7 @@ class TestShowRouter(TestRouter):
_router.id,
_router.name,
_router.tenant_id,
- _router.routes,
+ router._format_routes(_router.routes),
_router.status,
)
@@ -588,7 +584,6 @@ class TestShowRouter(TestRouter):
arglist = []
verifylist = []
- # Missing required args should bail here
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
@@ -605,5 +600,5 @@ class TestShowRouter(TestRouter):
self.network.find_router.assert_called_once_with(
self._router.name, ignore_missing=False)
- self.assertEqual(tuple(self.columns), columns)
+ self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
diff --git a/openstackclient/tests/network/v2/test_security_group.py b/openstackclient/tests/network/v2/test_security_group.py
index dd6a3d41..213367a4 100644
--- a/openstackclient/tests/network/v2/test_security_group.py
+++ b/openstackclient/tests/network/v2/test_security_group.py
@@ -114,7 +114,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
'description': self._security_group.name,
'name': self._security_group.name,
})
- self.assertEqual(tuple(self.columns), columns)
+ self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
def test_create_all_options(self):
@@ -139,7 +139,7 @@ class TestCreateSecurityGroupNetwork(TestSecurityGroupNetwork):
'name': self._security_group.name,
'tenant_id': identity_fakes.project_id,
})
- self.assertEqual(tuple(self.columns), columns)
+ self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
@@ -296,28 +296,30 @@ class TestDeleteSecurityGroupCompute(TestSecurityGroupCompute):
class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
# The security group to be listed.
- _security_group = \
- network_fakes.FakeSecurityGroup.create_one_security_group()
+ _security_groups = \
+ network_fakes.FakeSecurityGroup.create_security_groups(count=3)
- expected_columns = (
+ columns = (
'ID',
'Name',
'Description',
'Project',
)
- expected_data = ((
- _security_group.id,
- _security_group.name,
- _security_group.description,
- _security_group.tenant_id,
- ),)
+ data = []
+ for grp in _security_groups:
+ data.append((
+ grp.id,
+ grp.name,
+ grp.description,
+ grp.tenant_id,
+ ))
def setUp(self):
super(TestListSecurityGroupNetwork, self).setUp()
self.network.security_groups = mock.Mock(
- return_value=[self._security_group])
+ return_value=self._security_groups)
# Get the command object to test
self.cmd = security_group.ListSecurityGroup(self.app, self.namespace)
@@ -332,8 +334,8 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
columns, data = self.cmd.take_action(parsed_args)
self.network.security_groups.assert_called_once_with()
- self.assertEqual(self.expected_columns, columns)
- self.assertEqual(self.expected_data, tuple(data))
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
def test_security_group_list_all_projects(self):
arglist = [
@@ -347,45 +349,49 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
columns, data = self.cmd.take_action(parsed_args)
self.network.security_groups.assert_called_once_with()
- self.assertEqual(self.expected_columns, columns)
- self.assertEqual(self.expected_data, tuple(data))
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
class TestListSecurityGroupCompute(TestSecurityGroupCompute):
# The security group to be listed.
- _security_group = \
- compute_fakes.FakeSecurityGroup.create_one_security_group()
+ _security_groups = \
+ compute_fakes.FakeSecurityGroup.create_security_groups(count=3)
- expected_columns = (
+ columns = (
'ID',
'Name',
'Description',
)
- expected_columns_all_projects = (
+ columns_all_projects = (
'ID',
'Name',
'Description',
'Project',
)
- expected_data = ((
- _security_group.id,
- _security_group.name,
- _security_group.description,
- ),)
- expected_data_all_projects = ((
- _security_group.id,
- _security_group.name,
- _security_group.description,
- _security_group.tenant_id,
- ),)
+ data = []
+ for grp in _security_groups:
+ data.append((
+ grp.id,
+ grp.name,
+ grp.description,
+ ))
+ data_all_projects = []
+ for grp in _security_groups:
+ data_all_projects.append((
+ grp.id,
+ grp.name,
+ grp.description,
+ grp.tenant_id,
+ ))
def setUp(self):
super(TestListSecurityGroupCompute, self).setUp()
self.app.client_manager.network_endpoint_enabled = False
- self.compute.security_groups.list.return_value = [self._security_group]
+ self.compute.security_groups.list.return_value = self._security_groups
# Get the command object to test
self.cmd = security_group.ListSecurityGroup(self.app, None)
@@ -401,8 +407,8 @@ class TestListSecurityGroupCompute(TestSecurityGroupCompute):
kwargs = {'search_opts': {'all_tenants': False}}
self.compute.security_groups.list.assert_called_once_with(**kwargs)
- self.assertEqual(self.expected_columns, columns)
- self.assertEqual(self.expected_data, tuple(data))
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
def test_security_group_list_all_projects(self):
arglist = [
@@ -417,8 +423,8 @@ class TestListSecurityGroupCompute(TestSecurityGroupCompute):
kwargs = {'search_opts': {'all_tenants': True}}
self.compute.security_groups.list.assert_called_once_with(**kwargs)
- self.assertEqual(self.expected_columns_all_projects, columns)
- self.assertEqual(self.expected_data_all_projects, tuple(data))
+ self.assertEqual(self.columns_all_projects, columns)
+ self.assertEqual(self.data_all_projects, list(data))
class TestSetSecurityGroupNetwork(TestSecurityGroupNetwork):
diff --git a/openstackclient/tests/network/v2/test_security_group_rule.py b/openstackclient/tests/network/v2/test_security_group_rule.py
index df7414aa..bd903f9e 100644
--- a/openstackclient/tests/network/v2/test_security_group_rule.py
+++ b/openstackclient/tests/network/v2/test_security_group_rule.py
@@ -174,7 +174,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'remote_ip_prefix': self._security_group_rule.remote_ip_prefix,
'security_group_id': self._security_group.id,
})
- self.assertEqual(tuple(self.expected_columns), columns)
+ self.assertEqual(self.expected_columns, columns)
self.assertEqual(self.expected_data, data)
def test_create_source_group(self):
@@ -209,7 +209,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'remote_group_id': self._security_group_rule.remote_group_id,
'security_group_id': self._security_group.id,
})
- self.assertEqual(tuple(self.expected_columns), columns)
+ self.assertEqual(self.expected_columns, columns)
self.assertEqual(self.expected_data, data)
def test_create_source_ip(self):
@@ -240,7 +240,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'remote_ip_prefix': self._security_group_rule.remote_ip_prefix,
'security_group_id': self._security_group.id,
})
- self.assertEqual(tuple(self.expected_columns), columns)
+ self.assertEqual(self.expected_columns, columns)
self.assertEqual(self.expected_data, data)
def test_create_network_options(self):
@@ -282,7 +282,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
'security_group_id': self._security_group.id,
'tenant_id': identity_fakes.project_id,
})
- self.assertEqual(tuple(self.expected_columns), columns)
+ self.assertEqual(self.expected_columns, columns)
self.assertEqual(self.expected_data, data)
@@ -816,7 +816,7 @@ class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork):
self.network.find_security_group_rule.assert_called_once_with(
self._security_group_rule.id, ignore_missing=False)
- self.assertEqual(tuple(self.columns), columns)
+ self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
diff --git a/openstackclient/tests/network/v2/test_subnet.py b/openstackclient/tests/network/v2/test_subnet.py
index ede37416..1923286d 100644
--- a/openstackclient/tests/network/v2/test_subnet.py
+++ b/openstackclient/tests/network/v2/test_subnet.py
@@ -627,4 +627,4 @@ class TestShowSubnet(TestSubnet):
self._subnet.name, ignore_missing=False)
self.assertEqual(self.columns, columns)
- self.assertEqual(list(self.data), list(data))
+ self.assertEqual(self.data, data)
diff --git a/openstackclient/tests/network/v2/test_subnet_pool.py b/openstackclient/tests/network/v2/test_subnet_pool.py
index cbb32fc3..de12c9e9 100644
--- a/openstackclient/tests/network/v2/test_subnet_pool.py
+++ b/openstackclient/tests/network/v2/test_subnet_pool.py
@@ -109,7 +109,17 @@ class TestCreateSubnetPool(TestSubnetPool):
arglist = []
verifylist = []
- # Missing required args should bail here
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
+
+ def test_create_no_pool_prefix(self):
+ """Make sure --pool-prefix is a required argument"""
+ arglist = [
+ self._subnet_pool.name,
+ ]
+ verifylist = [
+ ('name', self._subnet_pool.name),
+ ]
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
@@ -138,23 +148,26 @@ class TestCreateSubnetPool(TestSubnetPool):
'--default-prefix-length', self._subnet_pool.default_prefixlen,
'--max-prefix-length', self._subnet_pool.max_prefixlen,
'--min-prefix-length', self._subnet_pool.min_prefixlen,
+ '--pool-prefix', '10.0.10.0/24',
self._subnet_pool.name,
]
verifylist = [
- ('default_prefix_length', self._subnet_pool.default_prefixlen),
- ('max_prefix_length', self._subnet_pool.max_prefixlen),
- ('min_prefix_length', self._subnet_pool.min_prefixlen),
+ ('default_prefix_length',
+ int(self._subnet_pool.default_prefixlen)),
+ ('max_prefix_length', int(self._subnet_pool.max_prefixlen)),
+ ('min_prefix_length', int(self._subnet_pool.min_prefixlen)),
('name', self._subnet_pool.name),
+ ('prefixes', ['10.0.10.0/24']),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = (self.cmd.take_action(parsed_args))
self.network.create_subnet_pool.assert_called_once_with(**{
- 'default_prefixlen': self._subnet_pool.default_prefixlen,
- 'max_prefixlen': self._subnet_pool.max_prefixlen,
- 'min_prefixlen': self._subnet_pool.min_prefixlen,
- 'prefixes': [],
+ 'default_prefixlen': int(self._subnet_pool.default_prefixlen),
+ 'max_prefixlen': int(self._subnet_pool.max_prefixlen),
+ 'min_prefixlen': int(self._subnet_pool.min_prefixlen),
+ 'prefixes': ['10.0.10.0/24'],
'name': self._subnet_pool.name,
})
self.assertEqual(self.columns, columns)
@@ -221,6 +234,32 @@ class TestCreateSubnetPool(TestSubnetPool):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
+ def test_create_default_and_shared_options(self):
+ arglist = [
+ '--pool-prefix', '10.0.10.0/24',
+ '--default',
+ '--share',
+ self._subnet_pool.name,
+ ]
+ verifylist = [
+ ('prefixes', ['10.0.10.0/24']),
+ ('default', True),
+ ('share', True),
+ ('name', self._subnet_pool.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_subnet_pool.assert_called_once_with(**{
+ 'is_default': True,
+ 'name': self._subnet_pool.name,
+ 'prefixes': ['10.0.10.0/24'],
+ 'shared': True,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
class TestDeleteSubnetPool(TestSubnetPool):
@@ -267,6 +306,8 @@ class TestListSubnetPool(TestSubnetPool):
columns_long = columns + (
'Default Prefix Length',
'Address Scope',
+ 'Default Subnet Pool',
+ 'Shared',
)
data = []
@@ -285,6 +326,8 @@ class TestListSubnetPool(TestSubnetPool):
utils.format_list(pool.prefixes),
pool.default_prefixlen,
pool.address_scope_id,
+ pool.is_default,
+ pool.shared,
))
def setUp(self):
@@ -354,8 +397,8 @@ class TestSetSubnetPool(TestSubnetPool):
]
verifylist = [
('name', 'noob'),
- ('default_prefix_length', '8'),
- ('min_prefix_length', '8'),
+ ('default_prefix_length', 8),
+ ('min_prefix_length', 8),
('subnet_pool', self._subnet_pool.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -364,8 +407,8 @@ class TestSetSubnetPool(TestSubnetPool):
attrs = {
'name': 'noob',
- 'default_prefixlen': '8',
- 'min_prefixlen': '8',
+ 'default_prefixlen': 8,
+ 'min_prefixlen': 8,
}
self.network.update_subnet_pool.assert_called_once_with(
self._subnet_pool, **attrs)
@@ -380,7 +423,7 @@ class TestSetSubnetPool(TestSubnetPool):
]
verifylist = [
('prefixes', ['10.0.1.0/24', '10.0.2.0/24']),
- ('max_prefix_length', '16'),
+ ('max_prefix_length', 16),
('subnet_pool', self._subnet_pool.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -391,7 +434,7 @@ class TestSetSubnetPool(TestSubnetPool):
prefixes.extend(self._subnet_pool.prefixes)
attrs = {
'prefixes': prefixes,
- 'max_prefixlen': '16',
+ 'max_prefixlen': 16,
}
self.network.update_subnet_pool.assert_called_once_with(
self._subnet_pool, **attrs)
@@ -474,6 +517,62 @@ class TestSetSubnetPool(TestSubnetPool):
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
+ def test_set_default(self):
+ arglist = [
+ '--default',
+ self._subnet_pool.name,
+ ]
+ verifylist = [
+ ('default', True),
+ ('subnet_pool', self._subnet_pool.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'is_default': True
+ }
+ self.network.update_subnet_pool.assert_called_once_with(
+ self._subnet_pool, **attrs)
+ self.assertIsNone(result)
+
+ def test_set_no_default(self):
+ arglist = [
+ '--no-default',
+ self._subnet_pool.name,
+ ]
+ verifylist = [
+ ('no_default', True),
+ ('subnet_pool', self._subnet_pool.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'is_default': False,
+ }
+ self.network.update_subnet_pool.assert_called_once_with(
+ self._subnet_pool, **attrs)
+ self.assertIsNone(result)
+
+ def test_set_no_default_conflict(self):
+ arglist = [
+ '--default',
+ '--no-default',
+ self._subnet_pool.name,
+ ]
+ verifylist = [
+ ('default', True),
+ ('no_default', True),
+ ('subnet_pool', self._subnet_pool.name),
+ ]
+
+ # Exclusive arguments will conflict here.
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
+
class TestShowSubnetPool(TestSubnetPool):
@@ -524,7 +623,6 @@ class TestShowSubnetPool(TestSubnetPool):
arglist = []
verifylist = []
- # Missing required args should bail here
self.assertRaises(tests_utils.ParserException, self.check_parser,
self.cmd, arglist, verifylist)
diff --git a/openstackclient/tests/volume/v1/fakes.py b/openstackclient/tests/volume/v1/fakes.py
index 42673efa..d6c46439 100644
--- a/openstackclient/tests/volume/v1/fakes.py
+++ b/openstackclient/tests/volume/v1/fakes.py
@@ -129,6 +129,97 @@ QOS_WITH_ASSOCIATIONS = {
}
+class FakeServiceClient(object):
+
+ def __init__(self, **kwargs):
+ self.services = mock.Mock()
+ self.services.resource_class = fakes.FakeResource(None, {})
+
+
+class TestService(utils.TestCommand):
+
+ def setUp(self):
+ super(TestService, self).setUp()
+
+ self.app.client_manager.volume = FakeServiceClient(
+ endpoint=fakes.AUTH_URL,
+ token=fakes.AUTH_TOKEN
+ )
+
+
+class FakeService(object):
+ """Fake one or more Services."""
+
+ @staticmethod
+ def create_one_service(attrs=None):
+ """Create a fake service.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes of service
+ :retrun:
+ A FakeResource object with host, status, etc.
+ """
+ # Set default attribute
+ service_info = {
+ 'host': 'host_test',
+ 'binary': 'cinder_test',
+ 'status': 'enabled',
+ 'disabled_reason': 'LongHoliday-GoldenWeek',
+ 'zone': 'fake_zone',
+ 'updated_at': 'fake_date',
+ 'state': 'fake_state',
+ }
+
+ # Overwrite default attributes if there are some attributes set
+ if attrs is None:
+ attrs = {}
+ service_info.update(attrs)
+
+ service = fakes.FakeResource(
+ None,
+ service_info,
+ loaded=True)
+
+ return service
+
+ @staticmethod
+ def create_services(attrs=None, count=2):
+ """Create multiple fake services.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes of service
+ :param Integer count:
+ The number of services to be faked
+ :return:
+ A list of FakeResource objects
+ """
+ services = []
+ for n in range(0, count):
+ services.append(FakeService.create_one_service(attrs))
+
+ return services
+
+ @staticmethod
+ def get_services(services=None, count=2):
+ """Get an iterable MagicMock object with a list of faked services.
+
+ If services list is provided, then initialize the Mock object with the
+ list. Otherwise create one.
+
+ :param List services:
+ A list of FakeResource objects faking services
+ :param Integer count:
+ The number of services to be faked
+ :return
+ An iterable Mock object with side_effect set to a list of faked
+ services
+ """
+ if services is None:
+ services = FakeService.create_services(count)
+
+ return mock.MagicMock(side_effect=services)
+
+
class FakeImagev1Client(object):
def __init__(self, **kwargs):
diff --git a/openstackclient/tests/volume/v1/test_service.py b/openstackclient/tests/volume/v1/test_service.py
new file mode 100644
index 00000000..71684344
--- /dev/null
+++ b/openstackclient/tests/volume/v1/test_service.py
@@ -0,0 +1,141 @@
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+
+from openstackclient.tests.volume.v1 import fakes as service_fakes
+from openstackclient.volume.v1 import service
+
+
+class TestService(service_fakes.TestService):
+
+ def setUp(self):
+ super(TestService, self).setUp()
+
+ # Get a shortcut to the ServiceManager Mock
+ self.service_mock = self.app.client_manager.volume.services
+ self.service_mock.reset_mock()
+
+
+class TestServiceList(TestService):
+
+ # The service to be listed
+ services = service_fakes.FakeService.create_one_service()
+
+ def setUp(self):
+ super(TestServiceList, self).setUp()
+
+ self.service_mock.list.return_value = [self.services]
+
+ # Get the command object to test
+ self.cmd = service.ListService(self.app, None)
+
+ def test_service_list(self):
+ arglist = [
+ '--host', self.services.host,
+ '--service', self.services.binary,
+ ]
+ verifylist = [
+ ('host', self.services.host),
+ ('service', self.services.binary),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class Lister in cliff, abstract method take_action()
+ # returns a tuple containing the column names and an iterable
+ # containing the data to be listed.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ expected_columns = [
+ 'Binary',
+ 'Host',
+ 'Zone',
+ 'Status',
+ 'State',
+ 'Updated At',
+ ]
+
+ # confirming if all expected columns are present in the result.
+ self.assertEqual(expected_columns, columns)
+
+ datalist = ((
+ self.services.binary,
+ self.services.host,
+ self.services.zone,
+ self.services.status,
+ self.services.state,
+ self.services.updated_at,
+ ), )
+
+ # confirming if all expected values are present in the result.
+ self.assertEqual(datalist, tuple(data))
+
+ # checking if proper call was made to list services
+ self.service_mock.list.assert_called_with(
+ self.services.host,
+ self.services.binary,
+ )
+
+ # checking if prohibited columns are present in output
+ self.assertNotIn("Disabled Reason", columns)
+ self.assertNotIn(self.services.disabled_reason,
+ tuple(data))
+
+ def test_service_list_with_long_option(self):
+ arglist = [
+ '--host', self.services.host,
+ '--service', self.services.binary,
+ '--long'
+ ]
+ verifylist = [
+ ('host', self.services.host),
+ ('service', self.services.binary),
+ ('long', True)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class Lister in cliff, abstract method take_action()
+ # returns a tuple containing the column names and an iterable
+ # containing the data to be listed.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ expected_columns = [
+ 'Binary',
+ 'Host',
+ 'Zone',
+ 'Status',
+ 'State',
+ 'Updated At',
+ 'Disabled Reason'
+ ]
+
+ # confirming if all expected columns are present in the result.
+ self.assertEqual(expected_columns, columns)
+
+ datalist = ((
+ self.services.binary,
+ self.services.host,
+ self.services.zone,
+ self.services.status,
+ self.services.state,
+ self.services.updated_at,
+ self.services.disabled_reason,
+ ), )
+
+ # confirming if all expected values are present in the result.
+ self.assertEqual(datalist, tuple(data))
+
+ self.service_mock.list.assert_called_with(
+ self.services.host,
+ self.services.binary,
+ )
diff --git a/openstackclient/tests/volume/v2/fakes.py b/openstackclient/tests/volume/v2/fakes.py
index 6e631c41..120666a0 100644
--- a/openstackclient/tests/volume/v2/fakes.py
+++ b/openstackclient/tests/volume/v2/fakes.py
@@ -232,6 +232,97 @@ EXTENSION = {
}
+class FakeServiceClient(object):
+
+ def __init__(self, **kwargs):
+ self.services = mock.Mock()
+ self.services.resource_class = fakes.FakeResource(None, {})
+
+
+class TestService(utils.TestCommand):
+
+ def setUp(self):
+ super(TestService, self).setUp()
+
+ self.app.client_manager.volume = FakeServiceClient(
+ endpoint=fakes.AUTH_URL,
+ token=fakes.AUTH_TOKEN
+ )
+
+
+class FakeService(object):
+ """Fake one or more Services."""
+
+ @staticmethod
+ def create_one_service(attrs=None):
+ """Create a fake service.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes of service
+ :retrun:
+ A FakeResource object with host, status, etc.
+ """
+ # Set default attribute
+ service_info = {
+ 'host': 'host_test',
+ 'binary': 'cinder_test',
+ 'status': 'enabled',
+ 'disabled_reason': 'LongHoliday-GoldenWeek',
+ 'zone': 'fake_zone',
+ 'updated_at': 'fake_date',
+ 'state': 'fake_state',
+ }
+
+ # Overwrite default attributes if there are some attributes set
+ if attrs is None:
+ attrs = {}
+ service_info.update(attrs)
+
+ service = fakes.FakeResource(
+ None,
+ service_info,
+ loaded=True)
+
+ return service
+
+ @staticmethod
+ def create_services(attrs=None, count=2):
+ """Create multiple fake services.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes of service
+ :param Integer count:
+ The number of services to be faked
+ :return:
+ A list of FakeResource objects
+ """
+ services = []
+ for n in range(0, count):
+ services.append(FakeService.create_one_service(attrs))
+
+ return services
+
+ @staticmethod
+ def get_services(services=None, count=2):
+ """Get an iterable MagicMock object with a list of faked services.
+
+ If services list is provided, then initialize the Mock object with the
+ list. Otherwise create one.
+
+ :param List services:
+ A list of FakeResource objects faking services
+ :param Integer count:
+ The number of services to be faked
+ :return
+ An iterable Mock object with side_effect set to a list of faked
+ services
+ """
+ if services is None:
+ services = FakeService.create_services(count)
+
+ return mock.MagicMock(side_effect=services)
+
+
class FakeVolumeClient(object):
def __init__(self, **kwargs):
@@ -281,7 +372,7 @@ class FakeVolume(object):
"""
@staticmethod
- def create_one_volume(attrs={}):
+ def create_one_volume(attrs=None):
"""Create a fake volume.
:param Dictionary attrs:
@@ -289,6 +380,8 @@ class FakeVolume(object):
:retrun:
A FakeResource object with id, name, status, etc.
"""
+ attrs = attrs or {}
+
# Set default attribute
volume_info = {
'id': 'volume-id' + uuid.uuid4().hex,
@@ -320,7 +413,7 @@ class FakeVolume(object):
return volume
@staticmethod
- def create_volumes(attrs={}, count=2):
+ def create_volumes(attrs=None, count=2):
"""Create multiple fake volumes.
:param Dictionary attrs:
@@ -361,7 +454,7 @@ class FakeAvailabilityZone(object):
"""Fake one or more volume availability zones (AZs)."""
@staticmethod
- def create_one_availability_zone(attrs={}):
+ def create_one_availability_zone(attrs=None):
"""Create a fake AZ.
:param Dictionary attrs:
@@ -369,6 +462,8 @@ class FakeAvailabilityZone(object):
:return:
A FakeResource object with zoneName, zoneState, etc.
"""
+ attrs = attrs or {}
+
# Set default attributes.
availability_zone = {
'zoneName': uuid.uuid4().hex,
@@ -384,7 +479,7 @@ class FakeAvailabilityZone(object):
return availability_zone
@staticmethod
- def create_availability_zones(attrs={}, count=2):
+ def create_availability_zones(attrs=None, count=2):
"""Create multiple fake AZs.
:param Dictionary attrs:
diff --git a/openstackclient/tests/volume/v2/test_service.py b/openstackclient/tests/volume/v2/test_service.py
new file mode 100644
index 00000000..ba2e1b32
--- /dev/null
+++ b/openstackclient/tests/volume/v2/test_service.py
@@ -0,0 +1,141 @@
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+
+from openstackclient.tests.volume.v2 import fakes as service_fakes
+from openstackclient.volume.v2 import service
+
+
+class TestService(service_fakes.TestService):
+
+ def setUp(self):
+ super(TestService, self).setUp()
+
+ # Get a shortcut to the ServiceManager Mock
+ self.service_mock = self.app.client_manager.volume.services
+ self.service_mock.reset_mock()
+
+
+class TestServiceList(TestService):
+
+ # The service to be listed
+ services = service_fakes.FakeService.create_one_service()
+
+ def setUp(self):
+ super(TestServiceList, self).setUp()
+
+ self.service_mock.list.return_value = [self.services]
+
+ # Get the command object to test
+ self.cmd = service.ListService(self.app, None)
+
+ def test_service_list(self):
+ arglist = [
+ '--host', self.services.host,
+ '--service', self.services.binary,
+ ]
+ verifylist = [
+ ('host', self.services.host),
+ ('service', self.services.binary),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class Lister in cliff, abstract method take_action()
+ # returns a tuple containing the column names and an iterable
+ # containing the data to be listed.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ expected_columns = [
+ 'Binary',
+ 'Host',
+ 'Zone',
+ 'Status',
+ 'State',
+ 'Updated At',
+ ]
+
+ # confirming if all expected columns are present in the result.
+ self.assertEqual(expected_columns, columns)
+
+ datalist = ((
+ self.services.binary,
+ self.services.host,
+ self.services.zone,
+ self.services.status,
+ self.services.state,
+ self.services.updated_at,
+ ), )
+
+ # confirming if all expected values are present in the result.
+ self.assertEqual(datalist, tuple(data))
+
+ # checking if proper call was made to list services
+ self.service_mock.list.assert_called_with(
+ self.services.host,
+ self.services.binary,
+ )
+
+ # checking if prohibited columns are present in output
+ self.assertNotIn("Disabled Reason", columns)
+ self.assertNotIn(self.services.disabled_reason,
+ tuple(data))
+
+ def test_service_list_with_long_option(self):
+ arglist = [
+ '--host', self.services.host,
+ '--service', self.services.binary,
+ '--long'
+ ]
+ verifylist = [
+ ('host', self.services.host),
+ ('service', self.services.binary),
+ ('long', True)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class Lister in cliff, abstract method take_action()
+ # returns a tuple containing the column names and an iterable
+ # containing the data to be listed.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ expected_columns = [
+ 'Binary',
+ 'Host',
+ 'Zone',
+ 'Status',
+ 'State',
+ 'Updated At',
+ 'Disabled Reason'
+ ]
+
+ # confirming if all expected columns are present in the result.
+ self.assertEqual(expected_columns, columns)
+
+ datalist = ((
+ self.services.binary,
+ self.services.host,
+ self.services.zone,
+ self.services.status,
+ self.services.state,
+ self.services.updated_at,
+ self.services.disabled_reason,
+ ), )
+
+ # confirming if all expected values are present in the result.
+ self.assertEqual(datalist, tuple(data))
+
+ self.service_mock.list.assert_called_with(
+ self.services.host,
+ self.services.binary,
+ )
diff --git a/openstackclient/volume/v1/service.py b/openstackclient/volume/v1/service.py
new file mode 100644
index 00000000..f26be13e
--- /dev/null
+++ b/openstackclient/volume/v1/service.py
@@ -0,0 +1,70 @@
+#
+# 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.
+#
+
+"""Service action implementations"""
+
+from openstackclient.common import command
+from openstackclient.common import utils
+
+
+class ListService(command.Lister):
+ """List service command"""
+
+ def get_parser(self, prog_name):
+ parser = super(ListService, self).get_parser(prog_name)
+ parser.add_argument(
+ "--host",
+ metavar="<host>",
+ help="List services on specified host (name only)")
+ parser.add_argument(
+ "--service",
+ metavar="<service>",
+ help="List only specified service (name only)")
+ parser.add_argument(
+ "--long",
+ action="store_true",
+ default=False,
+ help="List additional fields in output"
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ service_client = self.app.client_manager.volume
+
+ if parsed_args.long:
+ columns = [
+ "Binary",
+ "Host",
+ "Zone",
+ "Status",
+ "State",
+ "Updated At",
+ "Disabled Reason"
+ ]
+ else:
+ columns = [
+ "Binary",
+ "Host",
+ "Zone",
+ "Status",
+ "State",
+ "Updated At"
+ ]
+
+ data = service_client.services.list(parsed_args.host,
+ parsed_args.service)
+ return (columns,
+ (utils.get_item_properties(
+ s, columns,
+ ) for s in data))
diff --git a/openstackclient/volume/v2/service.py b/openstackclient/volume/v2/service.py
new file mode 100644
index 00000000..f26be13e
--- /dev/null
+++ b/openstackclient/volume/v2/service.py
@@ -0,0 +1,70 @@
+#
+# 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.
+#
+
+"""Service action implementations"""
+
+from openstackclient.common import command
+from openstackclient.common import utils
+
+
+class ListService(command.Lister):
+ """List service command"""
+
+ def get_parser(self, prog_name):
+ parser = super(ListService, self).get_parser(prog_name)
+ parser.add_argument(
+ "--host",
+ metavar="<host>",
+ help="List services on specified host (name only)")
+ parser.add_argument(
+ "--service",
+ metavar="<service>",
+ help="List only specified service (name only)")
+ parser.add_argument(
+ "--long",
+ action="store_true",
+ default=False,
+ help="List additional fields in output"
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ service_client = self.app.client_manager.volume
+
+ if parsed_args.long:
+ columns = [
+ "Binary",
+ "Host",
+ "Zone",
+ "Status",
+ "State",
+ "Updated At",
+ "Disabled Reason"
+ ]
+ else:
+ columns = [
+ "Binary",
+ "Host",
+ "Zone",
+ "Status",
+ "State",
+ "Updated At"
+ ]
+
+ data = service_client.services.list(parsed_args.host,
+ parsed_args.service)
+ return (columns,
+ (utils.get_item_properties(
+ s, columns,
+ ) for s in data))