golem-vm-provider 0.1.59__tar.gz → 0.1.60__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/PKG-INFO +1 -1
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/api/routes.py +12 -2
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/main.py +1 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/vm/multipass_adapter.py +47 -14
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/pyproject.toml +1 -1
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/README.md +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/__init__.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/api/__init__.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/api/models.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/config.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/container.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/data/deployments/l2.json +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/discovery/__init__.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/discovery/advertiser.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/discovery/golem_base_advertiser.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/discovery/golem_base_utils.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/discovery/multi_advertiser.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/discovery/resource_monitor.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/discovery/resource_tracker.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/discovery/service.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/jobs/store.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/network/port_verifier.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/payments/blockchain_service.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/payments/monitor.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/payments/stream_map.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/security/ethereum.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/security/faucet.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/security/l2_faucet.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/service.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/utils/__init__.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/utils/ascii_art.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/utils/logging.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/utils/port_display.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/utils/pricing.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/utils/retry.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/utils/setup.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/vm/__init__.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/vm/cloud_init.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/vm/models.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/vm/multipass.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/vm/name_mapper.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/vm/port_manager.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/vm/provider.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/vm/proxy_manager.py +0 -0
- {golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/vm/service.py +0 -0
@@ -31,6 +31,12 @@ from ..vm.multipass_adapter import MultipassError
|
|
31
31
|
logger = setup_logger(__name__)
|
32
32
|
router = APIRouter()
|
33
33
|
|
34
|
+
# Expose Settings class at module scope for tests to monkeypatch default deployment lookup
|
35
|
+
try:
|
36
|
+
from ..config import Settings as _Cfg # type: ignore
|
37
|
+
except Exception: # noqa: BLE001
|
38
|
+
_Cfg = None # type: ignore
|
39
|
+
|
34
40
|
# Job status persisted in SQLite via JobStore (see Container.job_store)
|
35
41
|
|
36
42
|
|
@@ -56,8 +62,10 @@ async def create_vm(
|
|
56
62
|
if spa and spa != "0x0000000000000000000000000000000000000000":
|
57
63
|
if os.environ.get("PYTEST_CURRENT_TEST"):
|
58
64
|
try:
|
59
|
-
|
60
|
-
|
65
|
+
if _Cfg is not None:
|
66
|
+
default_spa, _ = _Cfg._load_l2_deployment() # type: ignore[attr-defined]
|
67
|
+
else:
|
68
|
+
default_spa = None
|
61
69
|
except Exception:
|
62
70
|
default_spa = None
|
63
71
|
if not default_spa or spa.lower() != default_spa.lower():
|
@@ -302,6 +310,8 @@ async def provider_info(settings: Any = Depends(Provide[Container.config])) -> P
|
|
302
310
|
provider_id=settings["PROVIDER_ID"],
|
303
311
|
stream_payment_address=settings["STREAM_PAYMENT_ADDRESS"],
|
304
312
|
glm_token_address=settings["GLM_TOKEN_ADDRESS"],
|
313
|
+
# Provide ETH-focused alias for clients; keep legacy field too
|
314
|
+
eth_token_address=settings["GLM_TOKEN_ADDRESS"],
|
305
315
|
ip_address=ip_addr,
|
306
316
|
country=(settings.get("PROVIDER_COUNTRY") if isinstance(settings, dict) else getattr(settings, "PROVIDER_COUNTRY", None)),
|
307
317
|
platform=platform_str,
|
@@ -15,6 +15,7 @@ if "--json" in _sys.argv:
|
|
15
15
|
|
16
16
|
# Defer heavy local imports (may import config) until after we decide on silence
|
17
17
|
from .container import Container
|
18
|
+
from .config import settings # used by pricing CLI and server commands
|
18
19
|
from .service import ProviderService
|
19
20
|
|
20
21
|
logger = setup_logger(__name__)
|
@@ -32,6 +32,26 @@ class MultipassAdapter(VMProvider):
|
|
32
32
|
self.proxy_manager = proxy_manager
|
33
33
|
self.name_mapper = name_mapper
|
34
34
|
|
35
|
+
@staticmethod
|
36
|
+
def _safe_int(value, default: int = 0) -> int:
|
37
|
+
"""Best-effort int conversion that treats missing/blank values as default.
|
38
|
+
|
39
|
+
Multipass may return empty strings for numeric fields (e.g., when a VM is
|
40
|
+
stopped). This helper prevents ValueError by mapping '', None, or
|
41
|
+
unparsable values to a sensible default.
|
42
|
+
"""
|
43
|
+
try:
|
44
|
+
if value is None:
|
45
|
+
return default
|
46
|
+
if isinstance(value, str):
|
47
|
+
v = value.strip()
|
48
|
+
if v == "":
|
49
|
+
return default
|
50
|
+
return int(v)
|
51
|
+
return int(value)
|
52
|
+
except (ValueError, TypeError):
|
53
|
+
return default
|
54
|
+
|
35
55
|
async def _run_multipass(self, args: List[str], check: bool = True) -> subprocess.CompletedProcess:
|
36
56
|
"""Run a multipass command."""
|
37
57
|
# Commands that produce JSON or version info that we need to parse.
|
@@ -160,8 +180,8 @@ class MultipassAdapter(VMProvider):
|
|
160
180
|
vms: List[VMInfo] = []
|
161
181
|
for requestor_name, multipass_name in list(all_mappings.items()):
|
162
182
|
try:
|
163
|
-
# get_vm_status
|
164
|
-
vm_info = await self.get_vm_status(
|
183
|
+
# Pass requestor id; get_vm_status accepts either id
|
184
|
+
vm_info = await self.get_vm_status(requestor_name)
|
165
185
|
vms.append(vm_info)
|
166
186
|
except VMNotFoundError:
|
167
187
|
logger.warning(
|
@@ -188,30 +208,40 @@ class MultipassAdapter(VMProvider):
|
|
188
208
|
await self._run_multipass(["stop", multipass_name])
|
189
209
|
return await self.get_vm_status(multipass_name)
|
190
210
|
|
191
|
-
async def get_vm_status(self,
|
192
|
-
"""Get
|
211
|
+
async def get_vm_status(self, name_or_id: str) -> VMInfo:
|
212
|
+
"""Get VM status by multipass name or requestor id."""
|
213
|
+
# Resolve identifiers flexibly
|
214
|
+
requestor_name = await self.name_mapper.get_requestor_name(name_or_id)
|
215
|
+
if requestor_name:
|
216
|
+
multipass_name = name_or_id
|
217
|
+
else:
|
218
|
+
multipass_name = await self.name_mapper.get_multipass_name(name_or_id)
|
219
|
+
if not multipass_name:
|
220
|
+
raise VMNotFoundError(f"VM {name_or_id} mapping not found")
|
221
|
+
requestor_name = name_or_id
|
193
222
|
try:
|
194
223
|
info = await self._get_vm_info(multipass_name)
|
195
224
|
except MultipassError:
|
196
225
|
raise VMNotFoundError(f"VM {multipass_name} not found in multipass")
|
197
226
|
|
198
|
-
requestor_name = await self.name_mapper.get_requestor_name(multipass_name)
|
199
|
-
if not requestor_name:
|
200
|
-
raise VMNotFoundError(f"Mapping for VM {multipass_name} not found")
|
201
|
-
|
202
227
|
ipv4 = info.get("ipv4")
|
203
228
|
ip_address = ipv4[0] if ipv4 else None
|
204
229
|
logger.debug(f"Parsed VM info for {requestor_name}: {info}")
|
205
230
|
|
206
231
|
disks_info = info.get("disks", {})
|
207
|
-
total_storage =
|
232
|
+
total_storage = 0
|
233
|
+
for disk in disks_info.values():
|
234
|
+
total_storage += self._safe_int(disk.get("total"), 0)
|
235
|
+
|
236
|
+
# Memory reported by multipass is in bytes; default to 1 GiB if missing/blank
|
237
|
+
mem_total_bytes = self._safe_int(info.get("memory", {}).get("total"), 1024**3)
|
208
238
|
vm_info_obj = VMInfo(
|
209
239
|
id=requestor_name,
|
210
240
|
name=requestor_name,
|
211
241
|
status=VMStatus(info["state"].lower()),
|
212
242
|
resources=VMResources(
|
213
|
-
cpu=
|
214
|
-
memory=round(
|
243
|
+
cpu=self._safe_int(info.get("cpu_count"), 1),
|
244
|
+
memory=round(mem_total_bytes / (1024**3)),
|
215
245
|
storage=round(total_storage / (1024**3)) if total_storage > 0 else 10
|
216
246
|
),
|
217
247
|
ip_address=ip_address,
|
@@ -228,10 +258,13 @@ class MultipassAdapter(VMProvider):
|
|
228
258
|
try:
|
229
259
|
info = await self._get_vm_info(multipass_name)
|
230
260
|
disks_info = info.get("disks", {})
|
231
|
-
total_storage =
|
261
|
+
total_storage = 0
|
262
|
+
for disk in disks_info.values():
|
263
|
+
total_storage += self._safe_int(disk.get("total"), 0)
|
264
|
+
mem_total_bytes = self._safe_int(info.get("memory", {}).get("total"), 1024**3)
|
232
265
|
vm_resources[requestor_name] = VMResources(
|
233
|
-
cpu=
|
234
|
-
memory=round(
|
266
|
+
cpu=self._safe_int(info.get("cpu_count"), 1),
|
267
|
+
memory=round(mem_total_bytes / (1024**3)),
|
235
268
|
storage=round(total_storage / (1024**3)) if total_storage > 0 else 10
|
236
269
|
)
|
237
270
|
except (MultipassError, VMNotFoundError):
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "golem-vm-provider"
|
3
|
-
version = "0.1.
|
3
|
+
version = "0.1.60"
|
4
4
|
description = "VM on Golem Provider Node - Run your own provider node to offer VMs on the Golem Network"
|
5
5
|
authors = ["Phillip Jensen <phillip+vm-on-golem@golemgrid.com>"]
|
6
6
|
readme = "README.md"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/discovery/golem_base_advertiser.py
RENAMED
File without changes
|
{golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/discovery/golem_base_utils.py
RENAMED
File without changes
|
{golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/discovery/multi_advertiser.py
RENAMED
File without changes
|
{golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/discovery/resource_monitor.py
RENAMED
File without changes
|
{golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/discovery/resource_tracker.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{golem_vm_provider-0.1.59 → golem_vm_provider-0.1.60}/provider/payments/blockchain_service.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|