Fix limits show command without Nova and Cinder

This patch implements an endpoint lookup when showing limits. This
addresses the issue when showing limits without both Nova and Cinder
and will display limits if one is missing.

Change-Id: I2214b281e0206f8fe117aae52de2bf4c4e2c6525
Closes-bug: #1707960
This commit is contained in:
Jude Cross 2018-04-02 15:07:25 -07:00
parent 180d012ca7
commit 24b06ef273
6 changed files with 383 additions and 11 deletions

View File

@ -125,6 +125,25 @@ class ClientManager(clientmanager.ClientManager):
# use Network API by default
return self.is_service_available('network') is not False
def is_compute_endpoint_enabled(self):
"""Check if Compute endpoint is enabled"""
return self.is_service_available('compute') is not False
def is_volume_endpoint_enabled(self, volume_client):
"""Check if volume endpoint is enabled"""
# NOTE(jcross): Cinder did some interesting things with their service
# name so we need to figure out which version to look
# for when calling is_service_available()
volume_version = volume_client.api_version.ver_major
if self.is_service_available(
"volumev%s" % volume_version) is not False:
return True
elif self.is_service_available('volume') is not False:
return True
else:
return False
# Plugin Support

View File

@ -83,24 +83,34 @@ class ShowLimits(command.Lister):
project_id = utils.find_resource(identity_client.projects,
parsed_args.project).id
compute_limits = compute_client.limits.get(parsed_args.is_reserved,
tenant_id=project_id)
volume_limits = volume_client.limits.get()
compute_limits = None
volume_limits = None
if self.app.client_manager.is_compute_endpoint_enabled():
compute_limits = compute_client.limits.get(parsed_args.is_reserved,
tenant_id=project_id)
if self.app.client_manager.is_volume_endpoint_enabled(volume_client):
volume_limits = volume_client.limits.get()
data = []
if parsed_args.is_absolute:
compute_limits = compute_limits.absolute
volume_limits = volume_limits.absolute
if compute_limits:
data.append(compute_limits.absolute)
if volume_limits:
data.append(volume_limits.absolute)
columns = ["Name", "Value"]
return (columns, (utils.get_item_properties(s, columns)
for s in itertools.chain(compute_limits, volume_limits)))
for s in itertools.chain(*data)))
elif parsed_args.is_rate:
compute_limits = compute_limits.rate
volume_limits = volume_limits.rate
if compute_limits:
data.append(compute_limits.rate)
if volume_limits:
data.append(volume_limits.rate)
columns = ["Verb", "URI", "Value", "Remain", "Unit",
"Next Available"]
return (columns, (utils.get_item_properties(s, columns)
for s in itertools.chain(compute_limits, volume_limits)))
for s in itertools.chain(*data)))
else:
return ({}, {})
return {}, {}

View File

