Pick cheapest GPU SKU and pin the VM to an existing disk AZ.
Scan ru-6 by Nova availability zones (a/b/c). First boot uses FLAVOR_SIZE_PRESET=cheap; reruns stay on the disk segment. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+55
-3
@@ -9,13 +9,25 @@ from gpu_rent.inventory import (
|
||||
|
||||
|
||||
class FakeFlavor:
|
||||
def __init__(self, flavor_id: str, name: str, extra=None, disabled: bool = False):
|
||||
def __init__(
|
||||
self,
|
||||
flavor_id: str,
|
||||
name: str,
|
||||
extra=None,
|
||||
disabled: bool = False,
|
||||
vcpus: int = 8,
|
||||
ram: int = 32768,
|
||||
disk: int = 0,
|
||||
availability_zones=None,
|
||||
):
|
||||
self.id = flavor_id
|
||||
self.name = name
|
||||
self.extra_specs = extra or {}
|
||||
self.is_disabled = disabled
|
||||
self.vcpus = 8
|
||||
self.ram = 32768
|
||||
self.vcpus = vcpus
|
||||
self.ram = ram
|
||||
self.disk = disk
|
||||
self.availability_zones = list(availability_zones or [])
|
||||
|
||||
|
||||
def test_match_4090_24_not_48():
|
||||
@@ -74,6 +86,46 @@ def test_resolve_flavor_no_fallback_requires_default():
|
||||
assert picked.id == "ok"
|
||||
|
||||
|
||||
def test_rank_cheap_prefers_1gpu_min_sku():
|
||||
extra = {"aggregate_instance_extra_specs:gpu": "RTX4090", "gpu_memory": "24"}
|
||||
flavors = [
|
||||
FakeFlavor("dual", "GL10.8-65536-0-2GPU", extra, vcpus=8, ram=65536),
|
||||
FakeFlavor("fat", "GL10.8-32768-0-1GPU", extra, vcpus=8, ram=32768),
|
||||
FakeFlavor("tiny", "GL10.4-16384-0-1GPU", extra, vcpus=4, ram=16384),
|
||||
]
|
||||
ranked = rank_flavors(flavors, ("4090-24",), size_preset="cheap")
|
||||
assert ranked[0].id == "tiny"
|
||||
assert ranked[0].gpu_count == 1
|
||||
balanced = rank_flavors(flavors, ("4090-24",), size_preset="balanced")
|
||||
assert balanced[0].id == "fat"
|
||||
|
||||
|
||||
def test_rank_filters_availability_zone():
|
||||
extra = {"aggregate_instance_extra_specs:gpu": "RTX4090", "gpu_memory": "24"}
|
||||
flavors = [
|
||||
FakeFlavor(
|
||||
"a",
|
||||
"GL10.4-16384-0-1GPU",
|
||||
extra,
|
||||
vcpus=4,
|
||||
ram=16384,
|
||||
availability_zones=["ru-6a"],
|
||||
),
|
||||
FakeFlavor(
|
||||
"h200",
|
||||
"GL-H200",
|
||||
{"aggregate_instance_extra_specs:gpu": "H200"},
|
||||
vcpus=12,
|
||||
ram=120000,
|
||||
availability_zones=["ru-6b"],
|
||||
),
|
||||
]
|
||||
in_a = rank_flavors(flavors, ("4090-24", "a5000"), az="ru-6a")
|
||||
assert [x.id for x in in_a] == ["a"]
|
||||
in_b = rank_flavors(flavors, ("4090-24",), az="ru-6b")
|
||||
assert in_b == []
|
||||
|
||||
|
||||
def test_pick_boot_image_prefers_24_580_without_docker():
|
||||
class Img:
|
||||
def __init__(self, name):
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
from gpu_rent.config import load_config
|
||||
from gpu_rent.flavor_presets import format_char_impact, normalize_size_preset
|
||||
from gpu_rent.placement import apply_az_policy, existing_disk_az
|
||||
from gpu_rent.pools import PoolGpuOffer
|
||||
from gpu_rent.state import SessionState
|
||||
|
||||
|
||||
def test_normalize_size_preset():
|
||||
assert normalize_size_preset("RAM") == "ram"
|
||||
assert normalize_size_preset("nope") == "cheap"
|
||||
assert "GPU" in "\n".join(format_char_impact())
|
||||
|
||||
|
||||
def test_apply_az_policy_disk_wins(monkeypatch):
|
||||
monkeypatch.setenv("OS_AUTH_URL", "https://example.invalid/identity/v3")
|
||||
monkeypatch.setenv("OS_USER_DOMAIN_NAME", "1")
|
||||
monkeypatch.setenv("OS_USERNAME", "u")
|
||||
monkeypatch.setenv("OS_PASSWORD", "p")
|
||||
monkeypatch.setenv("OS_PROJECT_ID", "proj")
|
||||
monkeypatch.setenv("OS_REGION_NAME", "ru-6")
|
||||
monkeypatch.setenv("GPU_RENT_AZ", "ru-6b")
|
||||
cfg = load_config(require_auth=True)
|
||||
best = PoolGpuOffer(region="ru-6", multizone=True, recommended_az="ru-6a")
|
||||
placed = apply_az_policy(cfg, disk_az="ru-6a", best=best)
|
||||
assert placed.reason == "disk"
|
||||
assert placed.cfg.gpu_rent_az == "ru-6a"
|
||||
|
||||
|
||||
def test_apply_az_policy_first_boot_cheap(monkeypatch):
|
||||
monkeypatch.setenv("OS_AUTH_URL", "https://example.invalid/identity/v3")
|
||||
monkeypatch.setenv("OS_USER_DOMAIN_NAME", "1")
|
||||
monkeypatch.setenv("OS_USERNAME", "u")
|
||||
monkeypatch.setenv("OS_PASSWORD", "p")
|
||||
monkeypatch.setenv("OS_PROJECT_ID", "proj")
|
||||
monkeypatch.setenv("OS_REGION_NAME", "ru-6")
|
||||
monkeypatch.setenv("GPU_RENT_AZ", "ru-6b")
|
||||
cfg = load_config(require_auth=True)
|
||||
best = PoolGpuOffer(region="ru-6", multizone=True, recommended_az="ru-6a")
|
||||
placed = apply_az_policy(cfg, disk_az=None, best=best)
|
||||
assert placed.reason == "cheap"
|
||||
assert placed.cfg.gpu_rent_az == "ru-6a"
|
||||
|
||||
|
||||
def test_existing_disk_az_from_volume():
|
||||
class Vol:
|
||||
availability_zone = "ru-6a"
|
||||
|
||||
class Storage:
|
||||
def get_volume(self, vid):
|
||||
assert vid == "boot-1"
|
||||
return Vol()
|
||||
|
||||
class Conn:
|
||||
block_storage = Storage()
|
||||
|
||||
cfg = load_config(require_auth=False)
|
||||
state = SessionState(boot_volume_id="boot-1")
|
||||
assert existing_disk_az(Conn(), cfg, state) == "ru-6a"
|
||||
+33
-1
@@ -1,4 +1,4 @@
|
||||
from gpu_rent.pools import PoolGpuOffer, format_pool_scan
|
||||
from gpu_rent.pools import AzGpuOffer, PoolGpuOffer, format_pool_scan
|
||||
|
||||
|
||||
def test_format_pool_scan_recommends_first_hit():
|
||||
@@ -14,9 +14,41 @@ def test_format_pool_scan_recommends_first_hit():
|
||||
matched=[
|
||||
FlavorInfo("1", "GL-4090", 4, 16384, False, {}, "4090-24"),
|
||||
],
|
||||
recommended_az="ru-6a",
|
||||
segments=[
|
||||
AzGpuOffer(
|
||||
az="ru-6a",
|
||||
gpu_labels_found=["4090-24"],
|
||||
gpu_types=["RTX4090"],
|
||||
matched=[FlavorInfo("1", "GL-4090", 4, 16384, False, {}, "4090-24")],
|
||||
),
|
||||
AzGpuOffer(az="ru-6b", gpu_labels_found=[], gpu_types=["H200"], matched=[]),
|
||||
AzGpuOffer(az="ru-6c", gpu_labels_found=[], gpu_types=["H200"], matched=[]),
|
||||
],
|
||||
),
|
||||
]
|
||||
text = "\n".join(format_pool_scan(offers, ("4090-24", "4090-48")))
|
||||
assert "ru-6" in text and "multizone" in text
|
||||
assert "типы:" in text
|
||||
assert "ru-6a" in text and "ru-6b" in text and "ru-6c" in text
|
||||
assert "рекомендация:" in text and "OS_REGION_NAME=ru-6" in text
|
||||
assert "GPU_RENT_AZ=ru-6a" in text
|
||||
|
||||
|
||||
def test_format_pool_scan_pins_disk_az():
|
||||
from gpu_rent.inventory import FlavorInfo
|
||||
|
||||
offers = [
|
||||
PoolGpuOffer(
|
||||
region="ru-6",
|
||||
multizone=True,
|
||||
gpu_labels_found=["4090-24"],
|
||||
gpu_types=["RTX4090"],
|
||||
matched=[FlavorInfo("1", "GL-4090", 4, 16384, False, {}, "4090-24")],
|
||||
recommended_az="ru-6a",
|
||||
)
|
||||
]
|
||||
text = "\n".join(
|
||||
format_pool_scan(offers, ("4090-24",), pin_az="ru-6a", size_preset="cheap")
|
||||
)
|
||||
assert "диск уже в ru-6a" in text
|
||||
|
||||
@@ -29,9 +29,9 @@ def test_prompt_server_plan_with_ranked(monkeypatch, tmp_path):
|
||||
|
||||
monkeypatch.setattr(
|
||||
"gpu_rent.ux.list_ranked_flavors",
|
||||
lambda flavors, cfg: [f1, f2],
|
||||
lambda flavors, cfg, **kw: [f1, f2],
|
||||
)
|
||||
answers = iter(["2", "200", "2"])
|
||||
answers = iter(["1", "2", "200", "2"])
|
||||
|
||||
def ask(msg: str, default: str = "") -> str:
|
||||
return next(answers)
|
||||
|
||||
Reference in New Issue
Block a user