Provide one resource for Compute v2 Limits

This change makes use of having Resources as property types and provides
one parent resource, Limits, to interact with the /limits REST API. As
this response body contains two different types of limits with two
different structures, it offers Limits.absolute and Limits.rate, which
replace the two separate resources that currently exist that require two
API calls to work with. Limits.absolute is an AbsoluteLimits instance,
and Limits.rate is a list of RateLimit instances.

The interaction would now work as follows:

my_limits = limits.Limits().get(session)
print(my_limits.absolute.server_meta) # 128 by default
for limits in my_limits.rate: # iterate over URIs with limits
    print(limits.uri) # prints "*/servers"
    for limit in limits:
        print(limit) # print the specific rate limits on this URI

Change-Id: I5363d2e39dcc57f9fd164ec88ab5b79abeaa18ed
Closes-Bug: 1422057
This commit is contained in:
Brian Curtin
2015-02-14 14:05:06 -06:00
committed by Brian Curtin
parent 9e44f605d3
commit fb0a4ed172
8 changed files with 272 additions and 262 deletions

View File

@@ -14,8 +14,7 @@ from openstack.compute.v2 import extension
from openstack.compute.v2 import flavor
from openstack.compute.v2 import image
from openstack.compute.v2 import keypair
from openstack.compute.v2 import limits_absolute
from openstack.compute.v2 import limits_rate
from openstack.compute.v2 import limits
from openstack.compute.v2 import server
from openstack.compute.v2 import server_interface
from openstack.compute.v2 import server_ip
@@ -86,17 +85,15 @@ class Proxy(object):
def update_keypair(self, **data):
return keypair.Keypair(data).update(self.session)
def find_limits_absolute(self, name_or_id):
return limits_absolute.LimitsAbsolute.find(self.session, name_or_id)
def limits(self):
"""Retrieve limits that are applied to the project's account
def list_limits_absolute(self):
return limits_absolute.LimitsAbsolute.list(self.session)
def find_limits_rate(self, name_or_id):
return limits_rate.LimitsRate.find(self.session, name_or_id)
def list_limits_rate(self):
return limits_rate.LimitsRate.list(self.session)
:returns: A Limits object, including both
:class:`~openstack.compute.v2.limits.AbsoluteLimits` and
:class:`~openstack.compute.v2.limits.RateLimits`
:rtype: :class:`~openstack.compute.v2.limits.Limits`
"""
return limits.Limits().get(self.session)
def create_server(self, **data):
return server.Server(data).create(self.session)

View File

@@ -0,0 +1,104 @@
# 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.compute import compute_service
from openstack import resource
class AbsoluteLimits(resource.Resource):
#: The maximum amount of key-value pairs to be set as image metadata.
image_meta = resource.prop("maxImageMeta")
#: The maximum number of personality contents that can be supplied.
personality = resource.prop("maxPersonality")
#: The maximum size, in bytes, of a personality.
personality_size = resource.prop("maxPersonalitySize")
#: The maximum amount of security group rules allowed.
security_group_rules = resource.prop("maxSecurityGroupRules")
#: The maximum amount of security groups allowed.
security_groups = resource.prop("maxSecurityGroups")
#: The amount of security groups currently in use.
security_groups_used = resource.prop("totalSecurityGroupsUsed")
#: The maximum amount of key-value pairs to be set as sever metadata.
server_meta = resource.prop("maxServerMeta")
#: The maximum amount of cores.
total_cores = resource.prop("maxTotalCores")
#: The amount of cores currently in use.
total_cores_used = resource.prop("totalCoresUsed")
#: The maximum amount of floating IPs.
floating_ips = resource.prop("maxTotalFloatingIps")
#: The amount of floating IPs currently in use.
floating_ips_used = resource.prop("totalFloatingIpsUsed")
#: The maximum amount of instances.
instances = resource.prop("maxTotalInstances")
#: The amount of instances currently in use.
instances_used = resource.prop("totalInstancesUsed")
#: The maximum amount of keypairs.
keypairs = resource.prop("maxTotalKeypairs")
#: The maximum RAM size in megabytes.
total_ram = resource.prop("maxTotalRAMSize")
#: The RAM size in megabytes currently in use.
total_ram_used = resource.prop("totalRAMUsed")
#: The maximum amount of server groups.
server_groups = resource.prop("maxServerGroups")
#: The amount of server groups currently in use.
server_groups_used = resource.prop("totalServerGroupsUsed")
#: The maximum number of members in a server group.
server_group_members = resource.prop("maxServerGroupMembers")
class RateLimits(resource.Resource):
#: A list of the specific limits that apply to the ``regex`` and ``uri``.
limits = resource.prop("limit", type=list)
#: A regex representing which routes this rate limit applies to.
regex = resource.prop("regex")
#: A URI representing which routes this rate limit applies to.
uri = resource.prop("uri")
class Limits(resource.Resource):
base_path = "/limits"
resource_key = "limits"
service = compute_service.ComputeService()
allow_retrieve = True
absolute = resource.prop("absolute", type=AbsoluteLimits)
rate = resource.prop("rate", type=list)
def get(self, session, include_headers=False):
"""Get the Limits resource.
:param session: The session to use for making this request.
:type session: :class:`~openstack.session.Session`
:returns: A Limits instance
:rtype: :class:`~openstack.compute.v2.limits.Limits`
"""
body = self.get_data_by_id(session, self.id, path_args=self,
include_headers=include_headers)
# Split the rates away from absolute limits. We can create
# the `absolute` property and AbsoluteLimits resource directly
# from the body. We have to iterate through the list inside `rate`
# in order to create the RateLimits instances for the `rate` property.
rate_body = body.pop("rate")
self._attrs.update(body)
rates = []
for rate in rate_body:
rates.append(RateLimits(rate))
self._attrs.update({"rate": rates})
self._loaded = True
return self