@ -0,0 +1,125 @@
# 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.common import limits
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes
class TestComputeLimits(compute_fakes.TestComputev2):
absolute_columns = [
'Name',
'Value',
]
rate_columns = [
"Verb",
"URI",
"Value",
"Remain",
"Unit",
"Next Available"
]
def setUp(self):
super(TestComputeLimits, self).setUp()
self.app.client_manager.volume_endpoint_enabled = False
self.compute = self.app.client_manager.compute
self.fake_limits = compute_fakes.FakeLimits()
self.compute.limits.get.return_value = self.fake_limits
def test_compute_show_absolute(self):
arglist = ['--absolute']
verifylist = [('is_absolute', True)]
cmd = limits.ShowLimits(self.app, None)
parsed_args = self.check_parser(cmd, arglist, verifylist)
columns, data = cmd.take_action(parsed_args)
ret_limits = list(data)
compute_reference_limits = self.fake_limits.absolute_limits()
self.assertEqual(self.absolute_columns, columns)
self.assertEqual(compute_reference_limits, ret_limits)
self.assertEqual(19, len(ret_limits))
def test_compute_show_rate(self):
arglist = ['--rate']
verifylist = [('is_rate', True)]
cmd = limits.ShowLimits(self.app, None)
parsed_args = self.check_parser(cmd, arglist, verifylist)
columns, data = cmd.take_action(parsed_args)
ret_limits = list(data)
compute_reference_limits = self.fake_limits.rate_limits()
self.assertEqual(self.rate_columns, columns)
self.assertEqual(compute_reference_limits, ret_limits)
self.assertEqual(3, len(ret_limits))
class TestVolumeLimits(volume_fakes.TestVolume):
absolute_columns = [
'Name',
'Value',
]
rate_columns = [
"Verb",
"URI",
"Value",
"Remain",
"Unit",
"Next Available"
]
def setUp(self):
super(TestVolumeLimits, self).setUp()
self.app.client_manager.compute_endpoint_enabled = False
self.volume = self.app.client_manager.volume
self.fake_limits = volume_fakes.FakeLimits()
self.volume.limits.get.return_value = self.fake_limits
def test_volume_show_absolute(self):
arglist = ['--absolute']
verifylist = [('is_absolute', True)]
cmd = limits.ShowLimits(self.app, None)
parsed_args = self.check_parser(cmd, arglist, verifylist)
columns, data = cmd.take_action(parsed_args)
ret_limits = list(data)
compute_reference_limits = self.fake_limits.absolute_limits()
self.assertEqual(self.absolute_columns, columns)
self.assertEqual(compute_reference_limits, ret_limits)
self.assertEqual(10, len(ret_limits))
def test_volume_show_rate(self):
arglist = ['--rate']
verifylist = [('is_rate', True)]
cmd = limits.ShowLimits(self.app, None)
parsed_args = self.check_parser(cmd, arglist, verifylist)
columns, data = cmd.take_action(parsed_args)
ret_limits = list(data)
compute_reference_limits = self.fake_limits.rate_limits()
self.assertEqual(self.rate_columns, columns)
self.assertEqual(compute_reference_limits, ret_limits)
self.assertEqual(3, len(ret_limits))

View File

@ -149,6 +149,9 @@ class FakeComputev2Client(object):
self.images = mock.Mock()
self.images.resource_class = fakes.FakeResource(None, {})
self.limits = mock.Mock()
self.limits.resource_class = fakes.FakeResource(None, {})
self.servers = mock.Mock()
self.servers.resource_class = fakes.FakeResource(None, {})
@ -1392,3 +1395,110 @@ class FakeQuota(object):
quota.project_id = quota_attrs['id']
return quota
class FakeLimits(object):
"""Fake limits"""
def __init__(self, absolute_attrs=None, rate_attrs=None):
self.absolute_limits_attrs = {
'maxServerMeta': 128,
'maxTotalInstances': 10,
'maxPersonality': 5,
'totalServerGroupsUsed': 0,
'maxImageMeta': 128,
'maxPersonalitySize': 10240,
'maxTotalRAMSize': 51200,
'maxServerGroups': 10,
'maxSecurityGroupRules': 20,
'maxTotalKeypairs': 100,
'totalCoresUsed': 0,
'totalRAMUsed': 0,
'maxSecurityGroups': 10,
'totalFloatingIpsUsed': 0,
'totalInstancesUsed': 0,
'maxServerGroupMembers': 10,
'maxTotalFloatingIps': 10,
'totalSecurityGroupsUsed': 0,
'maxTotalCores': 20,
}
absolute_attrs = absolute_attrs or {}
self.absolute_limits_attrs.update(absolute_attrs)
self.rate_limits_attrs = [{
"uri": "*",
"limit": [
{
"value": 10,
"verb": "POST",
"remaining": 2,
"unit": "MINUTE",
"next-available": "2011-12-15T22:42:45Z"
},
{
"value": 10,
"verb": "PUT",
"remaining": 2,
"unit": "MINUTE",
"next-available": "2011-12-15T22:42:45Z"
},
{
"value": 100,
"verb": "DELETE",
"remaining": 100,
"unit": "MINUTE",
"next-available": "2011-12-15T22:42:45Z"
}
]
}]
@property
def absolute(self):
for (name, value) in self.absolute_limits_attrs.items():
yield FakeAbsoluteLimit(name, value)
def absolute_limits(self):
reference_data = []
for (name, value) in self.absolute_limits_attrs.items():
reference_data.append((name, value))
return reference_data
@property
def rate(self):
for group in self.rate_limits_attrs:
uri = group['uri']
for rate in group['limit']:
yield FakeRateLimit(rate['verb'], uri, rate['value'],
rate['remaining'], rate['unit'],
rate['next-available'])
def rate_limits(self):
reference_data = []
for group in self.rate_limits_attrs:
uri = group['uri']
for rate in group['limit']:
reference_data.append((rate['verb'], uri, rate['value'],
rate['remaining'], rate['unit'],
rate['next-available']))
return reference_data
class FakeAbsoluteLimit(object):
"""Data model that represents an absolute limit"""
def __init__(self, name, value):
self.name = name
self.value = value
class FakeRateLimit(object):
"""Data model that represents a flattened view of a single rate limit"""
def __init__(self, verb, uri, value, remain,
unit, next_available):
self.verb = verb
self.uri = uri
self.value = value
self.remain = remain
self.unit = unit
self.next_available = next_available

