summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/common/quota.py13
-rw-r--r--openstackclient/compute/v2/server.py192
-rw-r--r--openstackclient/compute/v2/server_group.py38
-rw-r--r--openstackclient/identity/common.py2
-rw-r--r--openstackclient/identity/v3/project.py10
-rw-r--r--openstackclient/tests/unit/common/test_quota.py69
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server.py316
-rw-r--r--openstackclient/tests/unit/compute/v2/test_server_group.py47
-rw-r--r--openstackclient/tests/unit/identity/v3/test_project.py22
9 files changed, 502 insertions, 207 deletions
diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py
index 314ff8b5..b697c24c 100644
--- a/openstackclient/common/quota.py
+++ b/openstackclient/common/quota.py
@@ -279,9 +279,18 @@ class ListQuota(command.Lister, BaseQuota):
return parser
def take_action(self, parsed_args):
- projects = self.app.client_manager.identity.projects.list()
result = []
- project_ids = [getattr(p, 'id', '') for p in projects]
+ project_ids = []
+ if parsed_args.project is None:
+ for p in self.app.client_manager.identity.projects.list():
+ project_ids.append(getattr(p, 'id', ''))
+ else:
+ identity_client = self.app.client_manager.identity
+ project = utils.find_resource(
+ identity_client.projects,
+ parsed_args.project,
+ )
+ project_ids.append(getattr(project, 'id', ''))
if parsed_args.compute:
if parsed_args.detail:
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index d3bd1c72..2b68a5cf 100644
--- a/openstackclient/compute/v2/server.py
+++ b/openstackclient/compute/v2/server.py
@@ -1308,15 +1308,19 @@ class ListServer(command.Lister):
# 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
+ 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(image_client.images,
- parsed_args.image).id
+ image_id = utils.find_resource(
+ image_client.images,
+ parsed_args.image,
+ ).id
search_opts = {
'reservation_id': parsed_args.reservation_id,
@@ -1366,76 +1370,85 @@ class ListServer(command.Lister):
try:
timeutils.parse_isotime(search_opts['changes-since'])
except ValueError:
+ msg = _('Invalid changes-since value: %s')
raise exceptions.CommandError(
- _('Invalid changes-since value: %s') %
- search_opts['changes-since']
+ msg % search_opts['changes-since']
)
+ columns = (
+ 'id',
+ 'name',
+ 'status',
+ )
+ column_headers = (
+ 'ID',
+ 'Name',
+ 'Status',
+ )
+
if parsed_args.long:
- columns = (
- 'ID',
- 'Name',
- 'Status',
+ columns += (
'OS-EXT-STS:task_state',
'OS-EXT-STS:power_state',
- 'Networks',
- 'Image Name',
- 'Image ID',
- 'Flavor Name',
- 'Flavor ID',
- 'OS-EXT-AZ:availability_zone',
- 'OS-EXT-SRV-ATTR:host',
- 'Metadata',
)
- column_headers = (
- 'ID',
- 'Name',
- 'Status',
+ column_headers += (
'Task State',
'Power State',
- 'Networks',
+ )
+
+ columns += ('networks',)
+ column_headers += ('Networks',)
+
+ if parsed_args.long:
+ columns += (
+ 'image_name',
+ 'image_id',
+ )
+ column_headers += (
'Image Name',
'Image ID',
- 'Flavor Name',
- 'Flavor ID',
- '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:
if parsed_args.no_name_lookup:
- columns = (
- 'ID',
- 'Name',
- 'Status',
- 'Networks',
- 'Image ID',
- 'Flavor ID',
- )
+ columns += ('image_id',)
else:
- columns = (
- 'ID',
- 'Name',
- 'Status',
- 'Networks',
- 'Image Name',
+ columns += ('image_name',)
+ column_headers += ('Image',)
+
+ # microversion 2.47 puts the embedded flavor into the server response
+ # body but omits the id, so if not present we just expose the original
+ # flavor name in the output
+ if compute_client.api_version >= api_versions.APIVersion('2.47'):
+ columns += ('flavor_name',)
+ column_headers += ('Flavor',)
+ else:
+ if parsed_args.long:
+ columns += (
+ 'flavor_name',
+ 'flavor_id',
+ )
+ column_headers += (
'Flavor Name',
+ 'Flavor ID',
)
- column_headers = (
- 'ID',
- 'Name',
- 'Status',
- 'Networks',
- 'Image',
- 'Flavor',
+ else:
+ if parsed_args.no_name_lookup:
+ columns += ('flavor_id',)
+ else:
+ columns += ('flavor_name',)
+ column_headers += ('Flavor',)
+
+ if parsed_args.long:
+ columns += (
+ 'OS-EXT-AZ:availability_zone',
+ 'OS-EXT-SRV-ATTR:host',
+ 'metadata',
+ )
+ column_headers += (
+ 'Availability Zone',
+ 'Host',
+ 'Properties',
)
- mixed_case_fields = []
marker_id = None
@@ -1447,25 +1460,29 @@ class ListServer(command.Lister):
if parsed_args.deleted:
marker_id = parsed_args.marker
else:
- marker_id = utils.find_resource(compute_client.servers,
- parsed_args.marker).id
+ 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)
+ data = compute_client.servers.list(
+ search_opts=search_opts,
+ marker=marker_id,
+ limit=parsed_args.limit)
images = {}
flavors = {}
if data and not parsed_args.no_name_lookup:
- # Create a dict that maps image_id to image object.
- # Needed so that we can display the "Image Name" column.
- # "Image Name" is not crucial, so we swallow any exceptions.
- # The 'image' attribute can be an empty string if the server was
- # booted from a volume.
+ # create a dict that maps image_id to image object, which is used
+ # to display the "Image Name" column. Note that 'image.id' can be
+ # empty for BFV instances and 'image' can be missing entirely if
+ # there are infra failures
if parsed_args.name_lookup_one_by_one or image_id:
- for i_id in set(filter(lambda x: x is not None,
- (s.image.get('id') for s in data
- if s.image))):
+ for i_id in set(
+ s.image['id'] for s in data
+ if s.image and s.image.get('id')
+ ):
+ # "Image Name" is not crucial, so we swallow any exceptions
try:
images[i_id] = image_client.images.get(i_id)
except Exception:
@@ -1478,12 +1495,17 @@ class ListServer(command.Lister):
except Exception:
pass
- # Create a dict that maps flavor_id to flavor object.
- # Needed so that we can display the "Flavor Name" column.
- # "Flavor Name" is not crucial, so we swallow any exceptions.
+ # create a dict that maps flavor_id to flavor object, which is used
+ # to display the "Flavor Name" column. Note that 'flavor.id' is not
+ # present on microversion 2.47 or later and 'flavor' won't be
+ # present if there are infra failures
if parsed_args.name_lookup_one_by_one or flavor_id:
- for f_id in set(filter(lambda x: x is not None,
- (s.flavor.get('id') for s in data))):
+ for f_id in set(
+ s.flavor['id'] for s in data
+ if s.flavor and s.flavor.get('id')
+ ):
+ # "Flavor Name" is not crucial, so we swallow any
+ # exceptions
try:
flavors[f_id] = compute_client.flavors.get(f_id)
except Exception:
@@ -1507,6 +1529,7 @@ class ListServer(command.Lister):
# processing of the image and flavor informations.
if not hasattr(s, 'image') or not hasattr(s, 'flavor'):
continue
+
if 'id' in s.image:
image = images.get(s.image['id'])
if image:
@@ -1515,28 +1538,29 @@ class ListServer(command.Lister):
else:
s.image_name = ''
s.image_id = ''
- if 'id' in s.flavor:
+
+ if compute_client.api_version < api_versions.APIVersion('2.47'):
flavor = flavors.get(s.flavor['id'])
if flavor:
s.flavor_name = flavor.name
s.flavor_id = s.flavor['id']
else:
- # TODO(mriedem): Fix this for microversion >= 2.47 where the
- # flavor is embedded in the server response without the id.
- # We likely need to drop the Flavor ID column in that case if
- # --long is specified.
- s.flavor_name = ''
- s.flavor_id = ''
+ s.flavor_name = s.flavor['original_name']
table = (column_headers,
(utils.get_item_properties(
s, columns,
- mixed_case_fields=mixed_case_fields,
+ mixed_case_fields=(
+ 'OS-EXT-STS:task_state',
+ 'OS-EXT-STS:power_state',
+ 'OS-EXT-AZ:availability_zone',
+ 'OS-EXT-SRV-ATTR:host',
+ ),
formatters={
'OS-EXT-STS:power_state':
_format_servers_list_power_state,
- 'Networks': _format_servers_list_networks,
- 'Metadata': utils.format_dict,
+ 'networks': _format_servers_list_networks,
+ 'metadata': utils.format_dict,
},
) for s in data))
return table
diff --git a/openstackclient/compute/v2/server_group.py b/openstackclient/compute/v2/server_group.py
index 1af6e28d..b632effd 100644
--- a/openstackclient/compute/v2/server_group.py
+++ b/openstackclient/compute/v2/server_group.py
@@ -135,11 +135,47 @@ class ListServerGroup(command.Lister):
default=False,
help=_("List additional fields in output")
)
+ # TODO(stephenfin): This should really be a --marker option, but alas
+ # the API doesn't support that for some reason
+ parser.add_argument(
+ '--offset',
+ metavar='<offset>',
+ type=int,
+ default=None,
+ help=_(
+ 'Index from which to start listing servers. This should '
+ 'typically be a factor of --limit. Display all servers groups '
+ 'if not specified.'
+ ),
+ )
+ parser.add_argument(
+ '--limit',
+ metavar='<limit>',
+ type=int,
+ default=None,
+ help=_(
+ "Maximum number of server groups to display. "
+ "If limit is greater than 'osapi_max_limit' option of Nova "
+ "API, 'osapi_max_limit' will be used instead."
+ ),
+ )
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)
+
+ kwargs = {}
+
+ if parsed_args.all_projects:
+ kwargs['all_projects'] = parsed_args.all_projects
+
+ if parsed_args.offset:
+ kwargs['offset'] = parsed_args.offset
+
+ if parsed_args.limit:
+ kwargs['limit'] = parsed_args.limit
+
+ data = compute_client.server_groups.list(**kwargs)
policy_key = 'Policies'
if compute_client.api_version >= api_versions.APIVersion("2.64"):
diff --git a/openstackclient/identity/common.py b/openstackclient/identity/common.py
index e8251166..3200e535 100644
--- a/openstackclient/identity/common.py
+++ b/openstackclient/identity/common.py
@@ -207,7 +207,7 @@ def _find_identity_resource(identity_client_manager, name_or_id,
name_or_id, **kwargs)
if identity_resource is not None:
return identity_resource
- except identity_exc.Forbidden:
+ except (exceptions.Forbidden, identity_exc.Forbidden):
pass
return resource_type(None, {'id': name_or_id, 'name': name_or_id})
diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py
index 1d6f1810..35d68881 100644
--- a/openstackclient/identity/v3/project.py
+++ b/openstackclient/identity/v3/project.py
@@ -194,6 +194,11 @@ class ListProject(command.Lister):
help=_('Filter projects by <domain> (name or ID)'),
)
parser.add_argument(
+ '--parent',
+ metavar='<parent>',
+ help=_('Filter projects whose parent is <parent> (name or ID)'),
+ )
+ parser.add_argument(
'--user',
metavar='<user>',
help=_('Filter projects by <user> (name or ID)'),
@@ -234,6 +239,11 @@ class ListProject(command.Lister):
parsed_args.domain).id
kwargs['domain'] = domain_id
+ if parsed_args.parent:
+ parent_id = common.find_project(identity_client,
+ parsed_args.parent).id
+ kwargs['parent'] = parent_id
+
if parsed_args.user:
if parsed_args.domain:
user_id = utils.find_resource(identity_client.users,
diff --git a/openstackclient/tests/unit/common/test_quota.py b/openstackclient/tests/unit/common/test_quota.py
index f519f872..6d9ec451 100644
--- a/openstackclient/tests/unit/common/test_quota.py
+++ b/openstackclient/tests/unit/common/test_quota.py
@@ -392,6 +392,29 @@ class TestQuotaList(TestQuota):
parsed_args,
)
+ def test_quota_list_compute_by_project(self):
+ # Two projects with non-default quotas
+ self.compute.quotas.get = mock.Mock(
+ side_effect=self.compute_quotas,
+ )
+
+ arglist = [
+ '--compute',
+ '--project', self.projects[0].name,
+ ]
+ verifylist = [
+ ('compute', True),
+ ('project', self.projects[0].name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ ret_quotas = list(data)
+
+ self.assertEqual(self.compute_column_header, columns)
+ self.assertEqual(self.compute_reference_data, ret_quotas[0])
+ self.assertEqual(1, len(ret_quotas))
+
def test_quota_list_network(self):
# Two projects with non-default quotas
self.network.get_quota = mock.Mock(
@@ -461,6 +484,29 @@ class TestQuotaList(TestQuota):
self.assertEqual(self.network_reference_data, ret_quotas[0])
self.assertEqual(1, len(ret_quotas))
+ def test_quota_list_network_by_project(self):
+ # Two projects with non-default quotas
+ self.network.get_quota = mock.Mock(
+ side_effect=self.network_quotas,
+ )
+
+ arglist = [
+ '--network',
+ '--project', self.projects[0].name,
+ ]
+ verifylist = [
+ ('network', True),
+ ('project', self.projects[0].name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ ret_quotas = list(data)
+
+ self.assertEqual(self.network_column_header, columns)
+ self.assertEqual(self.network_reference_data, ret_quotas[0])
+ self.assertEqual(1, len(ret_quotas))
+
def test_quota_list_volume(self):
# Two projects with non-default quotas
self.volume.quotas.get = mock.Mock(
@@ -530,6 +576,29 @@ class TestQuotaList(TestQuota):
self.assertEqual(self.volume_reference_data, ret_quotas[0])
self.assertEqual(1, len(ret_quotas))
+ def test_quota_list_volume_by_project(self):
+ # Two projects with non-default quotas
+ self.volume.quotas.get = mock.Mock(
+ side_effect=self.volume_quotas,
+ )
+
+ arglist = [
+ '--volume',
+ '--project', self.projects[0].name,
+ ]
+ verifylist = [
+ ('volume', True),
+ ('project', self.projects[0].name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ ret_quotas = list(data)
+
+ self.assertEqual(self.volume_column_header, columns)
+ self.assertEqual(self.volume_reference_data, ret_quotas[0])
+ self.assertEqual(1, len(ret_quotas))
+
class TestQuotaSet(TestQuota):
diff --git a/openstackclient/tests/unit/compute/v2/test_server.py b/openstackclient/tests/unit/compute/v2/test_server.py
index 819fc354..b5071b6b 100644
--- a/openstackclient/tests/unit/compute/v2/test_server.py
+++ b/openstackclient/tests/unit/compute/v2/test_server.py
@@ -2337,7 +2337,7 @@ class TestServerDumpCreate(TestServer):
self.run_method_with_servers('trigger_crash_dump', 3)
-class TestServerList(TestServer):
+class _TestServerList(TestServer):
# Columns to be listed up.
columns = (
@@ -2365,7 +2365,7 @@ class TestServerList(TestServer):
)
def setUp(self):
- super(TestServerList, self).setUp()
+ super(_TestServerList, self).setUp()
self.search_opts = {
'reservation_id': None,
@@ -2420,10 +2420,11 @@ class TestServerList(TestServer):
# Get the command object to test
self.cmd = server.ListServer(self.app, None)
- # Prepare data returned by fake Nova API.
- self.data = []
- self.data_long = []
- self.data_no_name_lookup = []
+
+class TestServerList(_TestServerList):
+
+ def setUp(self):
+ super(TestServerList, self).setUp()
Image = collections.namedtuple('Image', 'id name')
self.images_mock.list.return_value = [
@@ -2438,8 +2439,8 @@ class TestServerList(TestServer):
for s in self.servers
]
- for s in self.servers:
- self.data.append((
+ self.data = tuple(
+ (
s.id,
s.name,
s.status,
@@ -2447,34 +2448,8 @@ class TestServerList(TestServer):
# Image will be an empty string if boot-from-volume
self.image.name if s.image else s.image,
self.flavor.name,
- ))
- self.data_long.append((
- s.id,
- s.name,
- s.status,
- getattr(s, 'OS-EXT-STS:task_state'),
- server._format_servers_list_power_state(
- getattr(s, 'OS-EXT-STS:power_state')
- ),
- server._format_servers_list_networks(s.networks),
- # Image will be an empty string if boot-from-volume
- self.image.name if s.image else s.image,
- s.image['id'] if s.image else s.image,
- self.flavor.name,
- s.flavor['id'],
- getattr(s, 'OS-EXT-AZ:availability_zone'),
- getattr(s, 'OS-EXT-SRV-ATTR:host'),
- s.Metadata,
- ))
- self.data_no_name_lookup.append((
- s.id,
- s.name,
- s.status,
- server._format_servers_list_networks(s.networks),
- # Image will be an empty string if boot-from-volume
- s.image['id'] if s.image else s.image,
- s.flavor['id']
- ))
+ ) for s in self.servers
+ )
def test_server_list_no_option(self):
arglist = []
@@ -2495,7 +2470,7 @@ class TestServerList(TestServer):
self.assertFalse(self.flavors_mock.get.call_count)
self.assertFalse(self.images_mock.get.call_count)
self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
+ self.assertEqual(self.data, tuple(data))
def test_server_list_no_servers(self):
arglist = []
@@ -2514,9 +2489,28 @@ class TestServerList(TestServer):
self.assertEqual(0, self.images_mock.list.call_count)
self.assertEqual(0, self.flavors_mock.list.call_count)
self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
+ self.assertEqual(self.data, tuple(data))
def test_server_list_long_option(self):
+ self.data = tuple(
+ (
+ s.id,
+ s.name,
+ s.status,
+ getattr(s, 'OS-EXT-STS:task_state'),
+ server._format_servers_list_power_state(
+ getattr(s, 'OS-EXT-STS:power_state')
+ ),
+ server._format_servers_list_networks(s.networks),
+ # Image will be an empty string if boot-from-volume
+ self.image.name if s.image else s.image,
+ s.image['id'] if s.image else s.image,
+ self.flavor.name,
+ s.flavor['id'],
+ getattr(s, 'OS-EXT-AZ:availability_zone'),
+ getattr(s, 'OS-EXT-SRV-ATTR:host'),
+ s.Metadata,
+ ) for s in self.servers)
arglist = [
'--long',
]
@@ -2527,12 +2521,23 @@ class TestServerList(TestServer):
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_long, columns)
- self.assertEqual(tuple(self.data_long), tuple(data))
+ self.assertEqual(self.data, tuple(data))
def test_server_list_no_name_lookup_option(self):
+ self.data = tuple(
+ (
+ s.id,
+ s.name,
+ s.status,
+ server._format_servers_list_networks(s.networks),
+ # Image will be an empty string if boot-from-volume
+ s.image['id'] if s.image else s.image,
+ s.flavor['id']
+ ) for s in self.servers
+ )
+
arglist = [
'--no-name-lookup',
]
@@ -2546,9 +2551,21 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data_no_name_lookup), tuple(data))
+ self.assertEqual(self.data, tuple(data))
def test_server_list_n_option(self):
+ self.data = tuple(
+ (
+ s.id,
+ s.name,
+ s.status,
+ server._format_servers_list_networks(s.networks),
+ # Image will be an empty string if boot-from-volume
+ s.image['id'] if s.image else s.image,
+ s.flavor['id']
+ ) for s in self.servers
+ )
+
arglist = [
'-n',
]
@@ -2562,7 +2579,7 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data_no_name_lookup), tuple(data))
+ self.assertEqual(self.data, tuple(data))
def test_server_list_name_lookup_one_by_one(self):
arglist = [
@@ -2584,7 +2601,7 @@ class TestServerList(TestServer):
self.flavors_mock.get.assert_called()
self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
+ self.assertEqual(self.data, tuple(data))
def test_server_list_with_image(self):
@@ -2604,145 +2621,213 @@ class TestServerList(TestServer):
self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
+ self.assertEqual(self.data, tuple(data))
- def test_server_list_with_locked_pre_v273(self):
+ def test_server_list_with_flavor(self):
arglist = [
- '--locked'
+ '--flavor', self.flavor.id
]
verifylist = [
- ('locked', True)
+ ('flavor', self.flavor.id)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- ex = self.assertRaises(exceptions.CommandError,
- self.cmd.take_action,
- parsed_args)
- self.assertIn(
- '--os-compute-api-version 2.73 or greater is required', str(ex))
+ columns, data = self.cmd.take_action(parsed_args)
- def test_server_list_with_locked_v273(self):
+ self.flavors_mock.get.has_calls(self.flavor.id)
+
+ self.search_opts['flavor'] = self.flavor.id
+ self.servers_mock.list.assert_called_with(**self.kwargs)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, tuple(data))
+
+ def test_server_list_with_changes_since(self):
- self.app.client_manager.compute.api_version = \
- api_versions.APIVersion('2.73')
arglist = [
- '--locked'
+ '--changes-since', '2016-03-04T06:27:59Z',
+ '--deleted'
]
verifylist = [
- ('locked', True)
+ ('changes_since', '2016-03-04T06:27:59Z'),
+ ('deleted', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.search_opts['locked'] = True
+ self.search_opts['changes-since'] = '2016-03-04T06:27:59Z'
+ self.search_opts['deleted'] = True
self.servers_mock.list.assert_called_with(**self.kwargs)
self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
+ self.assertEqual(self.data, tuple(data))
- def test_server_list_with_unlocked_v273(self):
+ @mock.patch.object(timeutils, 'parse_isotime', side_effect=ValueError)
+ def test_server_list_with_invalid_changes_since(self, mock_parse_isotime):
- self.app.client_manager.compute.api_version = \
- api_versions.APIVersion('2.73')
arglist = [
- '--unlocked'
+ '--changes-since', 'Invalid time value',
]
verifylist = [
- ('unlocked', True)
+ ('changes_since', 'Invalid time value'),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- columns, data = self.cmd.take_action(parsed_args)
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('Invalid changes-since value: Invalid time '
+ 'value', str(e))
+ mock_parse_isotime.assert_called_once_with(
+ 'Invalid time value'
+ )
- self.search_opts['locked'] = False
- self.servers_mock.list.assert_called_with(**self.kwargs)
- self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
+class TestServerListV273(_TestServerList):
+
+ # Columns to be listed up.
+ columns = (
+ 'ID',
+ 'Name',
+ 'Status',
+ 'Networks',
+ 'Image',
+ 'Flavor',
+ )
+ columns_long = (
+ 'ID',
+ 'Name',
+ 'Status',
+ 'Task State',
+ 'Power State',
+ 'Networks',
+ 'Image Name',
+ 'Image ID',
+ 'Flavor',
+ 'Availability Zone',
+ 'Host',
+ 'Properties',
+ )
- def test_server_list_with_locked_and_unlocked_v273(self):
+ def setUp(self):
+ super(TestServerListV273, self).setUp()
+
+ # The fake servers' attributes. Use the original attributes names in
+ # nova, not the ones printed by "server list" command.
+ self.attrs['flavor'] = {
+ 'vcpus': self.flavor.vcpus,
+ 'ram': self.flavor.ram,
+ 'disk': self.flavor.disk,
+ 'ephemeral': self.flavor.ephemeral,
+ 'swap': self.flavor.swap,
+ 'original_name': self.flavor.name,
+ 'extra_specs': self.flavor.properties,
+ }
+
+ # The servers to be listed.
+ self.servers = self.setup_servers_mock(3)
+ self.servers_mock.list.return_value = self.servers
+
+ Image = collections.namedtuple('Image', 'id name')
+ self.images_mock.list.return_value = [
+ Image(id=s.image['id'], name=self.image.name)
+ # Image will be an empty string if boot-from-volume
+ for s in self.servers if s.image
+ ]
+
+ # The flavor information is embedded, so now reason for this to be
+ # called
+ self.flavors_mock.list = mock.NonCallableMock()
+
+ self.data = tuple(
+ (
+ s.id,
+ s.name,
+ s.status,
+ server._format_servers_list_networks(s.networks),
+ # Image will be an empty string if boot-from-volume
+ self.image.name if s.image else s.image,
+ self.flavor.name,
+ ) for s in self.servers)
+
+ def test_server_list_with_locked_pre_v273(self):
- self.app.client_manager.compute.api_version = \
- api_versions.APIVersion('2.73')
arglist = [
- '--locked',
- '--unlocked'
+ '--locked'
]
verifylist = [
- ('locked', True),
- ('unlocked', True)
+ ('locked', True)
]
- ex = self.assertRaises(
- utils.ParserException,
- self.check_parser, self.cmd, arglist, verifylist)
- self.assertIn('Argument parse failed', str(ex))
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ ex = self.assertRaises(exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
+ self.assertIn(
+ '--os-compute-api-version 2.73 or greater is required', str(ex))
- def test_server_list_with_flavor(self):
+ def test_server_list_with_locked(self):
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.73')
arglist = [
- '--flavor', self.flavor.id
+ '--locked'
]
verifylist = [
- ('flavor', self.flavor.id)
+ ('locked', True)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.flavors_mock.get.has_calls(self.flavor.id)
-
- self.search_opts['flavor'] = self.flavor.id
+ self.search_opts['locked'] = True
self.servers_mock.list.assert_called_with(**self.kwargs)
- self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
+ self.assertItemsEqual(self.columns, columns)
+ self.assertItemsEqual(self.data, tuple(data))
- def test_server_list_with_changes_since(self):
+ def test_server_list_with_unlocked_v273(self):
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.73')
arglist = [
- '--changes-since', '2016-03-04T06:27:59Z',
- '--deleted'
+ '--unlocked'
]
verifylist = [
- ('changes_since', '2016-03-04T06:27:59Z'),
- ('deleted', True),
+ ('unlocked', True)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- self.search_opts['changes-since'] = '2016-03-04T06:27:59Z'
- self.search_opts['deleted'] = True
+ self.search_opts['locked'] = False
self.servers_mock.list.assert_called_with(**self.kwargs)
- self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
+ self.assertItemsEqual(self.columns, columns)
+ self.assertItemsEqual(self.data, tuple(data))
- @mock.patch.object(timeutils, 'parse_isotime', side_effect=ValueError)
- def test_server_list_with_invalid_changes_since(self, mock_parse_isotime):
+ def test_server_list_with_locked_and_unlocked(self):
+ self.app.client_manager.compute.api_version = \
+ api_versions.APIVersion('2.73')
arglist = [
- '--changes-since', 'Invalid time value',
+ '--locked',
+ '--unlocked'
]
verifylist = [
- ('changes_since', 'Invalid time value'),
+ ('locked', True),
+ ('unlocked', True)
]
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
- try:
- self.cmd.take_action(parsed_args)
- self.fail('CommandError should be raised.')
- except exceptions.CommandError as e:
- self.assertEqual('Invalid changes-since value: Invalid time '
- 'value', str(e))
- mock_parse_isotime.assert_called_once_with(
- 'Invalid time value'
- )
+ ex = self.assertRaises(
+ utils.ParserException,
+ self.check_parser, self.cmd, arglist, verifylist)
+ self.assertIn('Argument parse failed', str(ex))
- def test_server_list_v266_with_changes_before(self):
+ def test_server_list_with_changes_before(self):
self.app.client_manager.compute.api_version = (
api_versions.APIVersion('2.66'))
arglist = [
@@ -2759,13 +2844,14 @@ class TestServerList(TestServer):
self.search_opts['changes-before'] = '2016-03-05T06:27:59Z'
self.search_opts['deleted'] = True
+
self.servers_mock.list.assert_called_with(**self.kwargs)
- self.assertEqual(self.columns, columns)
- self.assertEqual(tuple(self.data), tuple(data))
+ self.assertItemsEqual(self.columns, columns)
+ self.assertItemsEqual(self.data, tuple(data))
@mock.patch.object(timeutils, 'parse_isotime', side_effect=ValueError)
- def test_server_list_v266_with_invalid_changes_before(
+ def test_server_list_with_invalid_changes_before(
self, mock_parse_isotime):
self.app.client_manager.compute.api_version = (
api_versions.APIVersion('2.66'))
diff --git a/openstackclient/tests/unit/compute/v2/test_server_group.py b/openstackclient/tests/unit/compute/v2/test_server_group.py
index 62d929ad..2928b87c 100644
--- a/openstackclient/tests/unit/compute/v2/test_server_group.py
+++ b/openstackclient/tests/unit/compute/v2/test_server_group.py
@@ -255,10 +255,13 @@ class TestServerGroupList(TestServerGroup):
verifylist = [
('all_projects', False),
('long', False),
+ ('limit', None),
+ ('offset', None),
]
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.server_groups_mock.list.assert_called_once_with()
self.assertEqual(self.list_columns, columns)
self.assertEqual(self.list_data, tuple(data))
@@ -271,14 +274,49 @@ class TestServerGroupList(TestServerGroup):
verifylist = [
('all_projects', True),
('long', True),
+ ('limit', None),
+ ('offset', None),
]
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.server_groups_mock.list.assert_called_once_with(
+ all_projects=True)
self.assertEqual(self.list_columns_long, columns)
self.assertEqual(self.list_data_long, tuple(data))
+ def test_server_group_list_with_limit(self):
+ arglist = [
+ '--limit', '1',
+ ]
+ verifylist = [
+ ('all_projects', False),
+ ('long', False),
+ ('limit', 1),
+ ('offset', None),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.cmd.take_action(parsed_args)
+
+ self.server_groups_mock.list.assert_called_once_with(limit=1)
+
+ def test_server_group_list_with_offset(self):
+ arglist = [
+ '--offset', '5',
+ ]
+ verifylist = [
+ ('all_projects', False),
+ ('long', False),
+ ('limit', None),
+ ('offset', 5),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.cmd.take_action(parsed_args)
+
+ self.server_groups_mock.list.assert_called_once_with(offset=5)
+
class TestServerGroupListV264(TestServerGroupV264):
@@ -328,7 +366,7 @@ class TestServerGroupListV264(TestServerGroupV264):
]
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.server_groups_mock.list.assert_called_once_with()
self.assertEqual(self.list_columns, columns)
self.assertEqual(self.list_data, tuple(data))
@@ -344,7 +382,8 @@ class TestServerGroupListV264(TestServerGroupV264):
]
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.server_groups_mock.list.assert_called_once_with(
+ all_projects=True)
self.assertEqual(self.list_columns_long, columns)
self.assertEqual(self.list_data_long, tuple(data))
diff --git a/openstackclient/tests/unit/identity/v3/test_project.py b/openstackclient/tests/unit/identity/v3/test_project.py
index ad081420..ce799c3a 100644
--- a/openstackclient/tests/unit/identity/v3/test_project.py
+++ b/openstackclient/tests/unit/identity/v3/test_project.py
@@ -762,6 +762,28 @@ class TestProjectList(TestProject):
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
+ def test_project_list_parent(self):
+ self.parent = identity_fakes.FakeProject.create_one_project()
+ self.project = identity_fakes.FakeProject.create_one_project(
+ attrs={'domain_id': self.domain.id, 'parent_id': self.parent.id})
+
+ arglist = [
+ '--parent', self.parent.id,
+ ]
+ verifylist = [
+ ('parent', self.parent.id),
+ ]
+
+ self.projects_mock.get.return_value = self.parent
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ self.projects_mock.list.assert_called_with(parent=self.parent.id)
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, tuple(data))
+
def test_project_list_sort(self):
self.projects_mock.list.return_value = self.projects