Adding 'absolute-limits' and 'rate-limits'
Change-Id: Ie1dff62f3a3dd74e96c8cad7c079b7378d25ae46
This commit is contained in:
parent
11a4ecfe44
commit
959c44ff59
@ -268,7 +268,11 @@ class Resource(object):
|
|||||||
|
|
||||||
def _add_details(self, info):
|
def _add_details(self, info):
|
||||||
for (k, v) in info.iteritems():
|
for (k, v) in info.iteritems():
|
||||||
setattr(self, k, v)
|
try:
|
||||||
|
setattr(self, k, v)
|
||||||
|
except AttributeError:
|
||||||
|
# In this case we already defined the attribute on the class
|
||||||
|
pass
|
||||||
|
|
||||||
def __getattr__(self, k):
|
def __getattr__(self, k):
|
||||||
if k not in self.__dict__:
|
if k not in self.__dict__:
|
||||||
|
@ -3,10 +3,11 @@ from novaclient.v1_1 import flavors
|
|||||||
from novaclient.v1_1 import floating_ips
|
from novaclient.v1_1 import floating_ips
|
||||||
from novaclient.v1_1 import images
|
from novaclient.v1_1 import images
|
||||||
from novaclient.v1_1 import keypairs
|
from novaclient.v1_1 import keypairs
|
||||||
|
from novaclient.v1_1 import limits
|
||||||
|
from novaclient.v1_1 import quotas
|
||||||
from novaclient.v1_1 import security_group_rules
|
from novaclient.v1_1 import security_group_rules
|
||||||
from novaclient.v1_1 import security_groups
|
from novaclient.v1_1 import security_groups
|
||||||
from novaclient.v1_1 import servers
|
from novaclient.v1_1 import servers
|
||||||
from novaclient.v1_1 import quotas
|
|
||||||
from novaclient.v1_1 import volumes
|
from novaclient.v1_1 import volumes
|
||||||
from novaclient.v1_1 import volume_snapshots
|
from novaclient.v1_1 import volume_snapshots
|
||||||
from novaclient.v1_1 import zones
|
from novaclient.v1_1 import zones
|
||||||
@ -37,11 +38,12 @@ class Client(object):
|
|||||||
# know it's not being used as keyword argument
|
# know it's not being used as keyword argument
|
||||||
password = api_key
|
password = api_key
|
||||||
self.flavors = flavors.FlavorManager(self)
|
self.flavors = flavors.FlavorManager(self)
|
||||||
self.floating_ips = floating_ips.FloatingIPManager(self)
|
|
||||||
self.images = images.ImageManager(self)
|
self.images = images.ImageManager(self)
|
||||||
|
self.limits = limits.LimitsManager(self)
|
||||||
self.servers = servers.ServerManager(self)
|
self.servers = servers.ServerManager(self)
|
||||||
|
|
||||||
# extensions
|
# extensions
|
||||||
|
self.floating_ips = floating_ips.FloatingIPManager(self)
|
||||||
self.volumes = volumes.VolumeManager(self)
|
self.volumes = volumes.VolumeManager(self)
|
||||||
self.volume_snapshots = volume_snapshots.SnapshotManager(self)
|
self.volume_snapshots = volume_snapshots.SnapshotManager(self)
|
||||||
self.keypairs = keypairs.KeypairManager(self)
|
self.keypairs = keypairs.KeypairManager(self)
|
||||||
|
79
novaclient/v1_1/limits.py
Normal file
79
novaclient/v1_1/limits.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
# Copyright 2011 OpenStack LLC.
|
||||||
|
|
||||||
|
from novaclient import base
|
||||||
|
|
||||||
|
|
||||||
|
class Limits(base.Resource):
|
||||||
|
"""A collection of RateLimit and AbsoluteLimit objects"""
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Limits>"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def absolute(self):
|
||||||
|
for (name, value) in self._info['absolute'].items():
|
||||||
|
yield AbsoluteLimit(name, value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rate(self):
|
||||||
|
for group in self._info['rate']:
|
||||||
|
uri = group['uri']
|
||||||
|
regex = group['regex']
|
||||||
|
for rate in group['limit']:
|
||||||
|
yield RateLimit(rate['verb'], uri, regex, rate['value'],
|
||||||
|
rate['remaining'], rate['unit'],
|
||||||
|
rate['next-available'])
|
||||||
|
|
||||||
|
|
||||||
|
class RateLimit(object):
|
||||||
|
"""Data model that represents a flattened view of a single rate limit"""
|
||||||
|
|
||||||
|
def __init__(self, verb, uri, regex, value, remain,
|
||||||
|
unit, next_available):
|
||||||
|
self.verb = verb
|
||||||
|
self.uri = uri
|
||||||
|
self.regex = regex
|
||||||
|
self.value = value
|
||||||
|
self.remain = remain
|
||||||
|
self.unit = unit
|
||||||
|
self.next_available = next_available
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.uri == other.uri \
|
||||||
|
and self.regex == other.regex \
|
||||||
|
and self.value == other.value \
|
||||||
|
and self.verb == other.verb \
|
||||||
|
and self.remain == other.remain \
|
||||||
|
and self.unit == other.unit \
|
||||||
|
and self.next_available == other.next_available
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<RateLimit: method=%s uri=%s>" % (self.method, self.uri)
|
||||||
|
|
||||||
|
|
||||||
|
class AbsoluteLimit(object):
|
||||||
|
"""Data model that represents a single absolute limit"""
|
||||||
|
|
||||||
|
def __init__(self, name, value):
|
||||||
|
self.name = name
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.value == other.value and self.name == other.name
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<AbsoluteLimit: name=%s>" % (self.name)
|
||||||
|
|
||||||
|
|
||||||
|
class LimitsManager(base.Manager):
|
||||||
|
"""Manager object used to interact with limits resource"""
|
||||||
|
|
||||||
|
resource_class = Limits
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
"""
|
||||||
|
Get a specific extension.
|
||||||
|
|
||||||
|
:rtype: :class:`Limits`
|
||||||
|
"""
|
||||||
|
return self._get("/limits", "limits")
|
@ -1207,3 +1207,17 @@ def do_keypair_list(cs, args):
|
|||||||
keypairs = cs.keypairs.list()
|
keypairs = cs.keypairs.list()
|
||||||
columns = ['Name', 'Fingerprint']
|
columns = ['Name', 'Fingerprint']
|
||||||
utils.print_list(keypairs, columns)
|
utils.print_list(keypairs, columns)
|
||||||
|
|
||||||
|
|
||||||
|
def do_absolute_limits(cs, args):
|
||||||
|
"""Print a list of absolute limits for a user"""
|
||||||
|
limits = cs.limits.get().absolute
|
||||||
|
columns = ['Name', 'Value']
|
||||||
|
utils.print_list(limits, columns)
|
||||||
|
|
||||||
|
|
||||||
|
def do_rate_limits(cs, args):
|
||||||
|
"""Print a list of rate limits for a user"""
|
||||||
|
limits = cs.limits.get().rate
|
||||||
|
columns = ['Verb', 'URI', 'Value', 'Remain', 'Unit', 'Next_Available']
|
||||||
|
utils.print_list(limits, columns)
|
||||||
|
2
setup.py
2
setup.py
@ -28,7 +28,7 @@ def read_file(file_name):
|
|||||||
|
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
name="python-novaclient",
|
name="python-novaclient",
|
||||||
version="2.6.8",
|
version="2.6.9",
|
||||||
author="Rackspace, based on work by Jacob Kaplan-Moss",
|
author="Rackspace, based on work by Jacob Kaplan-Moss",
|
||||||
author_email="github@racklabs.com",
|
author_email="github@racklabs.com",
|
||||||
description="Client library for OpenStack Nova API.",
|
description="Client library for OpenStack Nova API.",
|
||||||
|
@ -55,56 +55,55 @@ class FakeHTTPClient(base_client.HTTPClient):
|
|||||||
return (200, {"limits": {
|
return (200, {"limits": {
|
||||||
"rate": [
|
"rate": [
|
||||||
{
|
{
|
||||||
"verb": "POST",
|
"uri": "*",
|
||||||
"URI": "*",
|
|
||||||
"regex": ".*",
|
"regex": ".*",
|
||||||
"value": 10,
|
"limit": [
|
||||||
"remaining": 2,
|
{
|
||||||
"unit": "MINUTE",
|
"value": 10,
|
||||||
"resetTime": 1244425439
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"verb": "POST",
|
"uri": "*/servers",
|
||||||
"URI": "*/servers",
|
|
||||||
"regex": "^/servers",
|
"regex": "^/servers",
|
||||||
"value": 50,
|
"limit": [
|
||||||
"remaining": 49,
|
{
|
||||||
"unit": "DAY", "resetTime": 1244511839
|
"verb": "POST",
|
||||||
},
|
"value": 25,
|
||||||
{
|
"remaining": 24,
|
||||||
"verb": "PUT",
|
"unit": "DAY",
|
||||||
"URI": "*",
|
"next-available": "2011-12-15T22:42:45Z"
|
||||||
"regex": ".*",
|
}
|
||||||
"value": 10,
|
]
|
||||||
"remaining": 2,
|
|
||||||
"unit": "MINUTE",
|
|
||||||
"resetTime": 1244425439
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"verb": "GET",
|
|
||||||
"URI": "*changes-since*",
|
|
||||||
"regex": "changes-since",
|
|
||||||
"value": 3,
|
|
||||||
"remaining": 3,
|
|
||||||
"unit": "MINUTE",
|
|
||||||
"resetTime": 1244425439
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"verb": "DELETE",
|
|
||||||
"URI": "*",
|
|
||||||
"regex": ".*",
|
|
||||||
"value": 100,
|
|
||||||
"remaining": 100,
|
|
||||||
"unit": "MINUTE",
|
|
||||||
"resetTime": 1244425439
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"absolute": {
|
"absolute": {
|
||||||
"maxTotalRAMSize": 51200,
|
"maxTotalRAMSize": 51200,
|
||||||
"maxIPGroups": 50,
|
"maxServerMeta": 5,
|
||||||
"maxIPGroupMembers": 25
|
"maxImageMeta": 5,
|
||||||
}
|
"maxPersonality": 5,
|
||||||
}})
|
"maxPersonalitySize": 10240
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
#
|
#
|
||||||
# Servers
|
# Servers
|
||||||
|
52
tests/v1_1/test_limits.py
Normal file
52
tests/v1_1/test_limits.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
|
||||||
|
from novaclient.v1_1 import limits
|
||||||
|
from tests.v1_1 import fakes
|
||||||
|
from tests import utils
|
||||||
|
|
||||||
|
|
||||||
|
cs = fakes.FakeClient()
|
||||||
|
|
||||||
|
|
||||||
|
class LimitsTest(utils.TestCase):
|
||||||
|
|
||||||
|
def test_get_limits(self):
|
||||||
|
obj = cs.limits.get()
|
||||||
|
cs.assert_called('GET', '/limits')
|
||||||
|
self.assertTrue(isinstance(obj, limits.Limits))
|
||||||
|
|
||||||
|
def test_absolute_limits(self):
|
||||||
|
obj = cs.limits.get()
|
||||||
|
|
||||||
|
expected = (
|
||||||
|
limits.AbsoluteLimit("maxTotalRAMSize", 51200),
|
||||||
|
limits.AbsoluteLimit("maxServerMeta", 5),
|
||||||
|
limits.AbsoluteLimit("maxImageMeta", 5),
|
||||||
|
limits.AbsoluteLimit("maxPersonality", 5),
|
||||||
|
limits.AbsoluteLimit("maxPersonalitySize", 10240),
|
||||||
|
)
|
||||||
|
|
||||||
|
abs_limits = list(obj.absolute)
|
||||||
|
self.assertEqual(len(abs_limits), len(expected))
|
||||||
|
|
||||||
|
for limit in abs_limits:
|
||||||
|
self.assertTrue(limit in expected)
|
||||||
|
|
||||||
|
def test_rate_limits(self):
|
||||||
|
obj = cs.limits.get()
|
||||||
|
|
||||||
|
expected = (
|
||||||
|
limits.RateLimit('POST', '*', '.*', 10, 2, 'MINUTE',
|
||||||
|
'2011-12-15T22:42:45Z' ),
|
||||||
|
limits.RateLimit('PUT', '*', '.*', 10, 2, 'MINUTE',
|
||||||
|
'2011-12-15T22:42:45Z' ),
|
||||||
|
limits.RateLimit('DELETE', '*', '.*', 100, 100, 'MINUTE',
|
||||||
|
'2011-12-15T22:42:45Z' ),
|
||||||
|
limits.RateLimit('POST', '*/servers', '^/servers', 25, 24, 'DAY',
|
||||||
|
'2011-12-15T22:42:45Z' ),
|
||||||
|
)
|
||||||
|
|
||||||
|
rate_limits = list(obj.rate)
|
||||||
|
self.assertEqual(len(rate_limits), len(expected))
|
||||||
|
|
||||||
|
for limit in rate_limits:
|
||||||
|
self.assertTrue(limit in expected)
|
Loading…
x
Reference in New Issue
Block a user