View File

@ -140,6 +140,8 @@ class FakeClientManager(object):
self.auth_ref = None
self.auth_plugin_name = None
self.network_endpoint_enabled = True
self.compute_endpoint_enabled = True
self.volume_endpoint_enabled = True
def get_configuration(self):
return {
@ -155,6 +157,12 @@ class FakeClientManager(object):
def is_network_endpoint_enabled(self):
return self.network_endpoint_enabled
def is_compute_endpoint_enabled(self):
return self.compute_endpoint_enabled
def is_volume_endpoint_enabled(self, client):
return self.volume_endpoint_enabled
class FakeModule(object):

View File

@ -200,6 +200,8 @@ class FakeVolumeClient(object):
self.volumes.resource_class = fakes.FakeResource(None, {})
self.extensions = mock.Mock()
self.extensions.resource_class = fakes.FakeResource(None, {})
self.limits = mock.Mock()
self.limits.resource_class = fakes.FakeResource(None, {})
self.volume_snapshots = mock.Mock()
self.volume_snapshots.resource_class = fakes.FakeResource(None, {})
self.backups = mock.Mock()
@ -1004,3 +1006,101 @@ class FakeQuota(object):
quota.project_id = quota_attrs['id']
return quota
class FakeLimits(object):
"""Fake limits"""
def __init__(self, absolute_attrs=None):
self.absolute_limits_attrs = {
'totalSnapshotsUsed': 1,
'maxTotalBackups': 10,
'maxTotalVolumeGigabytes': 1000,
'maxTotalSnapshots': 10,
'maxTotalBackupGigabytes': 1000,
'totalBackupGigabytesUsed': 0,
'maxTotalVolumes': 10,
'totalVolumesUsed': 4,
'totalBackupsUsed': 0,
'totalGigabytesUsed': 35
}
absolute_attrs = absolute_attrs or {}
self.absolute_limits_attrs.update(absolute_attrs)
self.rate_limits_attrs = [{
"uri": "*",
"limit": [
{
"value": 10,
"verb": "POST",
"remaining": 2,
"unit": "MINUTE",
"next-available": "2011-12-15T22:42:45Z"
},
{
"value": 10,
"verb": "PUT",
"remaining": 2,
"unit": "MINUTE",
"next-available": "2011-12-15T22:42:45Z"
},
{
"value": 100,
"verb": "DELETE",
"remaining": 100,
"unit": "MINUTE",
"next-available": "2011-12-15T22:42:45Z"
}
]
}]
@property
def absolute(self):
for (name, value) in self.absolute_limits_attrs.items():
yield FakeAbsoluteLimit(name, value)
def absolute_limits(self):
reference_data = []
for (name, value) in self.absolute_limits_attrs.items():
reference_data.append((name, value))
return reference_data
@property
def rate(self):
for group in self.rate_limits_attrs:
uri = group['uri']
for rate in group['limit']:
yield FakeRateLimit(rate['verb'], uri, rate['value'],
rate['remaining'], rate['unit'],
rate['next-available'])
def rate_limits(self):
reference_data = []
for group in self.rate_limits_attrs:
uri = group['uri']
for rate in group['limit']:
reference_data.append((rate['verb'], uri, rate['value'],
rate['remaining'], rate['unit'],
rate['next-available']))
return reference_data
class FakeAbsoluteLimit(object):
"""Data model that represents an absolute limit."""
def __init__(self, name, value):
self.name = name
self.value = value
class FakeRateLimit(object):
"""Data model that represents a flattened view of a single rate limit."""
def __init__(self, verb, uri, value, remain,
unit, next_available):
self.verb = verb
self.uri = uri
self.value = value
self.remain = remain
self.unit = unit
self.next_available = next_available