golem-vm-provider 0.1.2__py3-none-any.whl → 0.1.7__py3-none-any.whl
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.2.dist-info → golem_vm_provider-0.1.7.dist-info}/METADATA +3 -3
- {golem_vm_provider-0.1.2.dist-info → golem_vm_provider-0.1.7.dist-info}/RECORD +8 -8
- {golem_vm_provider-0.1.2.dist-info → golem_vm_provider-0.1.7.dist-info}/WHEEL +1 -1
- provider/api/routes.py +16 -23
- provider/vm/multipass.py +1 -1
- provider/vm/port_manager.py +36 -5
- provider/vm/proxy_manager.py +2 -2
- {golem_vm_provider-0.1.2.dist-info → golem_vm_provider-0.1.7.dist-info}/entry_points.txt +0 -0
@@ -1,8 +1,7 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.3
|
2
2
|
Name: golem-vm-provider
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.7
|
4
4
|
Summary: VM on Golem Provider Node - Run your own provider node to offer VMs on the Golem Network
|
5
|
-
Home-page: https://github.com/cryptobench/vm-on-golem
|
6
5
|
Keywords: golem,vm,provider,cloud,decentralized
|
7
6
|
Author: Phillip Jensen
|
8
7
|
Author-email: phillip+vm-on-golem@golemgrid.com
|
@@ -33,6 +32,7 @@ Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
33
32
|
Requires-Dist: rich (>=13.7.0,<14.0.0)
|
34
33
|
Requires-Dist: setuptools (>=69.0.3,<70.0.0)
|
35
34
|
Requires-Dist: uvicorn (>=0.15.0,<0.16.0)
|
35
|
+
Project-URL: Homepage, https://github.com/cryptobench/vm-on-golem
|
36
36
|
Project-URL: Repository, https://github.com/cryptobench/vm-on-golem
|
37
37
|
Description-Content-Type: text/markdown
|
38
38
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
provider/__init__.py,sha256=HO1fkPpZqPO3z8O8-eVIyx8xXSMIVuTR_b1YF0RtXOg,45
|
2
2
|
provider/api/__init__.py,sha256=ssX1ugDqEPt8Fn04IymgmG-Ev8PiXLsCSaiZVvHQnec,344
|
3
3
|
provider/api/models.py,sha256=JOzoNf1oE5N97UqTN5xuIrTkqn2tCHqPDaIzGA3jUyo,3513
|
4
|
-
provider/api/routes.py,sha256=
|
4
|
+
provider/api/routes.py,sha256=P27RQvNqFWn6PacRwr1PaVz-yv5KAWsp9KeORejkXSI,6452
|
5
5
|
provider/config.py,sha256=Q6xmU6cPRBUYkVusv-Fc1U4ekr4OYLXfOfEtaWL9miM,5000
|
6
6
|
provider/discovery/__init__.py,sha256=VR3NRoQtZRH5Vs8FG7jnGLR7p7wn7XeZdLaBb3t8e1g,123
|
7
7
|
provider/discovery/advertiser.py,sha256=yv7RbRf1K43qOLAEa2Olj9hhN8etl2qsBuoHok0xoVs,6784
|
@@ -16,11 +16,11 @@ provider/utils/retry.py,sha256=ekP2ucaSJNN-lBcrIvyHa4QYPKNITMl1a5V1X6BBvsw,1560
|
|
16
16
|
provider/vm/__init__.py,sha256=JGs50tUmzOR1rQ_w4fMY_3XWylmiA1G7KKWZkVw51mY,501
|
17
17
|
provider/vm/cloud_init.py,sha256=o2CWLjl1ZN9fSEFHAWO-glh7BW-DxAMSe0MbqhzKNTg,1709
|
18
18
|
provider/vm/models.py,sha256=zkfvP5Z50SPDNajwZTt9NTDIMRQIsZLvSOsuirHEcJM,6256
|
19
|
-
provider/vm/multipass.py,sha256=
|
19
|
+
provider/vm/multipass.py,sha256=RLUqCeoYz4PG8RL7dBu_TzjNEAmgIz9NonBtSuYc4kw,16431
|
20
20
|
provider/vm/name_mapper.py,sha256=MrshNeJ4Dw-WBsyiIVcn9N5xyOxaBKX4Yqhyh_m5IFg,4103
|
21
|
-
provider/vm/port_manager.py,sha256=
|
22
|
-
provider/vm/proxy_manager.py,sha256=
|
23
|
-
golem_vm_provider-0.1.
|
24
|
-
golem_vm_provider-0.1.
|
25
|
-
golem_vm_provider-0.1.
|
26
|
-
golem_vm_provider-0.1.
|
21
|
+
provider/vm/port_manager.py,sha256=147phrKFXiv1jpJfqpwHsuHqYTj4t2kP3_UV0lV8Lr0,10063
|
22
|
+
provider/vm/proxy_manager.py,sha256=cu0FPPbeCc3CR6NRE_CnLjiRg7xVdSFUylVUOL1g1sI,10154
|
23
|
+
golem_vm_provider-0.1.7.dist-info/METADATA,sha256=LsfKA092K5_R6bZA25J5E3S3JLzEK-WTY2IJSBv7k4I,10461
|
24
|
+
golem_vm_provider-0.1.7.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
25
|
+
golem_vm_provider-0.1.7.dist-info/entry_points.txt,sha256=E4rCWo_Do_2zCG_GewNuftfVlHF_8b_OvioZre0dfeA,54
|
26
|
+
golem_vm_provider-0.1.7.dist-info/RECORD,,
|
provider/api/routes.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import json
|
2
2
|
from typing import List
|
3
3
|
from pathlib import Path
|
4
|
-
from fastapi import APIRouter, HTTPException
|
4
|
+
from fastapi import APIRouter, HTTPException, Request
|
5
5
|
|
6
6
|
from ..config import settings
|
7
7
|
from ..utils.logging import setup_logger, PROCESS, SUCCESS
|
@@ -9,19 +9,12 @@ from ..utils.ascii_art import vm_creation_animation, vm_status_change
|
|
9
9
|
from ..vm.models import VMInfo, VMStatus, VMAccessInfo, VMConfig, VMResources
|
10
10
|
from .models import CreateVMRequest
|
11
11
|
from ..vm.multipass import MultipassProvider, MultipassError
|
12
|
-
from ..discovery.resource_tracker import ResourceTracker
|
13
|
-
from ..vm.port_manager import PortManager
|
14
12
|
|
15
13
|
logger = setup_logger(__name__)
|
16
14
|
router = APIRouter()
|
17
15
|
|
18
|
-
# Initialize components
|
19
|
-
resource_tracker = ResourceTracker()
|
20
|
-
port_manager = PortManager()
|
21
|
-
provider = MultipassProvider(resource_tracker, port_manager)
|
22
|
-
|
23
16
|
@router.post("/vms", response_model=VMInfo)
|
24
|
-
async def create_vm(request: CreateVMRequest) -> VMInfo:
|
17
|
+
async def create_vm(request: CreateVMRequest, req: Request) -> VMInfo:
|
25
18
|
"""Create a new VM."""
|
26
19
|
try:
|
27
20
|
logger.info(f"📥 Received VM creation request for '{request.name}'")
|
@@ -47,7 +40,7 @@ async def create_vm(request: CreateVMRequest) -> VMInfo:
|
|
47
40
|
|
48
41
|
# Check and allocate resources
|
49
42
|
logger.process("🔄 Allocating resources")
|
50
|
-
if not await resource_tracker.allocate(resources):
|
43
|
+
if not await req.app.state.resource_tracker.allocate(resources):
|
51
44
|
logger.error("❌ Insufficient resources available")
|
52
45
|
raise HTTPException(400, "Insufficient resources available on provider")
|
53
46
|
|
@@ -62,7 +55,7 @@ async def create_vm(request: CreateVMRequest) -> VMInfo:
|
|
62
55
|
|
63
56
|
# Create VM
|
64
57
|
logger.process(f"🔄 Creating VM with config: {config}")
|
65
|
-
vm_info = await provider.create_vm(config)
|
58
|
+
vm_info = await req.app.state.provider.create_vm(config)
|
66
59
|
|
67
60
|
# Show success message
|
68
61
|
await vm_creation_animation(request.name)
|
@@ -70,7 +63,7 @@ async def create_vm(request: CreateVMRequest) -> VMInfo:
|
|
70
63
|
except Exception as e:
|
71
64
|
# If VM creation fails, deallocate resources
|
72
65
|
logger.warning("⚠️ VM creation failed, deallocating resources")
|
73
|
-
await resource_tracker.deallocate(resources)
|
66
|
+
await req.app.state.resource_tracker.deallocate(resources)
|
74
67
|
raise
|
75
68
|
|
76
69
|
except MultipassError as e:
|
@@ -78,13 +71,13 @@ async def create_vm(request: CreateVMRequest) -> VMInfo:
|
|
78
71
|
raise HTTPException(500, str(e))
|
79
72
|
|
80
73
|
@router.get("/vms", response_model=List[VMInfo])
|
81
|
-
async def list_vms() -> List[VMInfo]:
|
74
|
+
async def list_vms(req: Request) -> List[VMInfo]:
|
82
75
|
"""List all VMs."""
|
83
76
|
try:
|
84
77
|
logger.info("📋 Listing all VMs")
|
85
78
|
vms = []
|
86
|
-
for vm_id in resource_tracker.get_allocated_vms():
|
87
|
-
vm_info = await provider.get_vm_status(vm_id)
|
79
|
+
for vm_id in req.app.state.resource_tracker.get_allocated_vms():
|
80
|
+
vm_info = await req.app.state.provider.get_vm_status(vm_id)
|
88
81
|
vms.append(vm_info)
|
89
82
|
return vms
|
90
83
|
except MultipassError as e:
|
@@ -92,11 +85,11 @@ async def list_vms() -> List[VMInfo]:
|
|
92
85
|
raise HTTPException(500, str(e))
|
93
86
|
|
94
87
|
@router.get("/vms/{requestor_name}", response_model=VMInfo)
|
95
|
-
async def get_vm_status(requestor_name: str) -> VMInfo:
|
88
|
+
async def get_vm_status(requestor_name: str, req: Request) -> VMInfo:
|
96
89
|
"""Get VM status."""
|
97
90
|
try:
|
98
91
|
logger.info(f"🔍 Getting status for VM '{requestor_name}'")
|
99
|
-
status = await provider.get_vm_status(requestor_name)
|
92
|
+
status = await req.app.state.provider.get_vm_status(requestor_name)
|
100
93
|
vm_status_change(requestor_name, status.status.value)
|
101
94
|
return status
|
102
95
|
except MultipassError as e:
|
@@ -104,16 +97,16 @@ async def get_vm_status(requestor_name: str) -> VMInfo:
|
|
104
97
|
raise HTTPException(500, str(e))
|
105
98
|
|
106
99
|
@router.get("/vms/{requestor_name}/access", response_model=VMAccessInfo)
|
107
|
-
async def get_vm_access(requestor_name: str) -> VMAccessInfo:
|
100
|
+
async def get_vm_access(requestor_name: str, req: Request) -> VMAccessInfo:
|
108
101
|
"""Get VM access information."""
|
109
102
|
try:
|
110
103
|
# Get VM info
|
111
|
-
vm = await provider.get_vm_status(requestor_name)
|
104
|
+
vm = await req.app.state.provider.get_vm_status(requestor_name)
|
112
105
|
if not vm:
|
113
106
|
raise HTTPException(404, "VM not found")
|
114
107
|
|
115
108
|
# Get multipass name from mapper
|
116
|
-
multipass_name = await provider.name_mapper.get_multipass_name(requestor_name)
|
109
|
+
multipass_name = await req.app.state.provider.name_mapper.get_multipass_name(requestor_name)
|
117
110
|
if not multipass_name:
|
118
111
|
raise HTTPException(404, "VM mapping not found")
|
119
112
|
|
@@ -130,7 +123,7 @@ async def get_vm_access(requestor_name: str) -> VMAccessInfo:
|
|
130
123
|
raise HTTPException(500, str(e))
|
131
124
|
|
132
125
|
@router.delete("/vms/{requestor_name}")
|
133
|
-
async def delete_vm(requestor_name: str) -> None:
|
126
|
+
async def delete_vm(requestor_name: str, req: Request) -> None:
|
134
127
|
"""Delete a VM.
|
135
128
|
|
136
129
|
Args:
|
@@ -140,14 +133,14 @@ async def delete_vm(requestor_name: str) -> None:
|
|
140
133
|
logger.process(f"🗑️ Deleting VM '{requestor_name}'")
|
141
134
|
|
142
135
|
# Get multipass name from mapper
|
143
|
-
multipass_name = await provider.name_mapper.get_multipass_name(requestor_name)
|
136
|
+
multipass_name = await req.app.state.provider.name_mapper.get_multipass_name(requestor_name)
|
144
137
|
if not multipass_name:
|
145
138
|
logger.warning(f"No multipass name found for VM '{requestor_name}' (may have been already deleted)")
|
146
139
|
return
|
147
140
|
|
148
141
|
try:
|
149
142
|
vm_status_change(requestor_name, "STOPPING", "Cleanup in progress")
|
150
|
-
await provider.delete_vm(requestor_name)
|
143
|
+
await req.app.state.provider.delete_vm(requestor_name)
|
151
144
|
vm_status_change(requestor_name, "TERMINATED", "Cleanup complete")
|
152
145
|
logger.success(f"✨ Successfully deleted VM '{requestor_name}'")
|
153
146
|
except MultipassError as e:
|
provider/vm/multipass.py
CHANGED
@@ -37,7 +37,7 @@ class MultipassProvider(VMProvider):
|
|
37
37
|
self.vm_data_dir.mkdir(parents=True, exist_ok=True)
|
38
38
|
|
39
39
|
# Initialize managers
|
40
|
-
self.proxy_manager = PythonProxyManager()
|
40
|
+
self.proxy_manager = PythonProxyManager(port_manager=port_manager)
|
41
41
|
self.name_mapper = VMNameMapper(self.vm_data_dir / "vm_names.json")
|
42
42
|
|
43
43
|
def _verify_installation(self) -> None:
|
provider/vm/port_manager.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import os
|
2
2
|
import json
|
3
|
+
import socket
|
3
4
|
import logging
|
4
5
|
import asyncio
|
5
6
|
from pathlib import Path
|
@@ -172,7 +173,23 @@ class PortManager:
|
|
172
173
|
if vm_id in self._used_ports:
|
173
174
|
port = self._used_ports[vm_id]
|
174
175
|
if port in self.verified_ports:
|
175
|
-
|
176
|
+
# Quick check if port is still available
|
177
|
+
try:
|
178
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
179
|
+
sock.settimeout(1)
|
180
|
+
result = sock.connect_ex(('127.0.0.1', port))
|
181
|
+
sock.close()
|
182
|
+
|
183
|
+
if result != 0: # Port is available
|
184
|
+
return port
|
185
|
+
else:
|
186
|
+
# Port is in use, remove from verified ports
|
187
|
+
self.verified_ports.remove(port)
|
188
|
+
self._used_ports.pop(vm_id)
|
189
|
+
except Exception as e:
|
190
|
+
logger.debug(f"Failed to check port {port}: {e}")
|
191
|
+
# Keep the port if check fails, let proxy setup handle any issues
|
192
|
+
return port
|
176
193
|
else:
|
177
194
|
# Previously allocated port is no longer verified
|
178
195
|
self._used_ports.pop(vm_id)
|
@@ -182,10 +199,24 @@ class PortManager:
|
|
182
199
|
# Find first available verified port
|
183
200
|
for port in sorted(self.verified_ports):
|
184
201
|
if port not in used_ports:
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
202
|
+
# Quick check if port is actually available
|
203
|
+
try:
|
204
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
205
|
+
sock.settimeout(1)
|
206
|
+
result = sock.connect_ex(('127.0.0.1', port))
|
207
|
+
sock.close()
|
208
|
+
|
209
|
+
if result != 0: # Port is available
|
210
|
+
self._used_ports[vm_id] = port
|
211
|
+
self._save_state()
|
212
|
+
logger.info(f"Allocated verified port {port} for VM {vm_id}")
|
213
|
+
return port
|
214
|
+
else:
|
215
|
+
# Port is in use, remove from verified ports
|
216
|
+
self.verified_ports.remove(port)
|
217
|
+
except Exception as e:
|
218
|
+
logger.debug(f"Failed to check port {port}: {e}")
|
219
|
+
continue
|
189
220
|
|
190
221
|
logger.error("No verified ports available for allocation")
|
191
222
|
return None
|
provider/vm/proxy_manager.py
CHANGED
@@ -146,7 +146,7 @@ class PythonProxyManager:
|
|
146
146
|
|
147
147
|
def __init__(
|
148
148
|
self,
|
149
|
-
port_manager:
|
149
|
+
port_manager: PortManager,
|
150
150
|
state_file: Optional[str] = None
|
151
151
|
):
|
152
152
|
"""Initialize the proxy manager.
|
@@ -155,7 +155,7 @@ class PythonProxyManager:
|
|
155
155
|
port_manager: Port allocation manager
|
156
156
|
state_file: Path to persist proxy state
|
157
157
|
"""
|
158
|
-
self.port_manager = port_manager
|
158
|
+
self.port_manager = port_manager
|
159
159
|
self.state_file = state_file or os.path.expanduser("~/.golem/provider/proxy_state.json")
|
160
160
|
self._proxies: Dict[str, ProxyServer] = {} # vm_id -> ProxyServer
|
161
161
|
self._load_state()
|
File without changes
|