Skip to content

Commit 94d74de

Browse files
committed
Add global quotas and quota usage support for OBJ services
1 parent bc5f735 commit 94d74de

9 files changed

+244
-4
lines changed

linode_api4/groups/object_storage.py

Lines changed: 18 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,20 @@ 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(
552+
ObjectStorageGlobalQuota, *filters
553+
)

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: 80 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,20 @@ 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((quota for quota in quotas if quota.has_usage), None)
41+
42+
if quota_with_usage is None:
43+
quota_id = quotas[0].quota_id
44+
quota = test_linode_client.load(ObjectStorageQuota, quota_id)
45+
46+
with pytest.raises(ApiError) as exc:
47+
quota.usage()
48+
49+
assert exc.value.status == 404
50+
assert "Usage not supported" in str(exc.value)
51+
return
52+
53+
quota_id = quota_with_usage.quota_id
3754
quota = test_linode_client.load(ObjectStorageQuota, quota_id)
3855

3956
quota_usage = quota.usage()
@@ -43,3 +60,65 @@ def test_get_obj_storage_quota_usage(test_linode_client):
4360

4461
if quota_usage.usage is not None:
4562
assert quota_usage.usage >= 0
63+
64+
65+
def test_list_and_get_obj_storage_global_quotas(test_linode_client):
66+
try:
67+
quotas = test_linode_client.object_storage.global_quotas()
68+
except ApiError as err:
69+
if err.status == 404:
70+
pytest.skip("Object Storage is not enabled on this account.")
71+
raise
72+
73+
if len(quotas) < 1:
74+
pytest.skip("No available global quota for testing. Skipping now...")
75+
76+
found_quota = quotas[0]
77+
78+
get_quota = test_linode_client.load(
79+
ObjectStorageGlobalQuota, found_quota.quota_id
80+
)
81+
82+
assert found_quota.quota_id == get_quota.quota_id
83+
assert found_quota.quota_type == get_quota.quota_type
84+
assert found_quota.quota_name == get_quota.quota_name
85+
assert found_quota.description == get_quota.description
86+
assert found_quota.resource_metric == get_quota.resource_metric
87+
assert found_quota.quota_limit == get_quota.quota_limit
88+
assert found_quota.has_usage == get_quota.has_usage
89+
90+
91+
def test_get_obj_storage_global_quota_usage(test_linode_client):
92+
try:
93+
quotas = test_linode_client.object_storage.global_quotas()
94+
except ApiError as err:
95+
if err.status == 404:
96+
pytest.skip("Object Storage is not enabled on this account.")
97+
raise
98+
99+
if len(quotas) < 1:
100+
pytest.skip("No available global quota for testing. Skipping now...")
101+
102+
quota_with_usage = next((quota for quota in quotas if quota.has_usage), None)
103+
104+
if quota_with_usage is None:
105+
quota_id = quotas[0].quota_id
106+
quota = test_linode_client.load(ObjectStorageGlobalQuota, quota_id)
107+
108+
with pytest.raises(ApiError) as exc:
109+
quota.usage()
110+
111+
assert exc.value.status == 404
112+
assert "Usage not supported" in str(exc.value)
113+
return
114+
115+
quota_id = quota_with_usage.quota_id
116+
quota = test_linode_client.load(ObjectStorageGlobalQuota, quota_id)
117+
118+
quota_usage = quota.usage()
119+
120+
assert isinstance(quota_usage, ObjectStorageQuotaUsage)
121+
assert quota_usage.quota_limit >= 0
122+
123+
if quota_usage.usage is not None:
124+
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 = "/object-storage/global-quotas/obj-access-keys-per-account/usage"
369+
with self.mock_get(usage_url) as m:
370+
usage = quota.usage()
371+
self.assertIsNotNone(usage)
372+
self.assertEqual(m.call_url, usage_url)
373+
self.assertEqual(usage.quota_limit, 100)
374+
self.assertEqual(usage.usage, 25)
375+
376+
list_url = "/object-storage/global-quotas"
377+
with self.mock_get(list_url) as m:
378+
quotas = self.client.object_storage.global_quotas()
379+
self.assertIsNotNone(quotas)
380+
self.assertEqual(m.call_url, list_url)
381+
self.assertEqual(len(quotas), 2)
382+
self.assertEqual(
383+
quotas[0].quota_id, "obj-access-keys-per-account"
384+
)
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)