View File

@@ -1,38 +0,0 @@
# 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 six
from openstack.compute import compute_service
from openstack import resource
class LimitsAbsolute(resource.Resource):
resource_key = 'limits_absolute'
resources_key = 'limits_absolutes'
base_path = '/limits'
service = compute_service.ComputeService()
# capabilities
allow_list = True
# Properties
name = resource.prop('name')
value = resource.prop('value')
@classmethod
def list(cls, session, path_args=None, **params):
url = cls.base_path
resp = session.get(url, service=cls.service, params=params).body
resp = resp['limits']['absolute']
return [cls.existing(name=key, value=value)
for key, value in six.iteritems(resp)]

View File

@@ -1,36 +0,0 @@
# 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.compute import compute_service
from openstack import resource
class LimitsRate(resource.Resource):
resource_key = 'limits_rate'
resources_key = 'limits_rates'
base_path = '/limits'
service = compute_service.ComputeService()
# capabilities
allow_list = True
# Properties
limit = resource.prop('limit')
regex = resource.prop('regex')
uri = resource.prop('uri')
@classmethod
def list(cls, session, path_args=None, **params):
url = cls.base_path
resp = session.get(url, service=cls.service, params=params).body
resp = resp['limits']['rate']
return [cls.existing(**data) for data in resp]

View File

