cua-computer 0.2.2__tar.gz → 0.2.3__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.
- {cua_computer-0.2.2 → cua_computer-0.2.3}/PKG-INFO +1 -1
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/computer.py +29 -10
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/interface/base.py +5 -1
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/interface/factory.py +8 -4
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/interface/linux.py +31 -3
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/interface/macos.py +33 -5
- cua_computer-0.2.3/computer/providers/cloud/provider.py +75 -0
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/providers/factory.py +4 -6
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/providers/lumier/provider.py +7 -1
- {cua_computer-0.2.2 → cua_computer-0.2.3}/pyproject.toml +3 -3
- cua_computer-0.2.2/computer/providers/cloud/provider.py +0 -100
- {cua_computer-0.2.2 → cua_computer-0.2.3}/README.md +0 -0
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/__init__.py +0 -0
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/interface/__init__.py +0 -0
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/interface/models.py +0 -0
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/logger.py +0 -0
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/models.py +0 -0
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/providers/__init__.py +0 -0
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/providers/base.py +0 -0
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/providers/cloud/__init__.py +0 -0
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/providers/lume/__init__.py +0 -0
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/providers/lume/provider.py +0 -0
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/providers/lume_api.py +0 -0
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/providers/lumier/__init__.py +0 -0
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/telemetry.py +0 -0
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/ui/__init__.py +0 -0
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/ui/gradio/__init__.py +0 -0
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/ui/gradio/app.py +0 -0
- {cua_computer-0.2.2 → cua_computer-0.2.3}/computer/utils.py +0 -0
@@ -38,7 +38,8 @@ class Computer:
|
|
38
38
|
noVNC_port: Optional[int] = 8006,
|
39
39
|
host: str = os.environ.get("PYLUME_HOST", "localhost"),
|
40
40
|
storage: Optional[str] = None,
|
41
|
-
ephemeral: bool = False
|
41
|
+
ephemeral: bool = False,
|
42
|
+
api_key: Optional[str] = None
|
42
43
|
):
|
43
44
|
"""Initialize a new Computer instance.
|
44
45
|
|
@@ -77,6 +78,8 @@ class Computer:
|
|
77
78
|
self.os_type = os_type
|
78
79
|
self.provider_type = provider_type
|
79
80
|
self.ephemeral = ephemeral
|
81
|
+
|
82
|
+
self.api_key = api_key
|
80
83
|
|
81
84
|
# The default is currently to use non-ephemeral storage
|
82
85
|
if storage and ephemeral and storage != "ephemeral":
|
@@ -256,9 +259,7 @@ class Computer:
|
|
256
259
|
elif self.provider_type == VMProviderType.CLOUD:
|
257
260
|
self.config.vm_provider = VMProviderFactory.create_provider(
|
258
261
|
self.provider_type,
|
259
|
-
|
260
|
-
host=host,
|
261
|
-
storage=storage,
|
262
|
+
api_key=self.api_key,
|
262
263
|
verbose=verbose,
|
263
264
|
)
|
264
265
|
else:
|
@@ -392,12 +393,25 @@ class Computer:
|
|
392
393
|
self.logger.info(f"Initializing interface for {self.os_type} at {ip_address}")
|
393
394
|
from .interface.base import BaseComputerInterface
|
394
395
|
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
396
|
+
# Pass authentication credentials if using cloud provider
|
397
|
+
if self.provider_type == VMProviderType.CLOUD and self.api_key and self.config.name:
|
398
|
+
self._interface = cast(
|
399
|
+
BaseComputerInterface,
|
400
|
+
InterfaceFactory.create_interface_for_os(
|
401
|
+
os=self.os_type,
|
402
|
+
ip_address=ip_address,
|
403
|
+
api_key=self.api_key,
|
404
|
+
vm_name=self.config.name
|
405
|
+
),
|
406
|
+
)
|
407
|
+
else:
|
408
|
+
self._interface = cast(
|
409
|
+
BaseComputerInterface,
|
410
|
+
InterfaceFactory.create_interface_for_os(
|
411
|
+
os=self.os_type,
|
412
|
+
ip_address=ip_address
|
413
|
+
),
|
414
|
+
)
|
401
415
|
|
402
416
|
# Wait for the WebSocket interface to be ready
|
403
417
|
self.logger.info("Connecting to WebSocket interface...")
|
@@ -492,6 +506,11 @@ class Computer:
|
|
492
506
|
|
493
507
|
# Call the provider's get_ip method which will wait indefinitely
|
494
508
|
storage_param = "ephemeral" if self.ephemeral else self.storage
|
509
|
+
|
510
|
+
# Log the image being used
|
511
|
+
self.logger.info(f"Running VM using image: {self.image}")
|
512
|
+
|
513
|
+
# Call provider.get_ip with explicit image parameter
|
495
514
|
ip = await self.config.vm_provider.get_ip(
|
496
515
|
name=self.config.name,
|
497
516
|
storage=storage_param,
|
@@ -8,17 +8,21 @@ from ..logger import Logger, LogLevel
|
|
8
8
|
class BaseComputerInterface(ABC):
|
9
9
|
"""Base class for computer control interfaces."""
|
10
10
|
|
11
|
-
def __init__(self, ip_address: str, username: str = "lume", password: str = "lume"):
|
11
|
+
def __init__(self, ip_address: str, username: str = "lume", password: str = "lume", api_key: Optional[str] = None, vm_name: Optional[str] = None):
|
12
12
|
"""Initialize interface.
|
13
13
|
|
14
14
|
Args:
|
15
15
|
ip_address: IP address of the computer to control
|
16
16
|
username: Username for authentication
|
17
17
|
password: Password for authentication
|
18
|
+
api_key: Optional API key for cloud authentication
|
19
|
+
vm_name: Optional VM name for cloud authentication
|
18
20
|
"""
|
19
21
|
self.ip_address = ip_address
|
20
22
|
self.username = username
|
21
23
|
self.password = password
|
24
|
+
self.api_key = api_key
|
25
|
+
self.vm_name = vm_name
|
22
26
|
self.logger = Logger("cua.interface", LogLevel.NORMAL)
|
23
27
|
|
24
28
|
@abstractmethod
|
@@ -1,6 +1,6 @@
|
|
1
1
|
"""Factory for creating computer interfaces."""
|
2
2
|
|
3
|
-
from typing import Literal
|
3
|
+
from typing import Literal, Optional
|
4
4
|
from .base import BaseComputerInterface
|
5
5
|
|
6
6
|
class InterfaceFactory:
|
@@ -9,13 +9,17 @@ class InterfaceFactory:
|
|
9
9
|
@staticmethod
|
10
10
|
def create_interface_for_os(
|
11
11
|
os: Literal['macos', 'linux'],
|
12
|
-
ip_address: str
|
12
|
+
ip_address: str,
|
13
|
+
api_key: Optional[str] = None,
|
14
|
+
vm_name: Optional[str] = None
|
13
15
|
) -> BaseComputerInterface:
|
14
16
|
"""Create an interface for the specified OS.
|
15
17
|
|
16
18
|
Args:
|
17
19
|
os: Operating system type ('macos' or 'linux')
|
18
20
|
ip_address: IP address of the computer to control
|
21
|
+
api_key: Optional API key for cloud authentication
|
22
|
+
vm_name: Optional VM name for cloud authentication
|
19
23
|
|
20
24
|
Returns:
|
21
25
|
BaseComputerInterface: The appropriate interface for the OS
|
@@ -28,8 +32,8 @@ class InterfaceFactory:
|
|
28
32
|
from .linux import LinuxComputerInterface
|
29
33
|
|
30
34
|
if os == 'macos':
|
31
|
-
return MacOSComputerInterface(ip_address)
|
35
|
+
return MacOSComputerInterface(ip_address, api_key=api_key, vm_name=vm_name)
|
32
36
|
elif os == 'linux':
|
33
|
-
return LinuxComputerInterface(ip_address)
|
37
|
+
return LinuxComputerInterface(ip_address, api_key=api_key, vm_name=vm_name)
|
34
38
|
else:
|
35
39
|
raise ValueError(f"Unsupported OS type: {os}")
|
@@ -15,8 +15,8 @@ from .models import Key, KeyType
|
|
15
15
|
class LinuxComputerInterface(BaseComputerInterface):
|
16
16
|
"""Interface for Linux."""
|
17
17
|
|
18
|
-
def __init__(self, ip_address: str, username: str = "lume", password: str = "lume"):
|
19
|
-
super().__init__(ip_address, username, password)
|
18
|
+
def __init__(self, ip_address: str, username: str = "lume", password: str = "lume", api_key: Optional[str] = None, vm_name: Optional[str] = None):
|
19
|
+
super().__init__(ip_address, username, password, api_key, vm_name)
|
20
20
|
self._ws = None
|
21
21
|
self._reconnect_task = None
|
22
22
|
self._closed = False
|
@@ -37,7 +37,9 @@ class LinuxComputerInterface(BaseComputerInterface):
|
|
37
37
|
Returns:
|
38
38
|
WebSocket URI for the Computer API Server
|
39
39
|
"""
|
40
|
-
|
40
|
+
protocol = "wss" if self.api_key else "ws"
|
41
|
+
port = "8443" if self.api_key else "8000"
|
42
|
+
return f"{protocol}://{self.ip_address}:{port}/ws"
|
41
43
|
|
42
44
|
async def _keep_alive(self):
|
43
45
|
"""Keep the WebSocket connection alive with automatic reconnection."""
|
@@ -86,6 +88,32 @@ class LinuxComputerInterface(BaseComputerInterface):
|
|
86
88
|
timeout=30,
|
87
89
|
)
|
88
90
|
self.logger.info("WebSocket connection established")
|
91
|
+
|
92
|
+
# If api_key and vm_name are provided, perform authentication handshake
|
93
|
+
if self.api_key and self.vm_name:
|
94
|
+
self.logger.info("Performing authentication handshake...")
|
95
|
+
auth_message = {
|
96
|
+
"command": "authenticate",
|
97
|
+
"params": {
|
98
|
+
"api_key": self.api_key,
|
99
|
+
"container_name": self.vm_name
|
100
|
+
}
|
101
|
+
}
|
102
|
+
await self._ws.send(json.dumps(auth_message))
|
103
|
+
|
104
|
+
# Wait for authentication response
|
105
|
+
auth_response = await asyncio.wait_for(self._ws.recv(), timeout=10)
|
106
|
+
auth_result = json.loads(auth_response)
|
107
|
+
|
108
|
+
if not auth_result.get("success"):
|
109
|
+
error_msg = auth_result.get("error", "Authentication failed")
|
110
|
+
self.logger.error(f"Authentication failed: {error_msg}")
|
111
|
+
await self._ws.close()
|
112
|
+
self._ws = None
|
113
|
+
raise ConnectionError(f"Authentication failed: {error_msg}")
|
114
|
+
|
115
|
+
self.logger.info("Authentication successful")
|
116
|
+
|
89
117
|
self._reconnect_delay = 1 # Reset reconnect delay on successful connection
|
90
118
|
self._last_ping = time.time()
|
91
119
|
retry_count = 0 # Reset retry count on successful connection
|
@@ -13,10 +13,10 @@ from .models import Key, KeyType
|
|
13
13
|
|
14
14
|
|
15
15
|
class MacOSComputerInterface(BaseComputerInterface):
|
16
|
-
"""Interface for
|
16
|
+
"""Interface for macOS."""
|
17
17
|
|
18
|
-
def __init__(self, ip_address: str, username: str = "lume", password: str = "lume"):
|
19
|
-
super().__init__(ip_address, username, password)
|
18
|
+
def __init__(self, ip_address: str, username: str = "lume", password: str = "lume", api_key: Optional[str] = None, vm_name: Optional[str] = None):
|
19
|
+
super().__init__(ip_address, username, password, api_key, vm_name)
|
20
20
|
self._ws = None
|
21
21
|
self._reconnect_task = None
|
22
22
|
self._closed = False
|
@@ -27,7 +27,7 @@ class MacOSComputerInterface(BaseComputerInterface):
|
|
27
27
|
self._max_reconnect_delay = 30 # Maximum delay between reconnection attempts
|
28
28
|
self._log_connection_attempts = True # Flag to control connection attempt logging
|
29
29
|
|
30
|
-
# Set logger name for
|
30
|
+
# Set logger name for macOS interface
|
31
31
|
self.logger = Logger("cua.interface.macos", LogLevel.NORMAL)
|
32
32
|
|
33
33
|
@property
|
@@ -37,7 +37,9 @@ class MacOSComputerInterface(BaseComputerInterface):
|
|
37
37
|
Returns:
|
38
38
|
WebSocket URI for the Computer API Server
|
39
39
|
"""
|
40
|
-
|
40
|
+
protocol = "wss" if self.api_key else "ws"
|
41
|
+
port = "8443" if self.api_key else "8000"
|
42
|
+
return f"{protocol}://{self.ip_address}:{port}/ws"
|
41
43
|
|
42
44
|
async def _keep_alive(self):
|
43
45
|
"""Keep the WebSocket connection alive with automatic reconnection."""
|
@@ -86,6 +88,32 @@ class MacOSComputerInterface(BaseComputerInterface):
|
|
86
88
|
timeout=30,
|
87
89
|
)
|
88
90
|
self.logger.info("WebSocket connection established")
|
91
|
+
|
92
|
+
# If api_key and vm_name are provided, perform authentication handshake
|
93
|
+
if self.api_key and self.vm_name:
|
94
|
+
self.logger.info("Performing authentication handshake...")
|
95
|
+
auth_message = {
|
96
|
+
"command": "authenticate",
|
97
|
+
"params": {
|
98
|
+
"api_key": self.api_key,
|
99
|
+
"container_name": self.vm_name
|
100
|
+
}
|
101
|
+
}
|
102
|
+
await self._ws.send(json.dumps(auth_message))
|
103
|
+
|
104
|
+
# Wait for authentication response
|
105
|
+
auth_response = await asyncio.wait_for(self._ws.recv(), timeout=10)
|
106
|
+
auth_result = json.loads(auth_response)
|
107
|
+
|
108
|
+
if not auth_result.get("success"):
|
109
|
+
error_msg = auth_result.get("error", "Authentication failed")
|
110
|
+
self.logger.error(f"Authentication failed: {error_msg}")
|
111
|
+
await self._ws.close()
|
112
|
+
self._ws = None
|
113
|
+
raise ConnectionError(f"Authentication failed: {error_msg}")
|
114
|
+
|
115
|
+
self.logger.info("Authentication successful")
|
116
|
+
|
89
117
|
self._reconnect_delay = 1 # Reset reconnect delay on successful connection
|
90
118
|
self._last_ping = time.time()
|
91
119
|
retry_count = 0 # Reset retry count on successful connection
|
@@ -0,0 +1,75 @@
|
|
1
|
+
"""Cloud VM provider implementation.
|
2
|
+
|
3
|
+
This module contains a stub implementation for a future cloud VM provider.
|
4
|
+
"""
|
5
|
+
|
6
|
+
import logging
|
7
|
+
from typing import Dict, List, Optional, Any
|
8
|
+
|
9
|
+
from ..base import BaseVMProvider, VMProviderType
|
10
|
+
|
11
|
+
# Setup logging
|
12
|
+
logger = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
import asyncio
|
15
|
+
import aiohttp
|
16
|
+
from urllib.parse import urlparse
|
17
|
+
|
18
|
+
class CloudProvider(BaseVMProvider):
|
19
|
+
"""Cloud VM Provider implementation."""
|
20
|
+
def __init__(
|
21
|
+
self,
|
22
|
+
api_key: str,
|
23
|
+
verbose: bool = False,
|
24
|
+
**kwargs,
|
25
|
+
):
|
26
|
+
"""
|
27
|
+
Args:
|
28
|
+
api_key: API key for authentication
|
29
|
+
name: Name of the VM
|
30
|
+
verbose: Enable verbose logging
|
31
|
+
"""
|
32
|
+
assert api_key, "api_key required for CloudProvider"
|
33
|
+
self.api_key = api_key
|
34
|
+
self.verbose = verbose
|
35
|
+
|
36
|
+
@property
|
37
|
+
def provider_type(self) -> VMProviderType:
|
38
|
+
return VMProviderType.CLOUD
|
39
|
+
|
40
|
+
async def __aenter__(self):
|
41
|
+
return self
|
42
|
+
|
43
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
44
|
+
pass
|
45
|
+
|
46
|
+
async def get_vm(self, name: str, storage: Optional[str] = None) -> Dict[str, Any]:
|
47
|
+
"""Get VM VNC URL by name using the cloud API."""
|
48
|
+
return {"name": name, "hostname": f"{name}.containers.cloud.trycua.com"}
|
49
|
+
|
50
|
+
async def list_vms(self) -> List[Dict[str, Any]]:
|
51
|
+
logger.warning("CloudProvider.list_vms is not implemented")
|
52
|
+
return []
|
53
|
+
|
54
|
+
async def run_vm(self, image: str, name: str, run_opts: Dict[str, Any], storage: Optional[str] = None) -> Dict[str, Any]:
|
55
|
+
logger.warning("CloudProvider.run_vm is not implemented")
|
56
|
+
return {"name": name, "status": "unavailable", "message": "CloudProvider is not implemented"}
|
57
|
+
|
58
|
+
async def stop_vm(self, name: str, storage: Optional[str] = None) -> Dict[str, Any]:
|
59
|
+
logger.warning("CloudProvider.stop_vm is not implemented")
|
60
|
+
return {"name": name, "status": "stopped", "message": "CloudProvider is not implemented"}
|
61
|
+
|
62
|
+
async def update_vm(self, name: str, update_opts: Dict[str, Any], storage: Optional[str] = None) -> Dict[str, Any]:
|
63
|
+
logger.warning("CloudProvider.update_vm is not implemented")
|
64
|
+
return {"name": name, "status": "unchanged", "message": "CloudProvider is not implemented"}
|
65
|
+
|
66
|
+
async def get_ip(self, name: Optional[str] = None, storage: Optional[str] = None, retry_delay: int = 2) -> str:
|
67
|
+
"""
|
68
|
+
Return the VM's IP address as '{container_name}.containers.cloud.trycua.com'.
|
69
|
+
Uses the provided 'name' argument (the VM name requested by the caller),
|
70
|
+
falling back to self.name only if 'name' is None.
|
71
|
+
Retries up to 3 times with retry_delay seconds if hostname is not available.
|
72
|
+
"""
|
73
|
+
if name is None:
|
74
|
+
raise ValueError("VM name is required for CloudProvider.get_ip")
|
75
|
+
return f"{name}.containers.cloud.trycua.com"
|
@@ -22,7 +22,8 @@ class VMProviderFactory:
|
|
22
22
|
image: Optional[str] = None,
|
23
23
|
verbose: bool = False,
|
24
24
|
ephemeral: bool = False,
|
25
|
-
noVNC_port: Optional[int] = None
|
25
|
+
noVNC_port: Optional[int] = None,
|
26
|
+
**kwargs,
|
26
27
|
) -> BaseVMProvider:
|
27
28
|
"""Create a VM provider of the specified type.
|
28
29
|
|
@@ -101,12 +102,9 @@ class VMProviderFactory:
|
|
101
102
|
elif provider_type == VMProviderType.CLOUD:
|
102
103
|
try:
|
103
104
|
from .cloud import CloudProvider
|
104
|
-
# Return the stub implementation of CloudProvider
|
105
105
|
return CloudProvider(
|
106
|
-
|
107
|
-
|
108
|
-
storage=storage,
|
109
|
-
verbose=verbose
|
106
|
+
verbose=verbose,
|
107
|
+
**kwargs,
|
110
108
|
)
|
111
109
|
except ImportError as e:
|
112
110
|
logger.error(f"Failed to import CloudProvider: {e}")
|
@@ -344,9 +344,15 @@ class LumierProvider(BaseVMProvider):
|
|
344
344
|
# Use the VM image passed from the Computer class
|
345
345
|
print(f"Using VM image: {self.image}")
|
346
346
|
|
347
|
+
# If ghcr.io is in the image, use the full image name
|
348
|
+
if "ghcr.io" in self.image:
|
349
|
+
vm_image = self.image
|
350
|
+
else:
|
351
|
+
vm_image = f"ghcr.io/trycua/{self.image}"
|
352
|
+
|
347
353
|
cmd.extend([
|
348
354
|
"-e", f"VM_NAME={self.container_name}",
|
349
|
-
"-e", f"VERSION=
|
355
|
+
"-e", f"VERSION={vm_image}",
|
350
356
|
"-e", f"CPU_CORES={run_opts.get('cpu', '4')}",
|
351
357
|
"-e", f"RAM_SIZE={memory_mb}",
|
352
358
|
])
|
@@ -6,7 +6,7 @@ build-backend = "pdm.backend"
|
|
6
6
|
|
7
7
|
[project]
|
8
8
|
name = "cua-computer"
|
9
|
-
version = "0.2.
|
9
|
+
version = "0.2.3"
|
10
10
|
description = "Computer-Use Interface (CUI) framework powering Cua"
|
11
11
|
readme = "README.md"
|
12
12
|
authors = [
|
@@ -57,7 +57,7 @@ target-version = [
|
|
57
57
|
|
58
58
|
[tool.ruff]
|
59
59
|
line-length = 100
|
60
|
-
target-version = "0.2.
|
60
|
+
target-version = "0.2.3"
|
61
61
|
select = [
|
62
62
|
"E",
|
63
63
|
"F",
|
@@ -71,7 +71,7 @@ docstring-code-format = true
|
|
71
71
|
|
72
72
|
[tool.mypy]
|
73
73
|
strict = true
|
74
|
-
python_version = "0.2.
|
74
|
+
python_version = "0.2.3"
|
75
75
|
ignore_missing_imports = true
|
76
76
|
disallow_untyped_defs = true
|
77
77
|
check_untyped_defs = true
|
@@ -1,100 +0,0 @@
|
|
1
|
-
"""Cloud VM provider implementation.
|
2
|
-
|
3
|
-
This module contains a stub implementation for a future cloud VM provider.
|
4
|
-
"""
|
5
|
-
|
6
|
-
import logging
|
7
|
-
from typing import Dict, List, Optional, Any
|
8
|
-
|
9
|
-
from ..base import BaseVMProvider, VMProviderType
|
10
|
-
|
11
|
-
# Setup logging
|
12
|
-
logger = logging.getLogger(__name__)
|
13
|
-
|
14
|
-
class CloudProvider(BaseVMProvider):
|
15
|
-
"""Cloud VM Provider stub implementation.
|
16
|
-
|
17
|
-
This is a placeholder for a future cloud VM provider implementation.
|
18
|
-
"""
|
19
|
-
|
20
|
-
def __init__(
|
21
|
-
self,
|
22
|
-
host: str = "localhost",
|
23
|
-
port: int = 7777,
|
24
|
-
storage: Optional[str] = None,
|
25
|
-
verbose: bool = False,
|
26
|
-
):
|
27
|
-
"""Initialize the Cloud provider.
|
28
|
-
|
29
|
-
Args:
|
30
|
-
host: Host to use for API connections (default: localhost)
|
31
|
-
port: Port for the API server (default: 7777)
|
32
|
-
storage: Path to store VM data
|
33
|
-
verbose: Enable verbose logging
|
34
|
-
"""
|
35
|
-
self.host = host
|
36
|
-
self.port = port
|
37
|
-
self.storage = storage
|
38
|
-
self.verbose = verbose
|
39
|
-
|
40
|
-
logger.warning("CloudProvider is not yet implemented")
|
41
|
-
|
42
|
-
@property
|
43
|
-
def provider_type(self) -> VMProviderType:
|
44
|
-
"""Get the provider type."""
|
45
|
-
return VMProviderType.CLOUD
|
46
|
-
|
47
|
-
async def __aenter__(self):
|
48
|
-
"""Enter async context manager."""
|
49
|
-
logger.debug("Entering CloudProvider context")
|
50
|
-
return self
|
51
|
-
|
52
|
-
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
53
|
-
"""Exit async context manager."""
|
54
|
-
logger.debug("Exiting CloudProvider context")
|
55
|
-
|
56
|
-
async def get_vm(self, name: str, storage: Optional[str] = None) -> Dict[str, Any]:
|
57
|
-
"""Get VM information by name."""
|
58
|
-
logger.warning("CloudProvider.get_vm is not implemented")
|
59
|
-
return {
|
60
|
-
"name": name,
|
61
|
-
"status": "unavailable",
|
62
|
-
"message": "CloudProvider is not implemented"
|
63
|
-
}
|
64
|
-
|
65
|
-
async def list_vms(self) -> List[Dict[str, Any]]:
|
66
|
-
"""List all available VMs."""
|
67
|
-
logger.warning("CloudProvider.list_vms is not implemented")
|
68
|
-
return []
|
69
|
-
|
70
|
-
async def run_vm(self, image: str, name: str, run_opts: Dict[str, Any], storage: Optional[str] = None) -> Dict[str, Any]:
|
71
|
-
"""Run a VM with the given options."""
|
72
|
-
logger.warning("CloudProvider.run_vm is not implemented")
|
73
|
-
return {
|
74
|
-
"name": name,
|
75
|
-
"status": "unavailable",
|
76
|
-
"message": "CloudProvider is not implemented"
|
77
|
-
}
|
78
|
-
|
79
|
-
async def stop_vm(self, name: str, storage: Optional[str] = None) -> Dict[str, Any]:
|
80
|
-
"""Stop a running VM."""
|
81
|
-
logger.warning("CloudProvider.stop_vm is not implemented")
|
82
|
-
return {
|
83
|
-
"name": name,
|
84
|
-
"status": "stopped",
|
85
|
-
"message": "CloudProvider is not implemented"
|
86
|
-
}
|
87
|
-
|
88
|
-
async def update_vm(self, name: str, update_opts: Dict[str, Any], storage: Optional[str] = None) -> Dict[str, Any]:
|
89
|
-
"""Update VM configuration."""
|
90
|
-
logger.warning("CloudProvider.update_vm is not implemented")
|
91
|
-
return {
|
92
|
-
"name": name,
|
93
|
-
"status": "unchanged",
|
94
|
-
"message": "CloudProvider is not implemented"
|
95
|
-
}
|
96
|
-
|
97
|
-
async def get_ip(self, name: str, storage: Optional[str] = None, retry_delay: int = 2) -> str:
|
98
|
-
"""Get the IP address of a VM."""
|
99
|
-
logger.warning("CloudProvider.get_ip is not implemented")
|
100
|
-
raise NotImplementedError("CloudProvider.get_ip is not implemented")
|
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
|