Skip to content

Commit 48ebe5a

Browse files
Merge branch 'dev' into zhiwei/obj-quotas
2 parents daab10f + 26e964f commit 48ebe5a

File tree

4 files changed

+201
-3
lines changed

4 files changed

+201
-3
lines changed

linode_api4/objects/linode.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,12 @@
4040
from linode_api4.objects.serializable import JSONObject, StrEnum
4141
from linode_api4.objects.vpc import VPC, VPCSubnet
4242
from linode_api4.paginated_list import PaginatedList
43-
from linode_api4.util import drop_null_keys
43+
from linode_api4.util import drop_null_keys, generate_device_suffixes
4444

4545
PASSWORD_CHARS = string.ascii_letters + string.digits + string.punctuation
46+
MIN_DEVICE_LIMIT = 8
47+
MB_PER_GB = 1024
48+
MAX_DEVICE_LIMIT = 64
4649

4750

4851
class InstanceDiskEncryptionType(StrEnum):
@@ -1272,9 +1275,19 @@ def config_create(
12721275
from .volume import Volume # pylint: disable=import-outside-toplevel
12731276

12741277
hypervisor_prefix = "sd" if self.hypervisor == "kvm" else "xvd"
1278+
1279+
device_limit = int(
1280+
max(
1281+
MIN_DEVICE_LIMIT,
1282+
min(self.specs.memory // MB_PER_GB, MAX_DEVICE_LIMIT),
1283+
)
1284+
)
1285+
12751286
device_names = [
1276-
hypervisor_prefix + string.ascii_lowercase[i] for i in range(0, 8)
1287+
hypervisor_prefix + suffix
1288+
for suffix in generate_device_suffixes(device_limit)
12771289
]
1290+
12781291
device_map = {
12791292
device_names[i]: None for i in range(0, len(device_names))
12801293
}

linode_api4/util.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Contains various utility functions.
33
"""
44

5+
import string
56
from typing import Any, Dict
67

78

@@ -27,3 +28,28 @@ def recursive_helper(value: Any) -> Any:
2728
return value
2829

2930
return recursive_helper(data)
31+
32+
33+
def generate_device_suffixes(n: int) -> list[str]:
34+
"""
35+
Generate n alphabetical suffixes starting with a, b, c, etc.
36+
After z, continue with aa, ab, ac, etc. followed by aaa, aab, etc.
37+
Example:
38+
generate_device_suffixes(30) ->
39+
['a', 'b', 'c', ..., 'z', 'aa', 'ab', 'ac', 'ad']
40+
"""
41+
letters = string.ascii_lowercase
42+
result = []
43+
i = 0
44+
45+
while len(result) < n:
46+
s = ""
47+
x = i
48+
while True:
49+
s = letters[x % 26] + s
50+
x = x // 26 - 1
51+
if x < 0:
52+
break
53+
result.append(s)
54+
i += 1
55+
return result
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
from test.integration.conftest import get_region
2+
from test.integration.helpers import get_test_label, retry_sending_request
3+
4+
5+
def test_config_create_with_extended_volume_limit(test_linode_client):
6+
client = test_linode_client
7+
8+
region = get_region(client, {"Linodes", "Block Storage"}, site_type="core")
9+
label = get_test_label()
10+
11+
linode, _ = client.linode.instance_create(
12+
"g6-standard-6",
13+
region,
14+
image="linode/debian12",
15+
label=label,
16+
)
17+
18+
volumes = [
19+
client.volume_create(
20+
f"{label}-vol-{i}",
21+
region=region,
22+
size=10,
23+
)
24+
for i in range(12)
25+
]
26+
27+
config = linode.config_create(volumes=volumes)
28+
29+
devices = config._raw_json["devices"]
30+
31+
assert len([d for d in devices.values() if d is not None]) == 12
32+
33+
assert "sdi" in devices
34+
assert "sdj" in devices
35+
assert "sdk" in devices
36+
assert "sdl" in devices
37+
38+
linode.delete()
39+
for v in volumes:
40+
retry_sending_request(3, v.delete)

test/unit/util_test.py

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

3-
from linode_api4.util import drop_null_keys
3+
from linode_api4.util import drop_null_keys, generate_device_suffixes
44

55

66
class UtilTest(unittest.TestCase):
@@ -53,3 +53,122 @@ def test_drop_null_keys_recursive(self):
5353
}
5454

5555
assert drop_null_keys(value) == expected_output
56+
57+
def test_generate_device_suffixes(self):
58+
"""
59+
Tests whether generate_device_suffixes works as expected.
60+
"""
61+
62+
expected_output_12 = [
63+
"a",
64+
"b",
65+
"c",
66+
"d",
67+
"e",
68+
"f",
69+
"g",
70+
"h",
71+
"i",
72+
"j",
73+
"k",
74+
"l",
75+
]
76+
assert generate_device_suffixes(12) == expected_output_12
77+
78+
expected_output_30 = [
79+
"a",
80+
"b",
81+
"c",
82+
"d",
83+
"e",
84+
"f",
85+
"g",
86+
"h",
87+
"i",
88+
"j",
89+
"k",
90+
"l",
91+
"m",
92+
"n",
93+
"o",
94+
"p",
95+
"q",
96+
"r",
97+
"s",
98+
"t",
99+
"u",
100+
"v",
101+
"w",
102+
"x",
103+
"y",
104+
"z",
105+
"aa",
106+
"ab",
107+
"ac",
108+
"ad",
109+
]
110+
assert generate_device_suffixes(30) == expected_output_30
111+
112+
expected_output_60 = [
113+
"a",
114+
"b",
115+
"c",
116+
"d",
117+
"e",
118+
"f",
119+
"g",
120+
"h",
121+
"i",
122+
"j",
123+
"k",
124+
"l",
125+
"m",
126+
"n",
127+
"o",
128+
"p",
129+
"q",
130+
"r",
131+
"s",
132+
"t",
133+
"u",
134+
"v",
135+
"w",
136+
"x",
137+
"y",
138+
"z",
139+
"aa",
140+
"ab",
141+
"ac",
142+
"ad",
143+
"ae",
144+
"af",
145+
"ag",
146+
"ah",
147+
"ai",
148+
"aj",
149+
"ak",
150+
"al",
151+
"am",
152+
"an",
153+
"ao",
154+
"ap",
155+
"aq",
156+
"ar",
157+
"as",
158+
"at",
159+
"au",
160+
"av",
161+
"aw",
162+
"ax",
163+
"ay",
164+
"az",
165+
"ba",
166+
"bb",
167+
"bc",
168+
"bd",
169+
"be",
170+
"bf",
171+
"bg",
172+
"bh",
173+
]
174+
assert generate_device_suffixes(60) == expected_output_60

0 commit comments

Comments
 (0)