@@ -0,0 +1,155 @@
# 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
import testtools
from openstack.compute.v2 import limits
ABSOLUTE_LIMITS = {
"maxImageMeta": 128,
"maxPersonality": 5,
"maxPersonalitySize": 10240,
"maxSecurityGroupRules": 20,
"maxSecurityGroups": 10,
"maxServerMeta": 128,
"maxTotalCores": 20,
"maxTotalFloatingIps": 10,
"maxTotalInstances": 10,
"maxTotalKeypairs": 100,
"maxTotalRAMSize": 51200,
"maxServerGroups": 10,
"maxServerGroupMembers": 10,
"totalFloatingIpsUsed": 1,
"totalSecurityGroupsUsed": 2,
"totalRAMUsed": 4,
"totalInstancesUsed": 5,
"totalServerGroupsUsed": 6,
"totalCoresUsed": 7
}
RATE_LIMIT = {
"limit": [
{
"next-available": "2012-11-27T17:22:18Z",
"remaining": 120,
"unit": "MINUTE",
"value": 120,
"verb": "POST"
},
],
"regex": ".*",
"uri": "*"
}
LIMITS_BODY = {
"limits": {
"absolute": ABSOLUTE_LIMITS,
"rate": [RATE_LIMIT]
}
}
class TestAbsoluteLimits(testtools.TestCase):
def test_basic(self):
sot = limits.AbsoluteLimits()
self.assertIsNone(sot.resource_key)
self.assertIsNone(sot.resources_key)
self.assertEqual("", sot.base_path)
self.assertIsNone(sot.service)
self.assertFalse(sot.allow_create)
self.assertFalse(sot.allow_retrieve)
self.assertFalse(sot.allow_update)
self.assertFalse(sot.allow_delete)
self.assertFalse(sot.allow_list)
def test_make_it(self):
sot = limits.AbsoluteLimits(ABSOLUTE_LIMITS)
self.assertEqual(ABSOLUTE_LIMITS["maxImageMeta"], sot.image_meta)
self.assertEqual(ABSOLUTE_LIMITS["maxPersonality"], sot.personality)
self.assertEqual(ABSOLUTE_LIMITS["maxPersonalitySize"],
sot.personality_size)
self.assertEqual(ABSOLUTE_LIMITS["maxSecurityGroupRules"],
sot.security_group_rules)
self.assertEqual(ABSOLUTE_LIMITS["maxSecurityGroups"],
sot.security_groups)
self.assertEqual(ABSOLUTE_LIMITS["maxServerMeta"], sot.server_meta)
self.assertEqual(ABSOLUTE_LIMITS["maxTotalCores"], sot.total_cores)
self.assertEqual(ABSOLUTE_LIMITS["maxTotalFloatingIps"],
sot.floating_ips)
self.assertEqual(ABSOLUTE_LIMITS["maxTotalInstances"],
sot.instances)
self.assertEqual(ABSOLUTE_LIMITS["maxTotalKeypairs"],
sot.keypairs)
self.assertEqual(ABSOLUTE_LIMITS["maxTotalRAMSize"],
sot.total_ram)
self.assertEqual(ABSOLUTE_LIMITS["maxServerGroups"], sot.server_groups)
self.assertEqual(ABSOLUTE_LIMITS["maxServerGroupMembers"],
sot.server_group_members)
self.assertEqual(ABSOLUTE_LIMITS["totalFloatingIpsUsed"],
sot.floating_ips_used)
self.assertEqual(ABSOLUTE_LIMITS["totalSecurityGroupsUsed"],
sot.security_groups_used)
self.assertEqual(ABSOLUTE_LIMITS["totalRAMUsed"], sot.total_ram_used)
self.assertEqual(ABSOLUTE_LIMITS["totalInstancesUsed"],
sot.instances_used)
self.assertEqual(ABSOLUTE_LIMITS["totalServerGroupsUsed"],
sot.server_groups_used)
self.assertEqual(ABSOLUTE_LIMITS["totalCoresUsed"],
sot.total_cores_used)
class TestRateLimits(testtools.TestCase):
def test_basic(self):
sot = limits.RateLimits()
self.assertIsNone(sot.resource_key)
self.assertIsNone(sot.resources_key)
self.assertEqual("", sot.base_path)
self.assertIsNone(sot.service)
self.assertFalse(sot.allow_create)
self.assertFalse(sot.allow_retrieve)
self.assertFalse(sot.allow_update)
self.assertFalse(sot.allow_delete)
self.assertFalse(sot.allow_list)
def test_make_it(self):
sot = limits.RateLimits(RATE_LIMIT)
self.assertEqual(RATE_LIMIT["regex"], sot.regex)
self.assertEqual(RATE_LIMIT["uri"], sot.uri)
self.assertEqual(RATE_LIMIT["limit"], sot.limits)
class TestLimits(testtools.TestCase):
def test_basic(self):
sot = limits.Limits()
self.assertEqual("limits", sot.resource_key)
self.assertEqual("/limits", sot.base_path)
self.assertEqual("compute", sot.service.service_type)
self.assertTrue(sot.allow_retrieve)
self.assertFalse(sot.allow_create)
self.assertFalse(sot.allow_update)
self.assertFalse(sot.allow_delete)
self.assertFalse(sot.allow_list)
@mock.patch("openstack.resource.Resource.get_data_by_id")
def test_get(self, mock_get):
# Only return values under the limits key since that's our
# resource_key, which would be filtered out in get_data_by_id.
mock_get.return_value = LIMITS_BODY["limits"]
sot = limits.Limits().get("fake session")
self.assertEqual(sot.absolute, limits.AbsoluteLimits(ABSOLUTE_LIMITS))
self.assertEqual(sot.rate, [limits.RateLimits(RATE_LIMIT)])

View File

