Skip to content

Commit d4b544c

Browse files
Merge branch 'dev' into bugfix/TPT-4213-investigate-and-fix-python-database-integration-tests
2 parents df296c0 + 26e964f commit d4b544c

File tree

9 files changed

+223
-22
lines changed

9 files changed

+223
-22
lines changed

docs/conf.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010

1111
# If extensions (or modules to document with autodoc) are in another directory,
1212
# add these directories to sys.path here. If the directory is relative to the
13-
# documentation root, use os.path.abspath to make it absolute, like shown here.
13+
# documentation root, use Path(...).absolute() to make it absolute, like shown here.
1414
#
15-
import os
1615
import sys
17-
sys.path.insert(0, os.path.abspath('..'))
16+
from pathlib import Path
17+
sys.path.insert(0, str(Path('..').absolute()))
1818

1919

2020
# -- Project information -----------------------------------------------------

linode_api4/common.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import os
21
from dataclasses import dataclass
2+
from pathlib import Path
33

44
from linode_api4.objects import JSONObject
55

@@ -47,9 +47,9 @@ def load_and_validate_keys(authorized_keys):
4747
ret.append(k)
4848
else:
4949
# it doesn't appear to be a key.. is it a path to the key?
50-
k = os.path.expanduser(k)
51-
if os.path.isfile(k):
52-
with open(k) as f:
50+
k_path = Path(k).expanduser()
51+
if k_path.is_file():
52+
with open(k_path) as f:
5353
ret.append(f.read().rstrip())
5454
else:
5555
raise ValueError(

linode_api4/groups/linode.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import base64
2-
import os
2+
from pathlib import Path
33
from typing import Any, Dict, List, Optional, Union
44

55
from linode_api4.common import load_and_validate_keys
@@ -457,8 +457,9 @@ def stackscript_create(
457457
script_body = script
458458
if not script.startswith("#!"):
459459
# it doesn't look like a stackscript body, let's see if it's a file
460-
if os.path.isfile(script):
461-
with open(script) as f:
460+
script_path = Path(script)
461+
if script_path.is_file():
462+
with open(script_path) as f:
462463
script_body = f.read()
463464
else:
464465
raise ValueError(

linode_api4/groups/profile.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import os
21
from datetime import datetime
2+
from pathlib import Path
33

44
from linode_api4 import UnexpectedResponseError
55
from linode_api4.common import SSH_KEY_TYPES
@@ -322,9 +322,9 @@ def ssh_key_upload(self, key, label):
322322
"""
323323
if not key.startswith(SSH_KEY_TYPES):
324324
# this might be a file path - look for it
325-
path = os.path.expanduser(key)
326-
if os.path.isfile(path):
327-
with open(path) as f:
325+
key_path = Path(key).expanduser()
326+
if key_path.is_file():
327+
with open(key_path) as f:
328328
key = f.read().strip()
329329
if not key.startswith(SSH_KEY_TYPES):
330330
raise ValueError("Invalid SSH Public Key")

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/objects/nodebalancer.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import os
1+
from pathlib import Path
22
from urllib import parse
33

44
from linode_api4.common import Price, RegionPrice
@@ -220,12 +220,14 @@ def load_ssl_data(self, cert_file, key_file):
220220

221221
# we're disabling warnings here because these attributes are defined dynamically
222222
# through linode.objects.Base, and pylint isn't privy
223-
if os.path.isfile(os.path.expanduser(cert_file)):
224-
with open(os.path.expanduser(cert_file)) as f:
223+
cert_path = Path(cert_file).expanduser()
224+
if cert_path.is_file():
225+
with open(cert_path) as f:
225226
self.ssl_cert = f.read()
226227

227-
if os.path.isfile(os.path.expanduser(key_file)):
228-
with open(os.path.expanduser(key_file)) as f:
228+
key_path = Path(key_file).expanduser()
229+
if key_path.is_file():
230+
with open(key_path) as f:
229231
self.ssl_key = f.read()
230232

231233

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)