golem-vm-provider 0.1.60__tar.gz → 0.1.61__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.60 → golem_vm_provider-0.1.61}/PKG-INFO +5 -5
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/README.md +4 -4
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/config.py +48 -3
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/discovery/golem_base_advertiser.py +37 -5
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/main.py +6 -5
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/payments/monitor.py +62 -12
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/vm/multipass_adapter.py +2 -1
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/vm/service.py +3 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/pyproject.toml +1 -1
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/__init__.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/api/__init__.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/api/models.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/api/routes.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/container.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/data/deployments/l2.json +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/discovery/__init__.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/discovery/advertiser.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/discovery/golem_base_utils.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/discovery/multi_advertiser.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/discovery/resource_monitor.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/discovery/resource_tracker.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/discovery/service.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/jobs/store.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/network/port_verifier.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/payments/blockchain_service.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/payments/stream_map.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/security/ethereum.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/security/faucet.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/security/l2_faucet.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/service.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/utils/__init__.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/utils/ascii_art.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/utils/logging.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/utils/port_display.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/utils/pricing.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/utils/retry.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/utils/setup.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/vm/__init__.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/vm/cloud_init.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/vm/models.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/vm/multipass.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/vm/name_mapper.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/vm/port_manager.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/vm/provider.py +0 -0
- {golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/vm/proxy_manager.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: golem-vm-provider
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.61
|
4
4
|
Summary: VM on Golem Provider Node - Run your own provider node to offer VMs on the Golem Network
|
5
5
|
Keywords: golem,vm,provider,cloud,decentralized
|
6
6
|
Author: Phillip Jensen
|
@@ -456,7 +456,7 @@ Response (per stream):
|
|
456
456
|
|
457
457
|
Notes:
|
458
458
|
- Endpoints return 400 when streaming is disabled (zero `STREAM_PAYMENT_ADDRESS`).
|
459
|
-
- In development mode (`
|
459
|
+
- In development mode (`GOLEM_ENVIRONMENT=development`) additional debug logs are emitted around stream verification and monitor ticks.
|
460
460
|
|
461
461
|
## Operations
|
462
462
|
|
@@ -467,7 +467,7 @@ Notes:
|
|
467
467
|
golem-provider start
|
468
468
|
|
469
469
|
# Development mode with extra logs and reload
|
470
|
-
|
470
|
+
GOLEM_ENVIRONMENT=development golem-provider start --network testnet
|
471
471
|
```
|
472
472
|
|
473
473
|
Run as a background service (no terminal):
|
@@ -485,7 +485,7 @@ golem-provider status [--json]
|
|
485
485
|
|
486
486
|
### Mode vs. Network
|
487
487
|
|
488
|
-
- Development Mode (`
|
488
|
+
- Development Mode (`GOLEM_ENVIRONMENT=development`)
|
489
489
|
- Optimizes for local iteration: enables reload + debug logging and uses local defaults (e.g., local port check servers). May derive a local/LAN IP automatically and prefix the provider name with `DEVMODE-`.
|
490
490
|
- Does not decide which chain you target.
|
491
491
|
|
@@ -498,7 +498,7 @@ golem-provider status [--json]
|
|
498
498
|
- Selects the payments chain profile (e.g., `l2.holesky`, `mainnet`). Determines default payments RPC, faucet enablement, and symbols.
|
499
499
|
|
500
500
|
Common setups:
|
501
|
-
- Local dev on testnet: `
|
501
|
+
- Local dev on testnet: `GOLEM_ENVIRONMENT=development` plus `--network testnet`.
|
502
502
|
- Staging on testnet: keep `ENVIRONMENT=production`, set `--network testnet` and testnet RPCs.
|
503
503
|
- Production on mainnet: `ENVIRONMENT=production` with `--network mainnet` and mainnet RPCs.
|
504
504
|
|
@@ -411,7 +411,7 @@ Response (per stream):
|
|
411
411
|
|
412
412
|
Notes:
|
413
413
|
- Endpoints return 400 when streaming is disabled (zero `STREAM_PAYMENT_ADDRESS`).
|
414
|
-
- In development mode (`
|
414
|
+
- In development mode (`GOLEM_ENVIRONMENT=development`) additional debug logs are emitted around stream verification and monitor ticks.
|
415
415
|
|
416
416
|
## Operations
|
417
417
|
|
@@ -422,7 +422,7 @@ Notes:
|
|
422
422
|
golem-provider start
|
423
423
|
|
424
424
|
# Development mode with extra logs and reload
|
425
|
-
|
425
|
+
GOLEM_ENVIRONMENT=development golem-provider start --network testnet
|
426
426
|
```
|
427
427
|
|
428
428
|
Run as a background service (no terminal):
|
@@ -440,7 +440,7 @@ golem-provider status [--json]
|
|
440
440
|
|
441
441
|
### Mode vs. Network
|
442
442
|
|
443
|
-
- Development Mode (`
|
443
|
+
- Development Mode (`GOLEM_ENVIRONMENT=development`)
|
444
444
|
- Optimizes for local iteration: enables reload + debug logging and uses local defaults (e.g., local port check servers). May derive a local/LAN IP automatically and prefix the provider name with `DEVMODE-`.
|
445
445
|
- Does not decide which chain you target.
|
446
446
|
|
@@ -453,7 +453,7 @@ golem-provider status [--json]
|
|
453
453
|
- Selects the payments chain profile (e.g., `l2.holesky`, `mainnet`). Determines default payments RPC, faucet enablement, and symbols.
|
454
454
|
|
455
455
|
Common setups:
|
456
|
-
- Local dev on testnet: `
|
456
|
+
- Local dev on testnet: `GOLEM_ENVIRONMENT=development` plus `--network testnet`.
|
457
457
|
- Staging on testnet: keep `ENVIRONMENT=production`, set `--network testnet` and testnet RPCs.
|
458
458
|
- Production on mainnet: `ENVIRONMENT=production` with `--network mainnet` and mainnet RPCs.
|
459
459
|
|
@@ -27,7 +27,7 @@ def ensure_config() -> None:
|
|
27
27
|
created = True
|
28
28
|
|
29
29
|
if not env_file.exists():
|
30
|
-
env_file.write_text("
|
30
|
+
env_file.write_text("GOLEM_ENVIRONMENT=production\n")
|
31
31
|
created = True
|
32
32
|
|
33
33
|
from .security.ethereum import EthereumIdentity
|
@@ -55,8 +55,12 @@ class Settings(BaseSettings):
|
|
55
55
|
PORT: int = 7466
|
56
56
|
SKIP_PORT_VERIFICATION: bool = False
|
57
57
|
ENVIRONMENT: str = "production"
|
58
|
-
# Logical network selector for
|
59
|
-
|
58
|
+
# Logical network selector for advertisement scope and client defaults
|
59
|
+
# If not explicitly provided, computed by validator below (dev -> testnet, else -> mainnet)
|
60
|
+
NETWORK: str = Field(
|
61
|
+
default="",
|
62
|
+
description="Logical Golem network: 'testnet' or 'mainnet'"
|
63
|
+
)
|
60
64
|
|
61
65
|
# Payments chain selection (modular network profiles). Keep default on l2.holesky
|
62
66
|
PAYMENTS_NETWORK: str = Field(
|
@@ -73,6 +77,37 @@ class Settings(BaseSettings):
|
|
73
77
|
def DEV_MODE(self) -> bool:
|
74
78
|
return self.ENVIRONMENT == "development"
|
75
79
|
|
80
|
+
@field_validator("ENVIRONMENT", mode='before')
|
81
|
+
@classmethod
|
82
|
+
def prefer_global_env(cls, v: str) -> str:
|
83
|
+
"""Prefer unified GOLEM_ENVIRONMENT when provided; fallback to service-specific env."""
|
84
|
+
ge = os.environ.get("GOLEM_ENVIRONMENT")
|
85
|
+
if ge:
|
86
|
+
return ge
|
87
|
+
return v
|
88
|
+
|
89
|
+
@field_validator("NETWORK", mode='before')
|
90
|
+
@classmethod
|
91
|
+
def resolve_network(cls, v: str, values: dict) -> str:
|
92
|
+
"""Resolve logical network with sensible defaults.
|
93
|
+
|
94
|
+
Priority:
|
95
|
+
1) Explicit override via GOLEM_PROVIDER_NETWORK env or provided value
|
96
|
+
2) If ENVIRONMENT == development -> 'testnet'
|
97
|
+
3) Otherwise -> 'mainnet'
|
98
|
+
"""
|
99
|
+
# Prefer explicit provider-scoped env override
|
100
|
+
env_override = os.environ.get("GOLEM_PROVIDER_NETWORK")
|
101
|
+
if env_override:
|
102
|
+
return env_override
|
103
|
+
# If value provided (via settings or direct assignment), keep it
|
104
|
+
val = (v or "").strip()
|
105
|
+
if val:
|
106
|
+
return val
|
107
|
+
# Default based on environment
|
108
|
+
env = (values.data.get("ENVIRONMENT") or "").lower()
|
109
|
+
return "testnet" if env == "development" else "mainnet"
|
110
|
+
|
76
111
|
@field_validator("SKIP_PORT_VERIFICATION", mode='before')
|
77
112
|
def set_skip_verification(cls, v: bool, values: dict) -> bool:
|
78
113
|
"""Set skip verification based on debug mode."""
|
@@ -186,6 +221,16 @@ class Settings(BaseSettings):
|
|
186
221
|
description="Min withdrawable amount (wei) before triggering withdraw"
|
187
222
|
)
|
188
223
|
|
224
|
+
# Behavior on exhausted runway
|
225
|
+
STREAM_REMOVE_MAPPING_ON_EXHAUSTED: bool = Field(
|
226
|
+
default=True,
|
227
|
+
description="When true, remove the VM->stream mapping after a successful stop on exhausted runway to prevent repeated stop attempts."
|
228
|
+
)
|
229
|
+
STREAM_DELETE_ON_EXHAUSTED: bool = Field(
|
230
|
+
default=False,
|
231
|
+
description="When true, delete the VM entirely once runway is exhausted and the VM has been stopped."
|
232
|
+
)
|
233
|
+
|
189
234
|
# Shutdown behavior
|
190
235
|
STOP_VMS_ON_EXIT: bool = Field(
|
191
236
|
default=False,
|
{golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/discovery/golem_base_advertiser.py
RENAMED
@@ -1,7 +1,14 @@
|
|
1
1
|
import asyncio
|
2
2
|
from typing import Optional
|
3
3
|
|
4
|
-
from golem_base_sdk import
|
4
|
+
from golem_base_sdk import (
|
5
|
+
GolemBaseClient,
|
6
|
+
GolemBaseCreate,
|
7
|
+
GolemBaseUpdate,
|
8
|
+
GolemBaseDelete,
|
9
|
+
Annotation,
|
10
|
+
GolemBaseExtend,
|
11
|
+
)
|
5
12
|
from .advertiser import Advertiser
|
6
13
|
from .golem_base_utils import get_provider_entity_keys
|
7
14
|
from ..config import settings
|
@@ -36,7 +43,9 @@ class GolemBaseAdvertiser(Advertiser):
|
|
36
43
|
try:
|
37
44
|
while not self._stop_event.is_set():
|
38
45
|
await self.post_advertisement()
|
39
|
-
|
46
|
+
# Check more frequently than full TTL to ensure seamless renewal
|
47
|
+
interval = max(30, int(settings.GOLEM_BASE_ADVERTISEMENT_INTERVAL // 3))
|
48
|
+
await asyncio.sleep(interval)
|
40
49
|
finally:
|
41
50
|
await self.stop()
|
42
51
|
|
@@ -122,14 +131,36 @@ class GolemBaseAdvertiser(Advertiser):
|
|
122
131
|
logger.info(f"Current on-chain annotations: {current_annotations}")
|
123
132
|
logger.info(f"Expected annotations based on current config: {expected_annotations}")
|
124
133
|
|
134
|
+
desired_btl_blocks = int(settings.GOLEM_BASE_ADVERTISEMENT_INTERVAL) * 2 # ~2 blocks/sec
|
135
|
+
|
125
136
|
if sorted(current_annotations.items()) == sorted(expected_annotations.items()):
|
126
|
-
|
137
|
+
# Refresh TTL proactively if nearing expiry
|
138
|
+
try:
|
139
|
+
current_block = await self.client.http_client().eth.get_block_number()
|
140
|
+
remaining_blocks = int(metadata.expires_at_block) - int(current_block)
|
141
|
+
except Exception:
|
142
|
+
# If we cannot determine remaining blocks, extend defensively
|
143
|
+
remaining_blocks = 0
|
144
|
+
|
145
|
+
# Refresh when below 20% of desired TTL (or if unknown/negative)
|
146
|
+
refresh_threshold = max(10, desired_btl_blocks // 5)
|
147
|
+
if remaining_blocks <= refresh_threshold:
|
148
|
+
logger.info(
|
149
|
+
f"Extending advertisement TTL (remaining_blocks={remaining_blocks}, threshold={refresh_threshold})."
|
150
|
+
)
|
151
|
+
ext = GolemBaseExtend(entity_key=entity_key, number_of_blocks=desired_btl_blocks)
|
152
|
+
await self.client.extend_entities([ext])
|
153
|
+
logger.info(f"Extended advertisement. Entity key: {entity_key}")
|
154
|
+
else:
|
155
|
+
logger.info(
|
156
|
+
f"Advertisement up-to-date; TTL sufficient (remaining_blocks={remaining_blocks})."
|
157
|
+
)
|
127
158
|
else:
|
128
159
|
logger.info("Advertisement is outdated. Updating.")
|
129
160
|
update = GolemBaseUpdate(
|
130
161
|
entity_key=entity_key,
|
131
162
|
data=b"",
|
132
|
-
btl=
|
163
|
+
btl=desired_btl_blocks,
|
133
164
|
string_annotations=string_annotations,
|
134
165
|
numeric_annotations=numeric_annotations,
|
135
166
|
)
|
@@ -144,9 +175,10 @@ class GolemBaseAdvertiser(Advertiser):
|
|
144
175
|
|
145
176
|
async def _create_advertisement(self, string_annotations, numeric_annotations):
|
146
177
|
"""Helper to create a new advertisement."""
|
178
|
+
desired_btl_blocks = int(settings.GOLEM_BASE_ADVERTISEMENT_INTERVAL) * 2 # ~2 blocks/sec
|
147
179
|
entity = GolemBaseCreate(
|
148
180
|
data=b"",
|
149
|
-
btl=
|
181
|
+
btl=desired_btl_blocks,
|
150
182
|
string_annotations=string_annotations,
|
151
183
|
numeric_annotations=numeric_annotations,
|
152
184
|
)
|
@@ -325,8 +325,8 @@ def status(json_out: bool = typer.Option(False, "--json", help="Output machine-r
|
|
325
325
|
latest = _get_latest_version_from_pypi(pkg)
|
326
326
|
update_available = bool(latest and current != latest)
|
327
327
|
|
328
|
-
# Environment
|
329
|
-
env = os.environ.get("
|
328
|
+
# Environment (use unified GOLEM_ENVIRONMENT only)
|
329
|
+
env = os.environ.get("GOLEM_ENVIRONMENT", _settings.ENVIRONMENT)
|
330
330
|
net = getattr(_settings, "NETWORK", None)
|
331
331
|
dev_mode = env == "development" or bool(getattr(_settings, "DEV_MODE", False))
|
332
332
|
|
@@ -1324,7 +1324,7 @@ def stop(timeout: int = typer.Option(15, "--timeout", help="Seconds to wait for
|
|
1324
1324
|
_remove_pid_file()
|
1325
1325
|
print("Provider stopped")
|
1326
1326
|
|
1327
|
-
# Removed separate 'dev' command; use environment
|
1327
|
+
# Removed separate 'dev' command; use environment GOLEM_ENVIRONMENT=development instead.
|
1328
1328
|
|
1329
1329
|
def _env_path_for(dev_mode: Optional[bool]) -> str:
|
1330
1330
|
from pathlib import Path
|
@@ -1535,9 +1535,10 @@ def run_server(
|
|
1535
1535
|
from pathlib import Path
|
1536
1536
|
from dotenv import load_dotenv
|
1537
1537
|
import uvicorn
|
1538
|
-
# Decide dev mode from explicit arg or environment
|
1538
|
+
# Decide dev mode from explicit arg or environment (unified only)
|
1539
1539
|
if dev_mode is None:
|
1540
|
-
|
1540
|
+
env_val = os.environ.get("GOLEM_ENVIRONMENT", "")
|
1541
|
+
dev_mode = env_val.lower() == "development"
|
1541
1542
|
|
1542
1543
|
# Load appropriate .env file based on mode
|
1543
1544
|
env_file = ".env.dev" if dev_mode else ".env"
|
@@ -54,7 +54,18 @@ class StreamMonitor:
|
|
54
54
|
try:
|
55
55
|
s = self.reader.get_stream(stream_id)
|
56
56
|
except Exception as e:
|
57
|
-
|
57
|
+
# No payment info available; delete the VM and remove mapping per unified policy
|
58
|
+
logger.info(
|
59
|
+
f"Deleting VM {vm_id} due to missing/unavailable payment stream (id={stream_id}): {e}"
|
60
|
+
)
|
61
|
+
try:
|
62
|
+
await self.vm_service.delete_vm(vm_id)
|
63
|
+
except Exception as del_err:
|
64
|
+
logger.warning(f"delete_vm failed for {vm_id} after stream lookup failure: {del_err}")
|
65
|
+
try:
|
66
|
+
await self.stream_map.remove(vm_id)
|
67
|
+
except Exception as rem_err:
|
68
|
+
logger.debug(f"failed to remove vm {vm_id} from stream map: {rem_err}")
|
58
69
|
continue
|
59
70
|
# Stop VM if remaining runway < threshold
|
60
71
|
remaining = max(int(s["stopTime"]) - int(now), 0)
|
@@ -69,33 +80,72 @@ class StreamMonitor:
|
|
69
80
|
)
|
70
81
|
try:
|
71
82
|
await self.vm_service.delete_vm(vm_id)
|
83
|
+
# Best-effort verification of deletion for investigation
|
84
|
+
try:
|
85
|
+
_ = await self.vm_service.get_vm_status(vm_id)
|
86
|
+
logger.info(
|
87
|
+
f"Post-delete status check: VM {vm_id} still present after delete request"
|
88
|
+
)
|
89
|
+
except VMNotFoundError:
|
90
|
+
logger.info(
|
91
|
+
f"Post-delete status check: VM {vm_id} not found (expected)"
|
92
|
+
)
|
93
|
+
except Exception as chk_err:
|
94
|
+
logger.debug(
|
95
|
+
f"Post-delete status check failed for {vm_id}: {chk_err}"
|
96
|
+
)
|
72
97
|
except Exception as e:
|
73
98
|
logger.warning(f"delete_vm failed for {vm_id}: {e}")
|
74
99
|
try:
|
75
100
|
await self.stream_map.remove(vm_id)
|
101
|
+
logger.debug(f"Removed {vm_id} from stream map after delete")
|
76
102
|
except Exception as e:
|
77
103
|
logger.debug(f"failed to remove vm {vm_id} from stream map: {e}")
|
78
104
|
continue
|
79
105
|
|
80
|
-
#
|
106
|
+
# If runway is exhausted, delete the VM and remove mapping
|
81
107
|
if remaining == 0:
|
82
108
|
logger.info(
|
83
|
-
f"
|
109
|
+
f"Deleting VM {vm_id} as stream runway is exhausted (id={stream_id}, now={now}, stop={s.get('stopTime')})"
|
84
110
|
)
|
111
|
+
# Capture pre-delete status for context
|
112
|
+
try:
|
113
|
+
pre = await self.vm_service.get_vm_status(vm_id)
|
114
|
+
logger.info(
|
115
|
+
f"Pre-delete status for {vm_id}: status={getattr(pre, 'status', '?')} ip={getattr(pre, 'ip_address', '?')}"
|
116
|
+
)
|
117
|
+
except VMNotFoundError:
|
118
|
+
logger.info(
|
119
|
+
f"Pre-delete status for {vm_id}: not found (will remove mapping)"
|
120
|
+
)
|
121
|
+
except Exception as pre_err:
|
122
|
+
logger.debug(f"Pre-delete status check failed for {vm_id}: {pre_err}")
|
123
|
+
|
85
124
|
try:
|
86
|
-
await self.vm_service.
|
87
|
-
|
88
|
-
# If the VM cannot be found, remove it from the stream map
|
89
|
-
# to avoid repeated stop attempts and log spam.
|
90
|
-
logger.warning(f"stop_vm failed for {vm_id}: {e}")
|
125
|
+
await self.vm_service.delete_vm(vm_id)
|
126
|
+
# Verify deletion
|
91
127
|
try:
|
92
|
-
await self.
|
93
|
-
|
128
|
+
_ = await self.vm_service.get_vm_status(vm_id)
|
129
|
+
logger.info(
|
130
|
+
f"Post-delete status check: VM {vm_id} still present after delete request"
|
131
|
+
)
|
132
|
+
except VMNotFoundError:
|
133
|
+
logger.info(
|
134
|
+
f"Post-delete status check: VM {vm_id} not found (expected)"
|
135
|
+
)
|
136
|
+
except Exception as chk_err:
|
94
137
|
logger.debug(
|
95
|
-
f"
|
138
|
+
f"Post-delete status check failed for {vm_id}: {chk_err}"
|
96
139
|
)
|
97
140
|
except Exception as e:
|
98
|
-
logger.warning(f"
|
141
|
+
logger.warning(f"delete_vm failed for {vm_id}: {e}")
|
142
|
+
try:
|
143
|
+
await self.stream_map.remove(vm_id)
|
144
|
+
logger.info(f"Removed mapping for {vm_id} after delete on exhausted runway")
|
145
|
+
except Exception as rem_err:
|
146
|
+
logger.debug(
|
147
|
+
f"failed to remove vm {vm_id} from stream map after delete: {rem_err}"
|
148
|
+
)
|
99
149
|
continue
|
100
150
|
|
101
151
|
# Otherwise, do not stop; just log health and consider withdrawals
|
@@ -87,7 +87,8 @@ class MultipassAdapter(VMProvider):
|
|
87
87
|
"""Get detailed information about a VM."""
|
88
88
|
try:
|
89
89
|
result = await self._run_multipass(["info", vm_id, "--format", "json"])
|
90
|
-
|
90
|
+
# Only log raw multipass output in debug mode to avoid noisy logs
|
91
|
+
logger.debug(f"Raw multipass info for {vm_id}: {result.stdout}")
|
91
92
|
info = json.loads(result.stdout)
|
92
93
|
vm_info = info["info"][vm_id]
|
93
94
|
essential_fields = ["state", "ipv4", "cpu_count", "memory", "disks"]
|
@@ -59,6 +59,7 @@ class VMService:
|
|
59
59
|
|
60
60
|
try:
|
61
61
|
vm_info = await self.provider.get_vm_status(multipass_name)
|
62
|
+
logger.info(f"Deleting VM {vm_id} (multipass={multipass_name}) with status={vm_info.status}")
|
62
63
|
await self.provider.delete_vm(multipass_name)
|
63
64
|
await self.resource_tracker.deallocate(vm_info.resources, vm_id)
|
64
65
|
# Optional: best-effort on-chain termination if we have a mapping
|
@@ -83,7 +84,9 @@ class VMService:
|
|
83
84
|
multipass_name = await self.name_mapper.get_multipass_name(vm_id)
|
84
85
|
if not multipass_name:
|
85
86
|
raise VMNotFoundError(f"VM {vm_id} not found")
|
87
|
+
logger.info(f"Stopping VM {vm_id} (multipass={multipass_name})")
|
86
88
|
vm = await self.provider.stop_vm(multipass_name)
|
89
|
+
logger.info(f"Stopped VM {vm_id} result status={getattr(vm, 'status', '?')}")
|
87
90
|
# Optional: best-effort withdraw for active stream
|
88
91
|
try:
|
89
92
|
if self.blockchain_client:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "golem-vm-provider"
|
3
|
-
version = "0.1.
|
3
|
+
version = "0.1.61"
|
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
|
{golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/discovery/golem_base_utils.py
RENAMED
File without changes
|
{golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/discovery/multi_advertiser.py
RENAMED
File without changes
|
{golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/discovery/resource_monitor.py
RENAMED
File without changes
|
{golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/provider/discovery/resource_tracker.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{golem_vm_provider-0.1.60 → golem_vm_provider-0.1.61}/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
|