Merge "Added support for the Limits resource"

This commit is contained in:
Zuul
2021-03-23 14:25:32 +00:00
committed by Gerrit Code Review
5 changed files with 327 additions and 0 deletions

View File

@@ -13,6 +13,7 @@
from openstack.block_storage import _base_proxy
from openstack.block_storage.v3 import availability_zone
from openstack.block_storage.v3 import backup as _backup
from openstack.block_storage.v3 import limits as _limits
from openstack.block_storage.v3 import snapshot as _snapshot
from openstack.block_storage.v3 import stats as _stats
from openstack.block_storage.v3 import type as _type
@@ -514,6 +515,16 @@ class Proxy(_base_proxy.BaseBlockStorageProxy):
backup = self._get_resource(_backup.Backup, backup)
return backup.restore(self, volume_id=volume_id, name=name)
def get_limits(self):
"""Retrieves limits
:returns: A Limit object, including both
:class:`~openstack.block_storage.v3.limits.AbsoluteLimit` and
:class:`~openstack.block_storage.v3.limits.RateLimit`
:rtype: :class:`~openstack.block_storage.v3.limits.Limit`
"""
return self._get(_limits.Limit, requires_id=False)
def availability_zones(self):
"""Return a generator of availability zones

View File

@@ -0,0 +1,79 @@
# 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)

View File

@@ -0,0 +1,31 @@
# 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.tests.functional.block_storage.v3 import base
class TestLimits(base.BaseBlockStorageTest):
def test_get(self):
sot = self.conn.block_storage.get_limits()
self.assertIsNotNone(sot.absolute.max_total_backup_gigabytes)
self.assertIsNotNone(sot.absolute.max_total_backups)
self.assertIsNotNone(sot.absolute.max_total_snapshots)
self.assertIsNotNone(sot.absolute.max_total_volume_gigabytes)
self.assertIsNotNone(sot.absolute.max_total_volumes)
self.assertIsNotNone(sot.absolute.total_backup_gigabytes_used)
self.assertIsNotNone(sot.absolute.total_backups_used)
self.assertIsNotNone(sot.absolute.total_gigabytes_used)
self.assertIsNotNone(sot.absolute.total_snapshots_used)
self.assertIsNotNone(sot.absolute.total_volumes_used)
self.assertIsNotNone(sot.rate)

View File

@@ -0,0 +1,200 @@
# 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.v3 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)

View File

@@ -13,6 +13,7 @@ from unittest import mock
from openstack.block_storage.v3 import _proxy
from openstack.block_storage.v3 import backup
from openstack.block_storage.v3 import limits
from openstack.block_storage.v3 import snapshot
from openstack.block_storage.v3 import stats
from openstack.block_storage.v3 import type
@@ -230,3 +231,8 @@ class TestVolumeProxy(test_proxy_base.TestProxyBase):
expected_args=[self.proxy],
expected_kwargs={'volume_id': 'vol_id', 'name': 'name'}
)
def test_limits_get(self):
self.verify_get(
self.proxy.get_limits, limits.Limit, ignore_value=True,
expected_kwargs={'requires_id': False})