golem-vm-provider 0.1.12__tar.gz → 0.1.13__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.
Files changed (26) hide show
  1. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/PKG-INFO +1 -1
  2. golem_vm_provider-0.1.13/provider/config.py +253 -0
  3. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/pyproject.toml +1 -1
  4. golem_vm_provider-0.1.12/provider/config.py +0 -218
  5. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/README.md +0 -0
  6. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/__init__.py +0 -0
  7. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/api/__init__.py +0 -0
  8. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/api/models.py +0 -0
  9. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/api/routes.py +0 -0
  10. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/discovery/__init__.py +0 -0
  11. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/discovery/advertiser.py +0 -0
  12. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/discovery/resource_tracker.py +0 -0
  13. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/main.py +0 -0
  14. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/network/port_verifier.py +0 -0
  15. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/security/ethereum.py +0 -0
  16. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/utils/ascii_art.py +0 -0
  17. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/utils/logging.py +0 -0
  18. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/utils/port_display.py +0 -0
  19. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/utils/retry.py +0 -0
  20. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/vm/__init__.py +0 -0
  21. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/vm/cloud_init.py +0 -0
  22. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/vm/models.py +0 -0
  23. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/vm/multipass.py +0 -0
  24. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/vm/name_mapper.py +0 -0
  25. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/provider/vm/port_manager.py +0 -0
  26. {golem_vm_provider-0.1.12 → golem_vm_provider-0.1.13}/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.12
