golem-vm-provider 0.1.12__tar.gz → 0.1.14__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.12 → golem_vm_provider-0.1.14}/PKG-INFO +1 -1
- golem_vm_provider-0.1.14/provider/config.py +280 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/main.py +4 -32
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/pyproject.toml +1 -1
- golem_vm_provider-0.1.12/provider/config.py +0 -218
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/README.md +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/__init__.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/api/__init__.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/api/models.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/api/routes.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/discovery/__init__.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/discovery/advertiser.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/discovery/resource_tracker.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/network/port_verifier.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/security/ethereum.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/utils/ascii_art.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/utils/logging.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/utils/port_display.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/utils/retry.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/vm/__init__.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/vm/cloud_init.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/vm/models.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/vm/multipass.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/vm/name_mapper.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/vm/port_manager.py +0 -0
- {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.14}/provider/vm/proxy_manager.py +0 -0
@@ -0,0 +1,280 @@
|
|
1
|
+
import os
|
2
|
+
from pathlib import Path
|
3
|
+
from typing import Optional
|
4
|
+
import uuid
|
5
|
+
|
6
|
+
from pydantic import BaseSettings, validator, Field
|
7
|
+
from .utils.logging import setup_logger
|
8
|
+
|
9
|
+
logger = setup_logger(__name__)
|
10
|
+
|
11
|
+
|
12
|
+
class Settings(BaseSettings):
|
13
|
+
"""Provider configuration settings."""
|
14
|
+
|
15
|
+
# API Settings
|
16
|
+
DEBUG: bool = True
|
17
|
+
HOST: str = "0.0.0.0"
|
18
|
+
PORT: int = 7466
|
19
|
+
|
20
|
+
# Provider Settings
|
21
|
+
PROVIDER_ID: str = "" # Will be set from Ethereum identity
|
22
|
+
PROVIDER_NAME: str = "golem-provider"
|
23
|
+
PROVIDER_COUNTRY: str = "SE"
|
24
|
+
ETHEREUM_KEY_DIR: str = ""
|
25
|
+
|
26
|
+
@validator("ETHEREUM_KEY_DIR", pre=True)
|
27
|
+
def resolve_key_dir(cls, v: str) -> str:
|
28
|
+
"""Resolve Ethereum key directory path."""
|
29
|
+
if not v:
|
30
|
+
return str(Path.home() / ".golem" / "provider" / "keys")
|
31
|
+
path = Path(v)
|
32
|
+
if not path.is_absolute():
|
33
|
+
path = Path.home() / path
|
34
|
+
return str(path)
|
35
|
+
|
36
|
+
@validator("PROVIDER_ID", always=True)
|
37
|
+
def get_or_create_provider_id(cls, v: str, values: dict) -> str:
|
38
|
+
"""Get or create provider ID from Ethereum identity."""
|
39
|
+
from provider.security.ethereum import EthereumIdentity
|
40
|
+
|
41
|
+
# If ID provided in env, use it
|
42
|
+
if v:
|
43
|
+
return v
|
44
|
+
|
45
|
+
# Get ID from Ethereum identity
|
46
|
+
key_dir = values.get("ETHEREUM_KEY_DIR")
|
47
|
+
identity = EthereumIdentity(key_dir)
|
48
|
+
return identity.get_or_create_identity()
|
49
|
+
|
50
|
+
# Discovery Service Settings
|
51
|
+
DISCOVERY_URL: str = "http://195.201.39.101:9001"
|
52
|
+
ADVERTISEMENT_INTERVAL: int = 240 # seconds
|
53
|
+
|
54
|
+
# VM Settings
|
55
|
+
MAX_VMS: int = 10
|
56
|
+
DEFAULT_VM_IMAGE: str = "ubuntu:24.04"
|
57
|
+
VM_DATA_DIR: str = ""
|
58
|
+
SSH_KEY_DIR: str = ""
|
59
|
+
|
60
|
+
@validator("VM_DATA_DIR", pre=True)
|
61
|
+
def resolve_vm_data_dir(cls, v: str) -> str:
|
62
|
+
"""Resolve and create VM data directory path."""
|
63
|
+
if not v:
|
64
|
+
path = Path.home() / ".golem" / "provider" / "vms"
|
65
|
+
else:
|
66
|
+
path = Path(v)
|
67
|
+
if not path.is_absolute():
|
68
|
+
path = Path.home() / path
|
69
|
+
|
70
|
+
try:
|
71
|
+
path.mkdir(parents=True, exist_ok=True)
|
72
|
+
logger.debug(f"Created VM data directory at {path}")
|
73
|
+
except Exception as e:
|
74
|
+
logger.error(f"Failed to create VM data directory at {path}: {e}")
|
75
|
+
raise ValueError(f"Failed to create VM data directory: {e}")
|
76
|
+
|
77
|
+
return str(path)
|
78
|
+
|
79
|
+
@validator("SSH_KEY_DIR", pre=True)
|
80
|
+
def resolve_ssh_key_dir(cls, v: str) -> str:
|
81
|
+
"""Resolve and create SSH key directory path with secure permissions."""
|
82
|
+
if not v:
|
83
|
+
path = Path.home() / ".golem" / "provider" / "ssh"
|
84
|
+
else:
|
85
|
+
path = Path(v)
|
86
|
+
if not path.is_absolute():
|
87
|
+
path = Path.home() / path
|
88
|
+
|
89
|
+
try:
|
90
|
+
path.mkdir(parents=True, exist_ok=True)
|
91
|
+
path.chmod(0o700) # Secure permissions for SSH keys
|
92
|
+
logger.debug(f"Created SSH key directory at {path} with secure permissions")
|
93
|
+
except Exception as e:
|
94
|
+
logger.error(f"Failed to create SSH key directory at {path}: {e}")
|
95
|
+
raise ValueError(f"Failed to create SSH key directory: {e}")
|
96
|
+
|
97
|
+
return str(path)
|
98
|
+
|
99
|
+
# Resource Settings
|
100
|
+
MIN_MEMORY_GB: int = 1
|
101
|
+
MIN_STORAGE_GB: int = 10
|
102
|
+
MIN_CPU_CORES: int = 1
|
103
|
+
|
104
|
+
# Resource Thresholds (%)
|
105
|
+
CPU_THRESHOLD: int = 90
|
106
|
+
MEMORY_THRESHOLD: int = 85
|
107
|
+
STORAGE_THRESHOLD: int = 90
|
108
|
+
|
109
|
+
# Rate Limiting
|
110
|
+
RATE_LIMIT_PER_MINUTE: int = 100
|
111
|
+
|
112
|
+
# Multipass Settings
|
113
|
+
MULTIPASS_BINARY_PATH: str = Field(
|
114
|
+
default="",
|
115
|
+
description="Path to multipass binary"
|
116
|
+
)
|
117
|
+
|
118
|
+
@validator("MULTIPASS_BINARY_PATH")
|
119
|
+
def detect_multipass_path(cls, v: str) -> str:
|
120
|
+
"""Detect and validate Multipass binary path."""
|
121
|
+
import platform
|
122
|
+
import subprocess
|
123
|
+
|
124
|
+
def validate_path(path: str) -> bool:
|
125
|
+
"""Validate that a path exists and is executable."""
|
126
|
+
return os.path.isfile(path) and os.access(path, os.X_OK)
|
127
|
+
|
128
|
+
# If path provided via environment variable, ONLY validate that path
|
129
|
+
if v:
|
130
|
+
logger.info(f"Checking multipass binary at: {v}")
|
131
|
+
if not validate_path(v):
|
132
|
+
msg = f"Invalid multipass binary path: {v} (not found or not executable)"
|
133
|
+
logger.error(msg)
|
134
|
+
raise ValueError(msg)
|
135
|
+
logger.info(f"✓ Found valid multipass binary at: {v}")
|
136
|
+
return v
|
137
|
+
|
138
|
+
logger.info("No multipass path provided, attempting auto-detection...")
|
139
|
+
system = platform.system().lower()
|
140
|
+
logger.info(f"Detected OS: {system}")
|
141
|
+
binary_name = "multipass.exe" if system == "windows" else "multipass"
|
142
|
+
|
143
|
+
# Try to find multipass based on OS
|
144
|
+
if system == "linux":
|
145
|
+
logger.info("Checking for snap installation...")
|
146
|
+
# First try to find snap and check if multipass is installed
|
147
|
+
try:
|
148
|
+
# Check if snap exists
|
149
|
+
snap_result = subprocess.run(
|
150
|
+
["which", "snap"],
|
151
|
+
capture_output=True,
|
152
|
+
text=True,
|
153
|
+
check=True
|
154
|
+
)
|
155
|
+
if snap_result.returncode == 0:
|
156
|
+
logger.info("✓ Found snap, checking for multipass installation...")
|
157
|
+
# Check if multipass is installed via snap
|
158
|
+
try:
|
159
|
+
snap_list = subprocess.run(
|
160
|
+
["snap", "list", "multipass"],
|
161
|
+
capture_output=True,
|
162
|
+
text=True,
|
163
|
+
check=True
|
164
|
+
)
|
165
|
+
if snap_list.returncode == 0:
|
166
|
+
snap_path = "/snap/bin/multipass"
|
167
|
+
if validate_path(snap_path):
|
168
|
+
logger.info(f"✓ Found multipass via snap at {snap_path}")
|
169
|
+
return snap_path
|
170
|
+
except subprocess.CalledProcessError:
|
171
|
+
logger.info("✗ Multipass not installed via snap")
|
172
|
+
pass
|
173
|
+
except subprocess.CalledProcessError:
|
174
|
+
logger.info("✗ Snap not found")
|
175
|
+
pass
|
176
|
+
|
177
|
+
# Common Linux paths if snap installation not found
|
178
|
+
search_paths = [
|
179
|
+
"/usr/local/bin",
|
180
|
+
"/usr/bin",
|
181
|
+
"/snap/bin"
|
182
|
+
]
|
183
|
+
logger.info(f"Checking common Linux paths: {', '.join(search_paths)}")
|
184
|
+
|
185
|
+
elif system == "darwin": # macOS
|
186
|
+
search_paths = [
|
187
|
+
"/opt/homebrew/bin", # M1 Mac
|
188
|
+
"/usr/local/bin", # Intel Mac
|
189
|
+
"/opt/local/bin" # MacPorts
|
190
|
+
]
|
191
|
+
logger.info(f"Checking macOS paths: {', '.join(search_paths)}")
|
192
|
+
|
193
|
+
elif system == "windows":
|
194
|
+
search_paths = [
|
195
|
+
os.path.expandvars(r"%ProgramFiles%\Multipass"),
|
196
|
+
os.path.expandvars(r"%ProgramFiles(x86)%\Multipass"),
|
197
|
+
os.path.expandvars(r"%LocalAppData%\Multipass")
|
198
|
+
]
|
199
|
+
logger.info(f"Checking Windows paths: {', '.join(search_paths)}")
|
200
|
+
|
201
|
+
else:
|
202
|
+
search_paths = ["/usr/local/bin", "/usr/bin"]
|
203
|
+
logger.info(f"Checking default paths: {', '.join(search_paths)}")
|
204
|
+
|
205
|
+
# Search for multipass binary in OS-specific paths
|
206
|
+
for directory in search_paths:
|
207
|
+
path = os.path.join(directory, binary_name)
|
208
|
+
if validate_path(path):
|
209
|
+
logger.info(f"✓ Found valid multipass binary at: {path}")
|
210
|
+
return path
|
211
|
+
|
212
|
+
# OS-specific installation instructions
|
213
|
+
if system == "linux":
|
214
|
+
raise ValueError(
|
215
|
+
"Multipass binary not found. Please install using:\n"
|
216
|
+
"sudo snap install multipass\n"
|
217
|
+
"Or set GOLEM_PROVIDER_MULTIPASS_BINARY_PATH to your Multipass binary path."
|
218
|
+
)
|
219
|
+
elif system == "darwin":
|
220
|
+
raise ValueError(
|
221
|
+
"Multipass binary not found. Please install using:\n"
|
222
|
+
"brew install multipass\n"
|
223
|
+
"Or set GOLEM_PROVIDER_MULTIPASS_BINARY_PATH to your Multipass binary path."
|
224
|
+
)
|
225
|
+
elif system == "windows":
|
226
|
+
raise ValueError(
|
227
|
+
"Multipass binary not found. Please install from:\n"
|
228
|
+
"Microsoft Store or https://multipass.run/download/windows\n"
|
229
|
+
"Or set GOLEM_PROVIDER_MULTIPASS_BINARY_PATH to your Multipass binary path."
|
230
|
+
)
|
231
|
+
else:
|
232
|
+
raise ValueError(
|
233
|
+
"Multipass binary not found. Please install Multipass or set "
|
234
|
+
"GOLEM_PROVIDER_MULTIPASS_BINARY_PATH to your Multipass binary path."
|
235
|
+
)
|
236
|
+
|
237
|
+
# Proxy Settings
|
238
|
+
PORT_RANGE_START: int = 50800
|
239
|
+
PORT_RANGE_END: int = 50900
|
240
|
+
PROXY_STATE_DIR: str = ""
|
241
|
+
PUBLIC_IP: Optional[str] = None
|
242
|
+
|
243
|
+
@validator("PROXY_STATE_DIR", pre=True)
|
244
|
+
def resolve_proxy_state_dir(cls, v: str) -> str:
|
245
|
+
"""Resolve and create proxy state directory path."""
|
246
|
+
if not v:
|
247
|
+
path = Path.home() / ".golem" / "provider" / "proxy"
|
248
|
+
else:
|
249
|
+
path = Path(v)
|
250
|
+
if not path.is_absolute():
|
251
|
+
path = Path.home() / path
|
252
|
+
|
253
|
+
try:
|
254
|
+
path.mkdir(parents=True, exist_ok=True)
|
255
|
+
logger.debug(f"Created proxy state directory at {path}")
|
256
|
+
except Exception as e:
|
257
|
+
logger.error(f"Failed to create proxy state directory at {path}: {e}")
|
258
|
+
raise ValueError(f"Failed to create proxy state directory: {e}")
|
259
|
+
|
260
|
+
return str(path)
|
261
|
+
|
262
|
+
@validator("PUBLIC_IP", pre=True)
|
263
|
+
def get_public_ip(cls, v: Optional[str]) -> Optional[str]:
|
264
|
+
"""Get public IP if set to 'auto'."""
|
265
|
+
if v == "auto":
|
266
|
+
try:
|
267
|
+
import requests
|
268
|
+
response = requests.get("https://api.ipify.org")
|
269
|
+
return response.text.strip()
|
270
|
+
except Exception:
|
271
|
+
return None
|
272
|
+
return v
|
273
|
+
|
274
|
+
class Config:
|
275
|
+
env_prefix = "GOLEM_PROVIDER_"
|
276
|
+
case_sensitive = True
|
277
|
+
|
278
|
+
|
279
|
+
# Global settings instance
|
280
|
+
settings = Settings()
|
@@ -163,41 +163,13 @@ __all__ = ["app", "start"]
|
|
163
163
|
|
164
164
|
def check_requirements():
|
165
165
|
"""Check if all requirements are met."""
|
166
|
-
import os
|
167
|
-
from pathlib import Path
|
168
|
-
|
169
|
-
# Check if multipass is installed
|
170
|
-
multipass_path = os.environ.get('GOLEM_PROVIDER_MULTIPASS_BINARY_PATH', '/usr/local/bin/multipass')
|
171
|
-
if not Path(multipass_path).exists():
|
172
|
-
logger.error(f"Multipass binary not found at {multipass_path}")
|
173
|
-
return False
|
174
|
-
|
175
|
-
# Check required directories
|
176
|
-
vm_data_dir = os.environ.get(
|
177
|
-
'GOLEM_PROVIDER_VM_DATA_DIR',
|
178
|
-
str(Path.home() / '.golem' / 'provider' / 'vms')
|
179
|
-
)
|
180
|
-
ssh_key_dir = os.environ.get(
|
181
|
-
'GOLEM_PROVIDER_SSH_KEY_DIR',
|
182
|
-
str(Path.home() / '.golem' / 'provider' / 'ssh')
|
183
|
-
)
|
184
|
-
proxy_state_dir = os.environ.get(
|
185
|
-
'GOLEM_PROVIDER_PROXY_STATE_DIR',
|
186
|
-
str(Path.home() / '.golem' / 'provider' / 'proxy')
|
187
|
-
)
|
188
|
-
|
189
166
|
try:
|
190
|
-
#
|
191
|
-
|
192
|
-
|
193
|
-
path.mkdir(parents=True, exist_ok=True)
|
194
|
-
if directory == ssh_key_dir:
|
195
|
-
path.chmod(0o700) # Secure permissions for SSH keys
|
167
|
+
# Import settings to trigger validation
|
168
|
+
from .config import settings
|
169
|
+
return True
|
196
170
|
except Exception as e:
|
197
|
-
logger.error(f"
|
171
|
+
logger.error(f"Requirements check failed: {e}")
|
198
172
|
return False
|
199
|
-
|
200
|
-
return True
|
201
173
|
|
202
174
|
async def verify_provider_port(port: int) -> bool:
|
203
175
|
"""Verify that the provider port is available for binding.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "golem-vm-provider"
|
3
|
-
version = "0.1.
|
3
|
+
version = "0.1.14"
|
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"
|
@@ -1,218 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
from pathlib import Path
|
3
|
-
from typing import Optional
|
4
|
-
import uuid
|
5
|
-
|
6
|
-
from pydantic import BaseSettings, validator
|
7
|
-
|
8
|
-
|
9
|
-
class Settings(BaseSettings):
|
10
|
-
"""Provider configuration settings."""
|
11
|
-
|
12
|
-
# API Settings
|
13
|
-
DEBUG: bool = True
|
14
|
-
HOST: str = "0.0.0.0"
|
15
|
-
PORT: int = 7466
|
16
|
-
|
17
|
-
# Provider Settings
|
18
|
-
PROVIDER_ID: str = "" # Will be set from Ethereum identity
|
19
|
-
PROVIDER_NAME: str = "golem-provider"
|
20
|
-
PROVIDER_COUNTRY: str = "SE"
|
21
|
-
ETHEREUM_KEY_DIR: str = ""
|
22
|
-
|
23
|
-
@validator("ETHEREUM_KEY_DIR", pre=True)
|
24
|
-
def resolve_key_dir(cls, v: str) -> str:
|
25
|
-
"""Resolve Ethereum key directory path."""
|
26
|
-
if not v:
|
27
|
-
return str(Path.home() / ".golem" / "provider" / "keys")
|
28
|
-
path = Path(v)
|
29
|
-
if not path.is_absolute():
|
30
|
-
path = Path.home() / path
|
31
|
-
return str(path)
|
32
|
-
|
33
|
-
@validator("PROVIDER_ID", always=True)
|
34
|
-
def get_or_create_provider_id(cls, v: str, values: dict) -> str:
|
35
|
-
"""Get or create provider ID from Ethereum identity."""
|
36
|
-
from provider.security.ethereum import EthereumIdentity
|
37
|
-
|
38
|
-
# If ID provided in env, use it
|
39
|
-
if v:
|
40
|
-
return v
|
41
|
-
|
42
|
-
# Get ID from Ethereum identity
|
43
|
-
key_dir = values.get("ETHEREUM_KEY_DIR")
|
44
|
-
identity = EthereumIdentity(key_dir)
|
45
|
-
return identity.get_or_create_identity()
|
46
|
-
|
47
|
-
# Discovery Service Settings
|
48
|
-
DISCOVERY_URL: str = "http://195.201.39.101:9001"
|
49
|
-
ADVERTISEMENT_INTERVAL: int = 240 # seconds
|
50
|
-
|
51
|
-
# VM Settings
|
52
|
-
MAX_VMS: int = 10
|
53
|
-
DEFAULT_VM_IMAGE: str = "ubuntu:24.04"
|
54
|
-
VM_DATA_DIR: str = ""
|
55
|
-
SSH_KEY_DIR: str = ""
|
56
|
-
|
57
|
-
@validator("VM_DATA_DIR", pre=True)
|
58
|
-
def resolve_vm_data_dir(cls, v: str) -> str:
|
59
|
-
"""Resolve VM data directory path."""
|
60
|
-
if not v:
|
61
|
-
return str(Path.home() / ".golem" / "provider" / "vms")
|
62
|
-
path = Path(v)
|
63
|
-
if not path.is_absolute():
|
64
|
-
path = Path.home() / path
|
65
|
-
return str(path)
|
66
|
-
|
67
|
-
@validator("SSH_KEY_DIR", pre=True)
|
68
|
-
def resolve_ssh_key_dir(cls, v: str) -> str:
|
69
|
-
"""Resolve SSH key directory path."""
|
70
|
-
if not v:
|
71
|
-
return str(Path.home() / ".golem" / "provider" / "ssh")
|
72
|
-
path = Path(v)
|
73
|
-
if not path.is_absolute():
|
74
|
-
path = Path.home() / path
|
75
|
-
return str(path)
|
76
|
-
|
77
|
-
# Resource Settings
|
78
|
-
MIN_MEMORY_GB: int = 1
|
79
|
-
MIN_STORAGE_GB: int = 10
|
80
|
-
MIN_CPU_CORES: int = 1
|
81
|
-
|
82
|
-
# Resource Thresholds (%)
|
83
|
-
CPU_THRESHOLD: int = 90
|
84
|
-
MEMORY_THRESHOLD: int = 85
|
85
|
-
STORAGE_THRESHOLD: int = 90
|
86
|
-
|
87
|
-
# Rate Limiting
|
88
|
-
RATE_LIMIT_PER_MINUTE: int = 100
|
89
|
-
|
90
|
-
# Multipass Settings
|
91
|
-
MULTIPASS_BINARY_PATH: str = ""
|
92
|
-
|
93
|
-
@validator("MULTIPASS_BINARY_PATH", pre=True)
|
94
|
-
def detect_multipass_path(cls, v: str) -> str:
|
95
|
-
"""Detect and validate Multipass binary path."""
|
96
|
-
if v:
|
97
|
-
path = v
|
98
|
-
else:
|
99
|
-
import platform
|
100
|
-
import subprocess
|
101
|
-
|
102
|
-
system = platform.system().lower()
|
103
|
-
binary_name = "multipass.exe" if system == "windows" else "multipass"
|
104
|
-
|
105
|
-
# Try to find multipass based on OS
|
106
|
-
if system == "linux":
|
107
|
-
# First try to find snap
|
108
|
-
try:
|
109
|
-
snap_result = subprocess.run(
|
110
|
-
["which", "snap"],
|
111
|
-
capture_output=True,
|
112
|
-
text=True
|
113
|
-
)
|
114
|
-
if snap_result.returncode == 0:
|
115
|
-
# If snap exists, check if multipass is installed
|
116
|
-
snap_path = "/snap/bin/multipass"
|
117
|
-
if os.path.isfile(snap_path) and os.access(snap_path, os.X_OK):
|
118
|
-
return snap_path
|
119
|
-
except subprocess.SubprocessError:
|
120
|
-
pass
|
121
|
-
|
122
|
-
# Common Linux paths
|
123
|
-
search_paths = [
|
124
|
-
"/usr/local/bin",
|
125
|
-
"/usr/bin",
|
126
|
-
"/snap/bin"
|
127
|
-
]
|
128
|
-
|
129
|
-
elif system == "darwin": # macOS
|
130
|
-
search_paths = [
|
131
|
-
"/opt/homebrew/bin", # M1 Mac
|
132
|
-
"/usr/local/bin", # Intel Mac
|
133
|
-
"/opt/local/bin" # MacPorts
|
134
|
-
]
|
135
|
-
|
136
|
-
elif system == "windows":
|
137
|
-
search_paths = [
|
138
|
-
os.path.expandvars(r"%ProgramFiles%\Multipass"),
|
139
|
-
os.path.expandvars(r"%ProgramFiles(x86)%\Multipass"),
|
140
|
-
os.path.expandvars(r"%LocalAppData%\Multipass")
|
141
|
-
]
|
142
|
-
|
143
|
-
else:
|
144
|
-
search_paths = ["/usr/local/bin", "/usr/bin"]
|
145
|
-
|
146
|
-
# Search for multipass binary in OS-specific paths
|
147
|
-
for directory in search_paths:
|
148
|
-
path = os.path.join(directory, binary_name)
|
149
|
-
if os.path.isfile(path) and os.access(path, os.X_OK):
|
150
|
-
return path
|
151
|
-
|
152
|
-
# OS-specific installation instructions
|
153
|
-
if system == "linux":
|
154
|
-
raise ValueError(
|
155
|
-
"Multipass binary not found. Please install using:\n"
|
156
|
-
"sudo snap install multipass\n"
|
157
|
-
"Or set GOLEM_PROVIDER_MULTIPASS_BINARY_PATH to your Multipass binary path."
|
158
|
-
)
|
159
|
-
elif system == "darwin":
|
160
|
-
raise ValueError(
|
161
|
-
"Multipass binary not found. Please install using:\n"
|
162
|
-
"brew install multipass\n"
|
163
|
-
"Or set GOLEM_PROVIDER_MULTIPASS_BINARY_PATH to your Multipass binary path."
|
164
|
-
)
|
165
|
-
elif system == "windows":
|
166
|
-
raise ValueError(
|
167
|
-
"Multipass binary not found. Please install from:\n"
|
168
|
-
"Microsoft Store or https://multipass.run/download/windows\n"
|
169
|
-
"Or set GOLEM_PROVIDER_MULTIPASS_BINARY_PATH to your Multipass binary path."
|
170
|
-
)
|
171
|
-
else:
|
172
|
-
raise ValueError(
|
173
|
-
"Multipass binary not found. Please install Multipass or set "
|
174
|
-
"GOLEM_PROVIDER_MULTIPASS_BINARY_PATH to your Multipass binary path."
|
175
|
-
)
|
176
|
-
|
177
|
-
# Validate the path
|
178
|
-
if not os.path.isfile(path):
|
179
|
-
raise ValueError(f"Multipass binary not found at: {path}")
|
180
|
-
if not os.access(path, os.X_OK):
|
181
|
-
raise ValueError(f"Multipass binary at {path} is not executable")
|
182
|
-
return path
|
183
|
-
|
184
|
-
# Proxy Settings
|
185
|
-
PORT_RANGE_START: int = 50800
|
186
|
-
PORT_RANGE_END: int = 50900
|
187
|
-
PROXY_STATE_DIR: str = ""
|
188
|
-
PUBLIC_IP: Optional[str] = None
|
189
|
-
|
190
|
-
@validator("PROXY_STATE_DIR", pre=True)
|
191
|
-
def resolve_proxy_state_dir(cls, v: str) -> str:
|
192
|
-
"""Resolve proxy state directory path."""
|
193
|
-
if not v:
|
194
|
-
return str(Path.home() / ".golem" / "provider" / "proxy")
|
195
|
-
path = Path(v)
|
196
|
-
if not path.is_absolute():
|
197
|
-
path = Path.home() / path
|
198
|
-
return str(path)
|
199
|
-
|
200
|
-
@validator("PUBLIC_IP", pre=True)
|
201
|
-
def get_public_ip(cls, v: Optional[str]) -> Optional[str]:
|
202
|
-
"""Get public IP if set to 'auto'."""
|
203
|
-
if v == "auto":
|
204
|
-
try:
|
205
|
-
import requests
|
206
|
-
response = requests.get("https://api.ipify.org")
|
207
|
-
return response.text.strip()
|
208
|
-
except Exception:
|
209
|
-
return None
|
210
|
-
return v
|
211
|
-
|
212
|
-
class Config:
|
213
|
-
env_prefix = "GOLEM_PROVIDER_"
|
214
|
-
case_sensitive = True
|
215
|
-
|
216
|
-
|
217
|
-
# Global settings instance
|
218
|
-
settings = Settings()
|
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.12 → golem_vm_provider-0.1.14}/provider/discovery/resource_tracker.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
|