Skip to content

Commit 1a32329

Browse files
TPT-4224: Add global quotas and quota usage support for OBJ services (#661)
* Add global quotas and quota usage support for OBJ services * make format * Fix test
1 parent 03c5f8c commit 1a32329

9 files changed

Lines changed: 234 additions & 4 deletions

linode_api4/groups/object_storage.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
ObjectStorageACL,
2020
ObjectStorageBucket,
2121
ObjectStorageCluster,
22+
ObjectStorageGlobalQuota,
2223
ObjectStorageKeyPermission,
2324
ObjectStorageKeys,
2425
ObjectStorageQuota,
@@ -533,3 +534,18 @@ def quotas(self, *filters):
533534
:rtype: PaginatedList of ObjectStorageQuota
534535
"""
535536
return self.client._get_and_filter(ObjectStorageQuota, *filters)
537+
538+
def global_quotas(self, *filters):
539+
"""
540+
Lists the active account-level Object Storage quotas applied to your account.
541+
542+
API Documentation: TBD
543+
544+
:param filters: Any number of filters to apply to this query.
545+
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
546+
for more details on filtering.
547+
548+
:returns: A list of account-level Object Storage Quotas that matched the query.
549+
:rtype: PaginatedList of ObjectStorageGlobalQuota
550+
"""
551+
return self.client._get_and_filter(ObjectStorageGlobalQuota, *filters)

linode_api4/objects/object_storage.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,8 @@ class ObjectStorageQuota(Base):
596596
"description": Property(),
597597
"quota_limit": Property(),
598598
"resource_metric": Property(),
599+
"quota_type": Property(),
600+
"has_usage": Property(),
599601
}
600602

601603
def usage(self):
@@ -614,3 +616,41 @@ def usage(self):
614616
)
615617

616618
return ObjectStorageQuotaUsage.from_json(result)
619+
620+
621+
class ObjectStorageGlobalQuota(Base):
622+
"""
623+
An account-level Object Storage quota.
624+
625+
API documentation: TBD
626+
"""
627+
628+
api_endpoint = "/object-storage/global-quotas/{quota_id}"
629+
id_attribute = "quota_id"
630+
631+
properties = {
632+
"quota_id": Property(identifier=True),
633+
"quota_type": Property(),
634+
"quota_name": Property(),
635+
"description": Property(),
636+
"resource_metric": Property(),
637+
"quota_limit": Property(),
638+
"has_usage": Property(),
639+
}
640+
641+
def usage(self):
642+
"""
643+
Gets usage data for a specific account-level Object Storage quota.
644+
645+
API documentation: https://techdocs.akamai.com/linode-api/reference/get-object-storage-global-quota-usage
646+
647+
:returns: The Object Storage Global Quota usage.
648+
:rtype: ObjectStorageQuotaUsage
649+
"""
650+
651+
result = self._client.get(
652+
f"{type(self).api_endpoint}/usage",
653+
model=self,
654+
)
655+
656+
return ObjectStorageQuotaUsage.from_json(result)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"data": [
3+
{
4+
"quota_id": "obj-access-keys-per-account",
5+
"quota_type": "obj-access-keys",
6+
"quota_name": "Object Storage Access Keys per Account",
7+
"description": "Maximum number of access keys this customer is allowed to have on their account.",
8+
"resource_metric": "access_key",
9+
"quota_limit": 100,
10+
"has_usage": true
11+
},
12+
{
13+
"quota_id": "obj-total-capacity-per-account",
14+
"quota_type": "obj-total-capacity",
15+
"quota_name": "Object Storage Total Capacity per Account",
16+
"description": "Maximum total storage capacity in bytes this customer is allowed on their account.",
17+
"resource_metric": "byte",
18+
"quota_limit": 1099511627776,
19+
"has_usage": true
20+
}
21+
],
22+
"page": 1,
23+
"pages": 1,
24+
"results": 2
25+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"quota_id": "obj-access-keys-per-account",
3+
"quota_type": "obj-access-keys",
4+
"quota_name": "Object Storage Access Keys per Account",
5+
"description": "Maximum number of access keys this customer is allowed to have on their account.",
6+
"resource_metric": "access_key",
7+
"quota_limit": 100,
8+
"has_usage": true
9+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"quota_limit": 100,
3+
"usage": 25
4+
}

test/fixtures/object-storage_quotas.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
"endpoint_type": "E1",
88
"s3_endpoint": "us-iad-1.linodeobjects.com",
99
"quota_limit": 50,
10-
"resource_metric": "object"
10+
"resource_metric": "object",
11+
"quota_type": "obj-objects",
12+
"has_usage": true
1113
},
1214
{
1315
"quota_id": "obj-bucket-us-ord-1",
@@ -16,7 +18,9 @@
1618
"endpoint_type": "E1",
1719
"s3_endpoint": "us-iad-1.linodeobjects.com",
1820
"quota_limit": 50,
19-
"resource_metric": "bucket"
21+
"resource_metric": "bucket",
22+
"quota_type": "obj-bucket",
23+
"has_usage": true
2024
}
2125
],
2226
"page": 1,

test/fixtures/object-storage_quotas_obj-objects-us-ord-1.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,7 @@
55
"endpoint_type": "E1",
66
"s3_endpoint": "us-iad-1.linodeobjects.com",
77
"quota_limit": 50,
8-
"resource_metric": "object"
8+
"resource_metric": "object",
9+
"quota_type": "obj-objects",
10+
"has_usage": true
911
}

test/integration/models/object_storage/test_obj_quotas.py

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import pytest
22

3+
from linode_api4.errors import ApiError
34
from linode_api4.objects.object_storage import (
5+
ObjectStorageGlobalQuota,
46
ObjectStorageQuota,
57
ObjectStorageQuotaUsage,
68
)
@@ -25,6 +27,8 @@ def test_list_and_get_obj_storage_quotas(test_linode_client):
2527
assert found_quota.description == get_quota.description
2628
assert found_quota.quota_limit == get_quota.quota_limit
2729
assert found_quota.resource_metric == get_quota.resource_metric
30+
assert found_quota.quota_type == get_quota.quota_type
31+
assert found_quota.has_usage == get_quota.has_usage
2832

2933

3034
def test_get_obj_storage_quota_usage(test_linode_client):
@@ -33,7 +37,21 @@ def test_get_obj_storage_quota_usage(test_linode_client):
3337
if len(quotas) < 1:
3438
pytest.skip("No available quota for testing. Skipping now...")
3539

36-
quota_id = quotas[0].quota_id
40+
quota_with_usage = next(
41+
(quota for quota in quotas if quota.has_usage), None
42+
)
43+
44+
if quota_with_usage is None:
45+
quota_id = quotas[0].quota_id
46+
quota = test_linode_client.load(ObjectStorageQuota, quota_id)
47+
48+
# quota without usage should return an API error on usage retrieval
49+
with pytest.raises(ApiError):
50+
quota.usage()
51+
52+
return
53+
54+
quota_id = quota_with_usage.quota_id
3755
quota = test_linode_client.load(ObjectStorageQuota, quota_id)
3856

3957
quota_usage = quota.usage()
@@ -43,3 +61,56 @@ def test_get_obj_storage_quota_usage(test_linode_client):
4361

4462
if quota_usage.usage is not None:
4563
assert quota_usage.usage >= 0
64+
65+
66+
def test_list_and_get_obj_storage_global_quotas(test_linode_client):
67+
quotas = test_linode_client.object_storage.global_quotas()
68+
69+
if len(quotas) < 1:
70+
pytest.skip("No available global quota for testing. Skipping now...")
71+
72+
found_quota = quotas[0]
73+
74+
get_quota = test_linode_client.load(
75+
ObjectStorageGlobalQuota, found_quota.quota_id
76+
)
77+
78+
assert found_quota.quota_id == get_quota.quota_id
79+
assert found_quota.quota_type == get_quota.quota_type
80+
assert found_quota.quota_name == get_quota.quota_name
81+
assert found_quota.description == get_quota.description
82+
assert found_quota.resource_metric == get_quota.resource_metric
83+
assert found_quota.quota_limit == get_quota.quota_limit
84+
assert found_quota.has_usage == get_quota.has_usage
85+
86+
87+
def test_get_obj_storage_global_quota_usage(test_linode_client):
88+
quotas = test_linode_client.object_storage.global_quotas()
89+
90+
if len(quotas) < 1:
91+
pytest.skip("No available global quota for testing. Skipping now...")
92+
93+
quota_with_usage = next(
94+
(quota for quota in quotas if quota.has_usage), None
95+
)
96+
97+
if quota_with_usage is None:
98+
quota_id = quotas[0].quota_id
99+
quota = test_linode_client.load(ObjectStorageGlobalQuota, quota_id)
100+
101+
# quota without usage should return an API error on usage retrieval
102+
with pytest.raises(ApiError):
103+
quota.usage()
104+
105+
return
106+
107+
quota_id = quota_with_usage.quota_id
108+
quota = test_linode_client.load(ObjectStorageGlobalQuota, quota_id)
109+
110+
quota_usage = quota.usage()
111+
112+
assert isinstance(quota_usage, ObjectStorageQuotaUsage)
113+
assert quota_usage.quota_limit >= 0
114+
115+
if quota_usage.usage is not None:
116+
assert quota_usage.usage >= 0

test/unit/objects/object_storage_test.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
ObjectStorageACL,
77
ObjectStorageBucket,
88
ObjectStorageCluster,
9+
ObjectStorageGlobalQuota,
910
ObjectStorageQuota,
1011
)
1112

@@ -306,6 +307,8 @@ def test_quota_get_and_list(self):
306307
self.assertEqual(quota.s3_endpoint, "us-iad-1.linodeobjects.com")
307308
self.assertEqual(quota.quota_limit, 50)
308309
self.assertEqual(quota.resource_metric, "object")
310+
self.assertEqual(quota.quota_type, "obj-objects")
311+
self.assertTrue(quota.has_usage)
309312

310313
quota_usage_url = "/object-storage/quotas/obj-objects-us-ord-1/usage"
311314
with self.mock_get(quota_usage_url) as m:
@@ -335,3 +338,59 @@ def test_quota_get_and_list(self):
335338
)
336339
self.assertEqual(quotas[0].quota_limit, 50)
337340
self.assertEqual(quotas[0].resource_metric, "object")
341+
self.assertEqual(quotas[0].quota_type, "obj-objects")
342+
self.assertTrue(quotas[0].has_usage)
343+
344+
def test_global_quota_get_and_list(self):
345+
"""
346+
Test that you can get and list account-level Object Storage global quotas and usage.
347+
"""
348+
quota = ObjectStorageGlobalQuota(
349+
self.client,
350+
"obj-access-keys-per-account",
351+
)
352+
353+
self.assertIsNotNone(quota)
354+
self.assertEqual(quota.quota_id, "obj-access-keys-per-account")
355+
self.assertEqual(quota.quota_type, "obj-access-keys")
356+
self.assertEqual(
357+
quota.quota_name,
358+
"Object Storage Access Keys per Account",
359+
)
360+
self.assertEqual(
361+
quota.description,
362+
"Maximum number of access keys this customer is allowed to have on their account.",
363+
)
364+
self.assertEqual(quota.resource_metric, "access_key")
365+
self.assertEqual(quota.quota_limit, 100)
366+
self.assertTrue(quota.has_usage)
367+
368+
usage_url = (
369+
"/object-storage/global-quotas/obj-access-keys-per-account/usage"
370+
)
371+
with self.mock_get(usage_url) as m:
372+
usage = quota.usage()
373+
self.assertIsNotNone(usage)
374+
self.assertEqual(m.call_url, usage_url)
375+
self.assertEqual(usage.quota_limit, 100)
376+
self.assertEqual(usage.usage, 25)
377+
378+
list_url = "/object-storage/global-quotas"
379+
with self.mock_get(list_url) as m:
380+
quotas = self.client.object_storage.global_quotas()
381+
self.assertIsNotNone(quotas)
382+
self.assertEqual(m.call_url, list_url)
383+
self.assertEqual(len(quotas), 2)
384+
self.assertEqual(quotas[0].quota_id, "obj-access-keys-per-account")
385+
self.assertEqual(quotas[0].quota_type, "obj-access-keys")
386+
self.assertEqual(
387+
quotas[0].quota_name,
388+
"Object Storage Access Keys per Account",
389+
)
390+
self.assertEqual(
391+
quotas[0].description,
392+
"Maximum number of access keys this customer is allowed to have on their account.",
393+
)
394+
self.assertEqual(quotas[0].resource_metric, "access_key")
395+
self.assertEqual(quotas[0].quota_limit, 100)
396+
self.assertTrue(quotas[0].has_usage)

0 commit comments

Comments
 (0)