3
+ Version: 0.1.13
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
@@ -0,0 +1,253 @@
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 VM data directory path."""
63
+ if not v:
64
+ return str(Path.home() / ".golem" / "provider" / "vms")
65
+ path = Path(v)
66
+ if not path.is_absolute():
67
+ path = Path.home() / path
68
+ return str(path)
69
+
70
+ @validator("SSH_KEY_DIR", pre=True)
71
+ def resolve_ssh_key_dir(cls, v: str) -> str:
72
+ """Resolve SSH key directory path."""
73
+ if not v:
74
+ return str(Path.home() / ".golem" / "provider" / "ssh")
75
+ path = Path(v)
76
+ if not path.is_absolute():
77
+ path = Path.home() / path
78
+ return str(path)
79
+
80
+ # Resource Settings
81
+ MIN_MEMORY_GB: int = 1
82
+ MIN_STORAGE_GB: int = 10
83
+ MIN_CPU_CORES: int = 1
84
+
85
+ # Resource Thresholds (%)
86
+ CPU_THRESHOLD: int = 90
87
+ MEMORY_THRESHOLD: int = 85
88
+ STORAGE_THRESHOLD: int = 90
89
+
90
+ # Rate Limiting
91
+ RATE_LIMIT_PER_MINUTE: int = 100
92
+
93
+ # Multipass Settings
94
+ MULTIPASS_BINARY_PATH: str = Field(
95
+ default="",
96
+ description="Path to multipass binary"
97
+ )
98
+
99
+ @validator("MULTIPASS_BINARY_PATH")
100
+ def detect_multipass_path(cls, v: str) -> str:
101
+ """Detect and validate Multipass binary path."""
102
+ import platform
103
+ import subprocess
104
+
105
+ def validate_path(path: str) -> bool:
106
+ """Validate that a path exists and is executable."""
107
+ return os.path.isfile(path) and os.access(path, os.X_OK)
108
+
109
+ # If path provided via environment variable, ONLY validate that path
110
+ if v:
111
+ logger.debug(f"Using provided multipass path: {v}")
112
+ if not validate_path(v):
113
+ logger.error(f"Provided path {v} is invalid or not executable")
114
+ raise ValueError(f"Invalid multipass binary path: {v}")
115
+ return v
116
+
117
+ logger.debug("No multipass path provided, attempting auto-detection")
118
+ system = platform.system().lower()
119
+ logger.debug(f"Detected OS: {system}")
120
+ binary_name = "multipass.exe" if system == "windows" else "multipass"
121
+
122
+ # Try to find multipass based on OS
123
+ if system == "linux":
124
+ logger.debug("Checking for snap installation on Linux")
125
+ # First try to find snap and check if multipass is installed
126
+ try:
127
+ # Check if snap exists
128
+ snap_result = subprocess.run(
129
+ ["which", "snap"],
130
+ capture_output=True,
131
+ text=True,
132
+ check=True
133
+ )
134
+ if snap_result.returncode == 0:
135
+ logger.debug("Found snap, checking for multipass installation")
136
+ # Check if multipass is installed via snap
137
+ try:
138
+ snap_list = subprocess.run(
139
+ ["snap", "list", "multipass"],
140
+ capture_output=True,
141
+ text=True,
142
+ check=True
143
+ )
144
+ if snap_list.returncode == 0:
145
+ snap_path = "/snap/bin/multipass"
146
+ if validate_path(snap_path):
147
+ logger.debug(f"Found multipass via snap at {snap_path}")
148
+ return snap_path
149
+ except subprocess.CalledProcessError:
150
+ logger.debug("Multipass not installed via snap")
151
+ pass
152
+ except subprocess.CalledProcessError:
153
+ logger.debug("Snap not found")
154
+ pass
155
+
156
+ # Common Linux paths if snap installation not found
157
+ search_paths = [
158
+ "/usr/local/bin",
159
+ "/usr/bin",
160
+ "/snap/bin"
161
+ ]
162
+ logger.debug(f"Checking common Linux paths: {search_paths}")
163
+
164
+ elif system == "darwin": # macOS
165
+ search_paths = [
166
+ "/opt/homebrew/bin", # M1 Mac
167
+ "/usr/local/bin", # Intel Mac
168
+ "/opt/local/bin" # MacPorts
169
+ ]
170
+ logger.debug(f"Checking macOS paths: {search_paths}")
171
+
172
+ elif system == "windows":
173
+ search_paths = [
174
+ os.path.expandvars(r"%ProgramFiles%\Multipass"),
175
+ os.path.expandvars(r"%ProgramFiles(x86)%\Multipass"),
176
+ os.path.expandvars(r"%LocalAppData%\Multipass")
177
+ ]
178
+ logger.debug(f"Checking Windows paths: {search_paths}")
179
+
180
+ else:
181
+ search_paths = ["/usr/local/bin", "/usr/bin"]
182
+ logger.debug(f"Checking default paths: {search_paths}")
183
+
184
+ # Search for multipass binary in OS-specific paths
185
+ for directory in search_paths:
186
+ path = os.path.join(directory, binary_name)
187
+ logger.debug(f"Checking path: {path}")
188
+ if validate_path(path):
189
+ logger.debug(f"Found valid multipass binary at: {path}")
190
+ return path
191
+ else:
192
+ logger.debug(f"No valid multipass binary at: {path}")
193
+
194
+ # OS-specific installation instructions
195
+ if system == "linux":
196
+ raise ValueError(
197
+ "Multipass binary not found. Please install using:\n"
198
+ "sudo snap install multipass\n"
199
+ "Or set GOLEM_PROVIDER_MULTIPASS_BINARY_PATH to your Multipass binary path."
200
+ )
201
+ elif system == "darwin":
202
+ raise ValueError(
203
+ "Multipass binary not found. Please install using:\n"
204
+ "brew install multipass\n"
205
+ "Or set GOLEM_PROVIDER_MULTIPASS_BINARY_PATH to your Multipass binary path."
206
+ )
207
+ elif system == "windows":
208
+ raise ValueError(
209
+ "Multipass binary not found. Please install from:\n"
210
+ "Microsoft Store or https://multipass.run/download/windows\n"
211
+ "Or set GOLEM_PROVIDER_MULTIPASS_BINARY_PATH to your Multipass binary path."
212
+ )
213
+ else:
214
+ raise ValueError(
215
+ "Multipass binary not found. Please install Multipass or set "
216
+ "GOLEM_PROVIDER_MULTIPASS_BINARY_PATH to your Multipass binary path."
217
+ )
218
+
219
+ # Proxy Settings
220
+ PORT_RANGE_START: int = 50800
221
+ PORT_RANGE_END: int = 50900
222
+ PROXY_STATE_DIR: str = ""
223
+ PUBLIC_IP: Optional[str] = None
224
+
225
+ @validator("PROXY_STATE_DIR", pre=True)
226
+ def resolve_proxy_state_dir(cls, v: str) -> str:
227
+ """Resolve proxy state directory path."""
228
+ if not v:
229
+ return str(Path.home() / ".golem" / "provider" / "proxy")
230
+ path = Path(v)
231
+ if not path.is_absolute():
232
+ path = Path.home() / path
233
+ return str(path)
234
+
235
+ @validator("PUBLIC_IP", pre=True)
236
+ def get_public_ip(cls, v: Optional[str]) -> Optional[str]:
237
+ """Get public IP if set to 'auto'."""
238
+ if v == "auto":
239
+ try:
240
+ import requests
241
+ response = requests.get("https://api.ipify.org")
242
+ return response.text.strip()
243
+ except Exception:
244
+ return None
245
+ return v
246
+
247
+ class Config:
248
+ env_prefix = "GOLEM_PROVIDER_"
249
+ case_sensitive = True
250
+
251
+
252
+ # Global settings instance
253
+ settings = Settings()
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "golem-vm-provider"
3
- version = "0.1.12"
3
+ version = "0.1.13"
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()