diff --git a/openstack/compute/v2/_proxy.py b/openstack/compute/v2/_proxy.py index a551d6b8..4fcdfb23 100644 --- a/openstack/compute/v2/_proxy.py +++ b/openstack/compute/v2/_proxy.py @@ -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) diff --git a/openstack/compute/v2/limits.py b/openstack/compute/v2/limits.py new file mode 100644 index 00000000..529ed1de --- /dev/null +++ b/openstack/compute/v2/limits.py @@ -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 diff --git a/openstack/compute/v2/limits_absolute.py b/openstack/compute/v2/limits_absolute.py deleted file mode 100644 index d52a8855..00000000 --- a/openstack/compute/v2/limits_absolute.py +++ /dev/null @@ -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)] diff --git a/openstack/compute/v2/limits_rate.py b/openstack/compute/v2/limits_rate.py deleted file mode 100644 index 156ccda9..00000000 --- a/openstack/compute/v2/limits_rate.py +++ /dev/null @@ -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] diff --git a/openstack/tests/compute/v2/test_limits.py b/openstack/tests/compute/v2/test_limits.py new file mode 100644 index 00000000..fd4a9a6a --- /dev/null +++ b/openstack/tests/compute/v2/test_limits.py @@ -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)]) diff --git a/openstack/tests/compute/v2/test_limits_absolute.py b/openstack/tests/compute/v2/test_limits_absolute.py deleted file mode 100644 index b22d9c26..00000000 --- a/openstack/tests/compute/v2/test_limits_absolute.py +++ /dev/null @@ -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 diff --git a/openstack/tests/compute/v2/test_limits_rate.py b/openstack/tests/compute/v2/test_limits_rate.py deleted file mode 100644 index c1be091b..00000000 --- a/openstack/tests/compute/v2/test_limits_rate.py +++ /dev/null @@ -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 diff --git a/openstack/tests/compute/v2/test_proxy.py b/openstack/tests/compute/v2/test_proxy.py index 0c89fa95..ad286b71 100644 --- a/openstack/tests/compute/v2/test_proxy.py +++ b/openstack/tests/compute/v2/test_proxy.py @@ -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(