Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions linode_api4/objects/linode.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,12 @@
from linode_api4.objects.serializable import JSONObject, StrEnum
from linode_api4.objects.vpc import VPC, VPCSubnet
from linode_api4.paginated_list import PaginatedList
from linode_api4.util import drop_null_keys
from linode_api4.util import drop_null_keys, generate_device_suffixes

PASSWORD_CHARS = string.ascii_letters + string.digits + string.punctuation
MIN_DEVICE_LIMIT = 8
MB_PER_GB = 1024
MAX_DEVICE_LIMIT = 64


class InstanceDiskEncryptionType(StrEnum):
Expand Down Expand Up @@ -1258,9 +1261,19 @@ def config_create(
from .volume import Volume # pylint: disable=import-outside-toplevel

hypervisor_prefix = "sd" if self.hypervisor == "kvm" else "xvd"

device_limit = int(
max(
MIN_DEVICE_LIMIT,
min(self.specs.memory // MB_PER_GB, MAX_DEVICE_LIMIT),
)
)

device_names = [
hypervisor_prefix + string.ascii_lowercase[i] for i in range(0, 8)
hypervisor_prefix + suffix
for suffix in generate_device_suffixes(device_limit)
]

device_map = {
device_names[i]: None for i in range(0, len(device_names))
}
Expand Down
26 changes: 26 additions & 0 deletions linode_api4/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Contains various utility functions.
"""

import string
from typing import Any, Dict


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

return recursive_helper(data)


def generate_device_suffixes(n: int) -> list[str]:
"""
Generate n alphabetical suffixes starting with a, b, c, etc.
After z, continue with aa, ab, ac, etc. followed by aaa, aab, etc.
Example:
generate_device_suffixes(30) ->
['a', 'b', 'c', ..., 'z', 'aa', 'ab', 'ac', 'ad']
"""
letters = string.ascii_lowercase
result = []
i = 0

while len(result) < n:
s = ""
x = i
while True:
s = letters[x % 26] + s
x = x // 26 - 1
if x < 0:
break
result.append(s)
i += 1
return result
40 changes: 40 additions & 0 deletions test/integration/models/volume/test_blockstorage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from test.integration.conftest import get_region
from test.integration.helpers import get_test_label, retry_sending_request


def test_config_create_with_extended_volume_limit(test_linode_client):
client = test_linode_client

region = get_region(client, {"Linodes", "Block Storage"}, site_type="core")
label = get_test_label()

linode, _ = client.linode.instance_create(
"g6-standard-6",
region,
image="linode/debian12",
label=label,
)

volumes = [
client.volume_create(
f"{label}-vol-{i}",
region=region,
size=10,
)
for i in range(12)
]

config = linode.config_create(volumes=volumes)

devices = config._raw_json["devices"]

assert len([d for d in devices.values() if d is not None]) == 12

assert "sdi" in devices
assert "sdj" in devices
assert "sdk" in devices
assert "sdl" in devices

linode.delete()
for v in volumes:
retry_sending_request(3, v.delete)
121 changes: 120 additions & 1 deletion test/unit/util_test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import unittest

from linode_api4.util import drop_null_keys
from linode_api4.util import drop_null_keys, generate_device_suffixes


class UtilTest(unittest.TestCase):
Expand Down Expand Up @@ -53,3 +53,122 @@ def test_drop_null_keys_recursive(self):
}

assert drop_null_keys(value) == expected_output

def test_generate_device_suffixes(self):
"""
Tests whether generate_device_suffixes works as expected.
"""

expected_output_12 = [
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
]
assert generate_device_suffixes(12) == expected_output_12

expected_output_30 = [
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
"m",
"n",
"o",
"p",
"q",
"r",
"s",
"t",
"u",
"v",
"w",
"x",
"y",
"z",
"aa",
"ab",
"ac",
"ad",
]
assert generate_device_suffixes(30) == expected_output_30

expected_output_60 = [
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
"m",
"n",
"o",
"p",
"q",
"r",
"s",
"t",
"u",
"v",
"w",
"x",
"y",
"z",
"aa",
"ab",
"ac",
"ad",
"ae",
"af",
"ag",
"ah",
"ai",
"aj",
"ak",
"al",
"am",
"an",
"ao",
"ap",
"aq",
"ar",
"as",
"at",
"au",
"av",
"aw",
"ax",
"ay",
"az",
"ba",
"bb",
"bc",
"bd",
"be",
"bf",
"bg",
"bh",
]
assert generate_device_suffixes(60) == expected_output_60
Loading