volume: Add Limit to volume v2 API
Another case of adding a resource from v3 to the legacy v2 API. We tackled Extension in change I1e4528f5a5d8e2caaaf204792ddcee7267e4c2e9 and Capability in change I98252ddd0dadba2bfa1aaf97b59932a19c396cd4. Now it's time for Limit. Note that the API is unchanged between v2 and v3 so this can be a straightforward copy-paste job. This was determined by inspecting the cinder code before the v2 API was removed (e.g. commit 'e05b261af~', file 'cinder/api/views/limits.py'). Change-Id: I5a375d8dee7e68368f2e454851812a3638acf9ee Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
This commit is contained in:
parent
7235634f74
commit
5dd7d64769
@ -33,6 +33,13 @@ Capabilities Operations
|
||||
:noindex:
|
||||
:members: get_capabilities
|
||||
|
||||
Limits Operations
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. autoclass:: openstack.block_storage.v2._proxy.Proxy
|
||||
:noindex:
|
||||
:members: get_limits
|
||||
|
||||
Type Operations
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
|
37
doc/source/user/resources/block_storage/v2/limits.rst
Normal file
37
doc/source/user/resources/block_storage/v2/limits.rst
Normal file
@ -0,0 +1,37 @@
|
||||
openstack.block_storage.v2.limits
|
||||
=================================
|
||||
|
||||
.. automodule:: openstack.block_storage.v2.limits
|
||||
|
||||
The AbsoluteLimit Class
|
||||
-----------------------
|
||||
|
||||
The ``AbsoluteLimit`` class inherits from
|
||||
:class:`~openstack.resource.Resource`.
|
||||
|
||||
.. autoclass:: openstack.block_storage.v2.limits.AbsoluteLimit
|
||||
:members:
|
||||
|
||||
The Limit Class
|
||||
---------------
|
||||
|
||||
The ``Limit`` class inherits from :class:`~openstack.resource.Resource`.
|
||||
|
||||
.. autoclass:: openstack.block_storage.v2.limits.Limit
|
||||
:members:
|
||||
|
||||
The RateLimit Class
|
||||
-------------------
|
||||
|
||||
The ``RateLimit`` class inherits from :class:`~openstack.resource.Resource`.
|
||||
|
||||
.. autoclass:: openstack.block_storage.v2.limits.RateLimit
|
||||
:members:
|
||||
|
||||
The RateLimits Class
|
||||
--------------------
|
||||
|
||||
The ``RateLimits`` class inherits from :class:`~openstack.resource.Resource`.
|
||||
|
||||
.. autoclass:: openstack.block_storage.v2.limits.RateLimits
|
||||
:members:
|
@ -14,6 +14,7 @@ from openstack.block_storage import _base_proxy
|
||||
from openstack.block_storage.v2 import backup as _backup
|
||||
from openstack.block_storage.v2 import capabilities as _capabilities
|
||||
from openstack.block_storage.v2 import extension as _extension
|
||||
from openstack.block_storage.v2 import limits as _limits
|
||||
from openstack.block_storage.v2 import quota_set as _quota_set
|
||||
from openstack.block_storage.v2 import snapshot as _snapshot
|
||||
from openstack.block_storage.v2 import stats as _stats
|
||||
@ -610,6 +611,23 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
|
||||
backup = self._get_resource(_backup.Backup, backup)
|
||||
backup.reset(self, status)
|
||||
|
||||
# ====== LIMITS ======
|
||||
def get_limits(self, project=None):
|
||||
"""Retrieves limits
|
||||
|
||||
:param project: A project to get limits for. The value can be either
|
||||
the ID of a project or an
|
||||
:class:`~openstack.identity.v2.project.Project` instance.
|
||||
:returns: A Limit object, including both
|
||||
:class:`~openstack.block_storage.v2.limits.AbsoluteLimit` and
|
||||
:class:`~openstack.block_storage.v2.limits.RateLimit`
|
||||
:rtype: :class:`~openstack.block_storage.v2.limits.Limit`
|
||||
"""
|
||||
params = {}
|
||||
if project:
|
||||
params['project_id'] = resource.Resource._get_id(project)
|
||||
return self._get(_limits.Limit, requires_id=False, **params)
|
||||
|
||||
# ====== CAPABILITIES ======
|
||||
def get_capabilities(self, host):
|
||||
"""Get a backend's capabilites
|
||||
|
82
openstack/block_storage/v2/limits.py
Normal file
82
openstack/block_storage/v2/limits.py
Normal file
@ -0,0 +1,82 @@
|
||||
# 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 openstack import resource
|
||||
|
||||
|
||||
class AbsoluteLimit(resource.Resource):
|
||||
#: Properties
|
||||
#: The maximum total amount of backups, in gibibytes (GiB).
|
||||
max_total_backup_gigabytes = resource.Body(
|
||||
"maxTotalBackupGigabytes", type=int
|
||||
)
|
||||
#: The maximum number of backups.
|
||||
max_total_backups = resource.Body("maxTotalBackups", type=int)
|
||||
#: The maximum number of snapshots.
|
||||
max_total_snapshots = resource.Body("maxTotalSnapshots", type=int)
|
||||
#: The maximum total amount of volumes, in gibibytes (GiB).
|
||||
max_total_volume_gigabytes = resource.Body(
|
||||
"maxTotalVolumeGigabytes", type=int
|
||||
)
|
||||
#: The maximum number of volumes.
|
||||
max_total_volumes = resource.Body("maxTotalVolumes", type=int)
|
||||
#: The total number of backups gibibytes (GiB) used.
|
||||
total_backup_gigabytes_used = resource.Body(
|
||||
"totalBackupGigabytesUsed", type=int
|
||||
)
|
||||
#: The total number of backups used.
|
||||
total_backups_used = resource.Body("totalBackupsUsed", type=int)
|
||||
#: The total number of gibibytes (GiB) used.
|
||||
total_gigabytes_used = resource.Body("totalGigabytesUsed", type=int)
|
||||
#: The total number of snapshots used.
|
||||
total_snapshots_used = resource.Body("totalSnapshotsUsed", type=int)
|
||||
#: The total number of volumes used.
|
||||
total_volumes_used = resource.Body("totalVolumesUsed", type=int)
|
||||
|
||||
|
||||
class RateLimit(resource.Resource):
|
||||
#: Properties
|
||||
#: Rate limits next availabe time.
|
||||
next_available = resource.Body("next-available")
|
||||
#: Integer for rate limits remaining.
|
||||
remaining = resource.Body("remaining", type=int)
|
||||
#: Unit of measurement for the value parameter.
|
||||
unit = resource.Body("unit")
|
||||
#: Integer number of requests which can be made.
|
||||
value = resource.Body("value", type=int)
|
||||
#: An HTTP verb (POST, PUT, etc.).
|
||||
verb = resource.Body("verb")
|
||||
|
||||
|
||||
class RateLimits(resource.Resource):
|
||||
#: Properties
|
||||
#: A list of the specific limits that apply to the ``regex`` and ``uri``.
|
||||
limits = resource.Body("limit", type=list, list_type=RateLimit)
|
||||
#: A regex representing which routes this rate limit applies to.
|
||||
regex = resource.Body("regex")
|
||||
#: A URI representing which routes this rate limit applies to.
|
||||
uri = resource.Body("uri")
|
||||
|
||||
|
||||
class Limit(resource.Resource):
|
||||
resource_key = "limits"
|
||||
base_path = "/limits"
|
||||
|
||||
# capabilities
|
||||
allow_fetch = True
|
||||
|
||||
#: Properties
|
||||
#: An absolute limits object.
|
||||
absolute = resource.Body("absolute", type=AbsoluteLimit)
|
||||
#: Rate-limit volume copy bandwidth, used to mitigate
|
||||
#: slow down of data access from the instances.
|
||||
rate = resource.Body("rate", type=list, list_type=RateLimits)
|
206
openstack/tests/unit/block_storage/v2/test_limits.py
Normal file
206
openstack/tests/unit/block_storage/v2/test_limits.py
Normal file
@ -0,0 +1,206 @@
|
||||
# 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 openstack.block_storage.v2 import limits
|
||||
from openstack.tests.unit import base
|
||||
|
||||
ABSOLUTE_LIMIT = {
|
||||
"totalSnapshotsUsed": 1,
|
||||
"maxTotalBackups": 10,
|
||||
"maxTotalVolumeGigabytes": 1000,
|
||||
"maxTotalSnapshots": 10,
|
||||
"maxTotalBackupGigabytes": 1000,
|
||||
"totalBackupGigabytesUsed": 1,
|
||||
"maxTotalVolumes": 10,
|
||||
"totalVolumesUsed": 2,
|
||||
"totalBackupsUsed": 3,
|
||||
"totalGigabytesUsed": 2,
|
||||
}
|
||||
|
||||
RATE_LIMIT = {
|
||||
"verb": "POST",
|
||||
"value": 80,
|
||||
"remaining": 80,
|
||||
"unit": "MINUTE",
|
||||
"next-available": "2021-02-23T22:08:00Z",
|
||||
}
|
||||
|
||||
RATE_LIMITS = {"regex": ".*", "uri": "*", "limit": [RATE_LIMIT]}
|
||||
|
||||
LIMIT = {"rate": [RATE_LIMITS], "absolute": ABSOLUTE_LIMIT}
|
||||
|
||||
|
||||
class TestAbsoluteLimit(base.TestCase):
|
||||
def test_basic(self):
|
||||
limit_resource = limits.AbsoluteLimit()
|
||||
self.assertIsNone(limit_resource.resource_key)
|
||||
self.assertIsNone(limit_resource.resources_key)
|
||||
self.assertEqual('', limit_resource.base_path)
|
||||
self.assertFalse(limit_resource.allow_create)
|
||||
self.assertFalse(limit_resource.allow_fetch)
|
||||
self.assertFalse(limit_resource.allow_delete)
|
||||
self.assertFalse(limit_resource.allow_commit)
|
||||
self.assertFalse(limit_resource.allow_list)
|
||||
|
||||
def test_make_absolute_limit(self):
|
||||
limit_resource = limits.AbsoluteLimit(**ABSOLUTE_LIMIT)
|
||||
self.assertEqual(
|
||||
ABSOLUTE_LIMIT['totalSnapshotsUsed'],
|
||||
limit_resource.total_snapshots_used,
|
||||
)
|
||||
self.assertEqual(
|
||||
ABSOLUTE_LIMIT['maxTotalBackups'], limit_resource.max_total_backups
|
||||
)
|
||||
self.assertEqual(
|
||||
ABSOLUTE_LIMIT['maxTotalVolumeGigabytes'],
|
||||
limit_resource.max_total_volume_gigabytes,
|
||||
)
|
||||
self.assertEqual(
|
||||
ABSOLUTE_LIMIT['maxTotalSnapshots'],
|
||||
limit_resource.max_total_snapshots,
|
||||
)
|
||||
self.assertEqual(
|
||||
ABSOLUTE_LIMIT['maxTotalBackupGigabytes'],
|
||||
limit_resource.max_total_backup_gigabytes,
|
||||
)
|
||||
self.assertEqual(
|
||||
ABSOLUTE_LIMIT['totalBackupGigabytesUsed'],
|
||||
limit_resource.total_backup_gigabytes_used,
|
||||
)
|
||||
self.assertEqual(
|
||||
ABSOLUTE_LIMIT['maxTotalVolumes'], limit_resource.max_total_volumes
|
||||
)
|
||||
self.assertEqual(
|
||||
ABSOLUTE_LIMIT['totalVolumesUsed'],
|
||||
limit_resource.total_volumes_used,
|
||||
)
|
||||
self.assertEqual(
|
||||
ABSOLUTE_LIMIT['totalBackupsUsed'],
|
||||
limit_resource.total_backups_used,
|
||||
)
|
||||
self.assertEqual(
|
||||
ABSOLUTE_LIMIT['totalGigabytesUsed'],
|
||||
limit_resource.total_gigabytes_used,
|
||||
)
|
||||
|
||||
|
||||
class TestRateLimit(base.TestCase):
|
||||
def test_basic(self):
|
||||
limit_resource = limits.RateLimit()
|
||||
self.assertIsNone(limit_resource.resource_key)
|
||||
self.assertIsNone(limit_resource.resources_key)
|
||||
self.assertEqual('', limit_resource.base_path)
|
||||
self.assertFalse(limit_resource.allow_create)
|
||||
self.assertFalse(limit_resource.allow_fetch)
|
||||
self.assertFalse(limit_resource.allow_delete)
|
||||
self.assertFalse(limit_resource.allow_commit)
|
||||
self.assertFalse(limit_resource.allow_list)
|
||||
|
||||
def test_make_rate_limit(self):
|
||||
limit_resource = limits.RateLimit(**RATE_LIMIT)
|
||||
self.assertEqual(RATE_LIMIT['verb'], limit_resource.verb)
|
||||
self.assertEqual(RATE_LIMIT['value'], limit_resource.value)
|
||||
self.assertEqual(RATE_LIMIT['remaining'], limit_resource.remaining)
|
||||
self.assertEqual(RATE_LIMIT['unit'], limit_resource.unit)
|
||||
self.assertEqual(
|
||||
RATE_LIMIT['next-available'], limit_resource.next_available
|
||||
)
|
||||
|
||||
|
||||
class TestRateLimits(base.TestCase):
|
||||
def test_basic(self):
|
||||
limit_resource = limits.RateLimits()
|
||||
self.assertIsNone(limit_resource.resource_key)
|
||||
self.assertIsNone(limit_resource.resources_key)
|
||||
self.assertEqual('', limit_resource.base_path)
|
||||
self.assertFalse(limit_resource.allow_create)
|
||||
self.assertFalse(limit_resource.allow_fetch)
|
||||
self.assertFalse(limit_resource.allow_delete)
|
||||
self.assertFalse(limit_resource.allow_commit)
|
||||
self.assertFalse(limit_resource.allow_list)
|
||||
|
||||
def _test_rate_limit(self, expected, actual):
|
||||
self.assertEqual(expected[0]['verb'], actual[0].verb)
|
||||
self.assertEqual(expected[0]['value'], actual[0].value)
|
||||
self.assertEqual(expected[0]['remaining'], actual[0].remaining)
|
||||
self.assertEqual(expected[0]['unit'], actual[0].unit)
|
||||
self.assertEqual(
|
||||
expected[0]['next-available'], actual[0].next_available
|
||||
)
|
||||
|
||||
def test_make_rate_limits(self):
|
||||
limit_resource = limits.RateLimits(**RATE_LIMITS)
|
||||
self.assertEqual(RATE_LIMITS['regex'], limit_resource.regex)
|
||||
self.assertEqual(RATE_LIMITS['uri'], limit_resource.uri)
|
||||
self._test_rate_limit(RATE_LIMITS['limit'], limit_resource.limits)
|
||||
|
||||
|
||||
class TestLimit(base.TestCase):
|
||||
def test_basic(self):
|
||||
limit_resource = limits.Limit()
|
||||
self.assertEqual('limits', limit_resource.resource_key)
|
||||
self.assertEqual('/limits', limit_resource.base_path)
|
||||
self.assertTrue(limit_resource.allow_fetch)
|
||||
self.assertFalse(limit_resource.allow_create)
|
||||
self.assertFalse(limit_resource.allow_commit)
|
||||
self.assertFalse(limit_resource.allow_delete)
|
||||
self.assertFalse(limit_resource.allow_list)
|
||||
|
||||
def _test_absolute_limit(self, expected, actual):
|
||||
self.assertEqual(
|
||||
expected['totalSnapshotsUsed'], actual.total_snapshots_used
|
||||
)
|
||||
self.assertEqual(expected['maxTotalBackups'], actual.max_total_backups)
|
||||
self.assertEqual(
|
||||
expected['maxTotalVolumeGigabytes'],
|
||||
actual.max_total_volume_gigabytes,
|
||||
)
|
||||
self.assertEqual(
|
||||
expected['maxTotalSnapshots'], actual.max_total_snapshots
|
||||
)
|
||||
self.assertEqual(
|
||||
expected['maxTotalBackupGigabytes'],
|
||||
actual.max_total_backup_gigabytes,
|
||||
)
|
||||
self.assertEqual(
|
||||
expected['totalBackupGigabytesUsed'],
|
||||
actual.total_backup_gigabytes_used,
|
||||
)
|
||||
self.assertEqual(expected['maxTotalVolumes'], actual.max_total_volumes)
|
||||
self.assertEqual(
|
||||
expected['totalVolumesUsed'], actual.total_volumes_used
|
||||
)
|
||||
self.assertEqual(
|
||||
expected['totalBackupsUsed'], actual.total_backups_used
|
||||
)
|
||||
self.assertEqual(
|
||||
expected['totalGigabytesUsed'], actual.total_gigabytes_used
|
||||
)
|
||||
|
||||
def _test_rate_limit(self, expected, actual):
|
||||
self.assertEqual(expected[0]['verb'], actual[0].verb)
|
||||
self.assertEqual(expected[0]['value'], actual[0].value)
|
||||
self.assertEqual(expected[0]['remaining'], actual[0].remaining)
|
||||
self.assertEqual(expected[0]['unit'], actual[0].unit)
|
||||
self.assertEqual(
|
||||
expected[0]['next-available'], actual[0].next_available
|
||||
)
|
||||
|
||||
def _test_rate_limits(self, expected, actual):
|
||||
self.assertEqual(expected[0]['regex'], actual[0].regex)
|
||||
self.assertEqual(expected[0]['uri'], actual[0].uri)
|
||||
self._test_rate_limit(expected[0]['limit'], actual[0].limits)
|
||||
|
||||
def test_make_limit(self):
|
||||
limit_resource = limits.Limit(**LIMIT)
|
||||
self._test_rate_limits(LIMIT['rate'], limit_resource.rate)
|
||||
self._test_absolute_limit(LIMIT['absolute'], limit_resource.absolute)
|
@ -14,6 +14,7 @@ from unittest import mock
|
||||
from openstack.block_storage.v2 import _proxy
|
||||
from openstack.block_storage.v2 import backup
|
||||
from openstack.block_storage.v2 import capabilities
|
||||
from openstack.block_storage.v2 import limits
|
||||
from openstack.block_storage.v2 import quota_set
|
||||
from openstack.block_storage.v2 import snapshot
|
||||
from openstack.block_storage.v2 import stats
|
||||
@ -290,6 +291,16 @@ class TestBackup(TestVolumeProxy):
|
||||
)
|
||||
|
||||
|
||||
class TestLimit(TestVolumeProxy):
|
||||
def test_limits_get(self):
|
||||
self.verify_get(
|
||||
self.proxy.get_limits,
|
||||
limits.Limit,
|
||||
method_args=[],
|
||||
expected_kwargs={'requires_id': False},
|
||||
)
|
||||
|
||||
|
||||
class TestCapabilities(TestVolumeProxy):
|
||||
def test_capabilites_get(self):
|
||||
self.verify_get(self.proxy.get_capabilities, capabilities.Capabilities)
|
||||
|
Loading…
x
Reference in New Issue
Block a user