@@ -1,79 +0,0 @@
# 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
import six
import testtools
from openstack.compute.v2 import limits_absolute
IDENTIFIER = 'IDENTIFIER'
EXAMPLE = {
'name': 'maxImageMeta',
'value': '2',
}
FAKE_RESPONSES = {
"limits": {
"absolute": {
"maxImageMeta": 128,
"maxPersonality": 5,
"maxPersonalitySize": 10240,
"maxSecurityGroupRules": 20,
"maxSecurityGroups": 10,
"maxServerMeta": 128,
"maxTotalCores": 20,
"maxTotalFloatingIps": 10,
"maxTotalInstances": 10,
"maxTotalKeypairs": 100,
"maxTotalRAMSize": 51200,
}
}
}
class TestLimitsAbsolute(testtools.TestCase):
def test_basic(self):
sot = limits_absolute.LimitsAbsolute()
self.assertEqual('limits_absolute', sot.resource_key)
self.assertEqual('limits_absolutes', sot.resources_key)
self.assertEqual('/limits', sot.base_path)
self.assertEqual('compute', sot.service.service_type)
self.assertFalse(sot.allow_create)
self.assertFalse(sot.allow_retrieve)
self.assertFalse(sot.allow_update)
self.assertFalse(sot.allow_delete)
self.assertTrue(sot.allow_list)
def test_make_it(self):
sot = limits_absolute.LimitsAbsolute(EXAMPLE)
self.assertEqual(EXAMPLE['name'], sot.name)
self.assertEqual(EXAMPLE['value'], sot.value)
def test_list(self):
resp = mock.Mock()
resp.body = FAKE_RESPONSES
sess = mock.Mock()
sess.get = mock.MagicMock()
sess.get.return_value = resp
sot = limits_absolute.LimitsAbsolute()
resp = sot.list(sess)
url = '/limits'
sess.get.assert_called_with(url, service=sot.service, params={})
absolute = FAKE_RESPONSES['limits']['absolute']
cnt = 0
for key, value in six.iteritems(absolute):
self.assertEqual(key, resp[cnt].name)
self.assertEqual(value, resp[cnt].value)
cnt = cnt + 1

View File

@@ -1,80 +0,0 @@
# 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
import testtools
from openstack.compute.v2 import limits_rate
IDENTIFIER = 'IDENTIFIER'
EXAMPLE = {
'limit': '1',
'regex': '2',
'uri': '3',
}
FAKE_RESPONSES = {
"limits": {
"rate": [
{
"limit": "1",
"regex": ".*",
"uri": "*"
},
{
"limit": "2",
"regex": "^/servers",
"uri": "*/servers"
},
]
}
}
class TestLimitsRate(testtools.TestCase):
def test_basic(self):
sot = limits_rate.LimitsRate()
self.assertEqual('limits_rate', sot.resource_key)
self.assertEqual('limits_rates', sot.resources_key)
self.assertEqual('/limits', sot.base_path)
self.assertEqual('compute', sot.service.service_type)
self.assertFalse(sot.allow_create)
self.assertFalse(sot.allow_retrieve)
self.assertFalse(sot.allow_update)
self.assertFalse(sot.allow_delete)
self.assertTrue(sot.allow_list)
def test_make_it(self):
sot = limits_rate.LimitsRate(EXAMPLE)
self.assertEqual(EXAMPLE['limit'], sot.limit)
self.assertEqual(EXAMPLE['regex'], sot.regex)
self.assertEqual(EXAMPLE['uri'], sot.uri)
def test_list(self):
resp = mock.Mock()
resp.body = FAKE_RESPONSES
sess = mock.Mock()
sess.get = mock.MagicMock()
sess.get.return_value = resp
sot = limits_rate.LimitsRate()
resp = sot.list(sess)
url = '/limits'
sess.get.assert_called_with(url, service=sot.service, params={})
rate = FAKE_RESPONSES['limits']['rate']
cnt = 0
for value in rate:
self.assertEqual(value['limit'], resp[cnt].limit)
self.assertEqual(value['regex'], resp[cnt].regex)
self.assertEqual(value['uri'], resp[cnt].uri)
cnt = cnt + 1

View File

@@ -99,23 +99,10 @@ class TestComputeProxy(test_proxy_base.TestProxyBase):
self.verify_update('openstack.compute.v2.keypair.Keypair.update',
self.proxy.update_keypair)
def test_limits_absolute_find(self):
self.verify_find(
'openstack.compute.v2.limits_absolute.LimitsAbsolute.find',
self.proxy.find_limits_absolute)
def test_limits_absolute_list(self):
self.verify_list(
'openstack.compute.v2.limits_absolute.LimitsAbsolute.list',
self.proxy.list_limits_absolute)
def test_limits_rate_find(self):
self.verify_find('openstack.compute.v2.limits_rate.LimitsRate.find',
self.proxy.find_limits_rate)
def test_limits_rate_list(self):
self.verify_list('openstack.compute.v2.limits_rate.LimitsRate.list',
self.proxy.list_limits_rate)
def test_limits(self):
self.verify_get(
'openstack.compute.v2.limits.Limits.get',
self.proxy.limits)
def test_server_interface_create(self):
self.verify_create(