lybic-guiagents 0.1.0__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.
Potentially problematic release.
This version of lybic-guiagents might be problematic. Click here for more details.
- desktop_env/__init__.py +1 -0
- desktop_env/actions.py +203 -0
- desktop_env/controllers/__init__.py +0 -0
- desktop_env/controllers/python.py +471 -0
- desktop_env/controllers/setup.py +882 -0
- desktop_env/desktop_env.py +509 -0
- desktop_env/evaluators/__init__.py +5 -0
- desktop_env/evaluators/getters/__init__.py +41 -0
- desktop_env/evaluators/getters/calc.py +15 -0
- desktop_env/evaluators/getters/chrome.py +1774 -0
- desktop_env/evaluators/getters/file.py +154 -0
- desktop_env/evaluators/getters/general.py +42 -0
- desktop_env/evaluators/getters/gimp.py +38 -0
- desktop_env/evaluators/getters/impress.py +126 -0
- desktop_env/evaluators/getters/info.py +24 -0
- desktop_env/evaluators/getters/misc.py +406 -0
- desktop_env/evaluators/getters/replay.py +20 -0
- desktop_env/evaluators/getters/vlc.py +86 -0
- desktop_env/evaluators/getters/vscode.py +35 -0
- desktop_env/evaluators/metrics/__init__.py +160 -0
- desktop_env/evaluators/metrics/basic_os.py +68 -0
- desktop_env/evaluators/metrics/chrome.py +493 -0
- desktop_env/evaluators/metrics/docs.py +1011 -0
- desktop_env/evaluators/metrics/general.py +665 -0
- desktop_env/evaluators/metrics/gimp.py +637 -0
- desktop_env/evaluators/metrics/libreoffice.py +28 -0
- desktop_env/evaluators/metrics/others.py +92 -0
- desktop_env/evaluators/metrics/pdf.py +31 -0
- desktop_env/evaluators/metrics/slides.py +957 -0
- desktop_env/evaluators/metrics/table.py +585 -0
- desktop_env/evaluators/metrics/thunderbird.py +176 -0
- desktop_env/evaluators/metrics/utils.py +719 -0
- desktop_env/evaluators/metrics/vlc.py +524 -0
- desktop_env/evaluators/metrics/vscode.py +283 -0
- desktop_env/providers/__init__.py +35 -0
- desktop_env/providers/aws/__init__.py +0 -0
- desktop_env/providers/aws/manager.py +278 -0
- desktop_env/providers/aws/provider.py +186 -0
- desktop_env/providers/aws/provider_with_proxy.py +315 -0
- desktop_env/providers/aws/proxy_pool.py +193 -0
- desktop_env/providers/azure/__init__.py +0 -0
- desktop_env/providers/azure/manager.py +87 -0
- desktop_env/providers/azure/provider.py +207 -0
- desktop_env/providers/base.py +97 -0
- desktop_env/providers/gcp/__init__.py +0 -0
- desktop_env/providers/gcp/manager.py +0 -0
- desktop_env/providers/gcp/provider.py +0 -0
- desktop_env/providers/virtualbox/__init__.py +0 -0
- desktop_env/providers/virtualbox/manager.py +463 -0
- desktop_env/providers/virtualbox/provider.py +124 -0
- desktop_env/providers/vmware/__init__.py +0 -0
- desktop_env/providers/vmware/manager.py +455 -0
- desktop_env/providers/vmware/provider.py +105 -0
- gui_agents/__init__.py +0 -0
- gui_agents/agents/Action.py +209 -0
- gui_agents/agents/__init__.py +0 -0
- gui_agents/agents/agent_s.py +832 -0
- gui_agents/agents/global_state.py +610 -0
- gui_agents/agents/grounding.py +651 -0
- gui_agents/agents/hardware_interface.py +129 -0
- gui_agents/agents/manager.py +568 -0
- gui_agents/agents/translator.py +132 -0
- gui_agents/agents/worker.py +355 -0
- gui_agents/cli_app.py +560 -0
- gui_agents/core/__init__.py +0 -0
- gui_agents/core/engine.py +1496 -0
- gui_agents/core/knowledge.py +449 -0
- gui_agents/core/mllm.py +555 -0
- gui_agents/tools/__init__.py +0 -0
- gui_agents/tools/tools.py +727 -0
- gui_agents/unit_test/__init__.py +0 -0
- gui_agents/unit_test/run_tests.py +65 -0
- gui_agents/unit_test/test_manager.py +330 -0
- gui_agents/unit_test/test_worker.py +269 -0
- gui_agents/utils/__init__.py +0 -0
- gui_agents/utils/analyze_display.py +301 -0
- gui_agents/utils/common_utils.py +263 -0
- gui_agents/utils/display_viewer.py +281 -0
- gui_agents/utils/embedding_manager.py +53 -0
- gui_agents/utils/image_axis_utils.py +27 -0
- lybic_guiagents-0.1.0.dist-info/METADATA +416 -0
- lybic_guiagents-0.1.0.dist-info/RECORD +85 -0
- lybic_guiagents-0.1.0.dist-info/WHEEL +5 -0
- lybic_guiagents-0.1.0.dist-info/licenses/LICENSE +201 -0
- lybic_guiagents-0.1.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import boto3
|
|
2
|
+
from botocore.exceptions import ClientError
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
from desktop_env.providers.base import Provider
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
import time
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger("desktopenv.providers.aws.AWSProvider")
|
|
12
|
+
logger.setLevel(logging.INFO)
|
|
13
|
+
|
|
14
|
+
WAIT_DELAY = 15
|
|
15
|
+
MAX_ATTEMPTS = 10
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AWSProvider(Provider):
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def start_emulator(self, path_to_vm: str, headless: bool, *args, **kwargs):
|
|
22
|
+
logger.info("Starting AWS VM...")
|
|
23
|
+
ec2_client = boto3.client('ec2', region_name=self.region)
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
# Check the current state of the instance
|
|
27
|
+
response = ec2_client.describe_instances(InstanceIds=[path_to_vm])
|
|
28
|
+
state = response['Reservations'][0]['Instances'][0]['State']['Name']
|
|
29
|
+
logger.info(f"Instance {path_to_vm} current state: {state}")
|
|
30
|
+
|
|
31
|
+
if state == 'running':
|
|
32
|
+
# If the instance is already running, skip starting it
|
|
33
|
+
logger.info(f"Instance {path_to_vm} is already running. Skipping start.")
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
if state == 'stopped':
|
|
37
|
+
# Start the instance if it's currently stopped
|
|
38
|
+
ec2_client.start_instances(InstanceIds=[path_to_vm])
|
|
39
|
+
logger.info(f"Instance {path_to_vm} is starting...")
|
|
40
|
+
|
|
41
|
+
# Wait until the instance reaches 'running' state
|
|
42
|
+
waiter = ec2_client.get_waiter('instance_running')
|
|
43
|
+
waiter.wait(
|
|
44
|
+
InstanceIds=[path_to_vm],
|
|
45
|
+
WaiterConfig={'Delay': WAIT_DELAY, 'MaxAttempts': MAX_ATTEMPTS}
|
|
46
|
+
)
|
|
47
|
+
logger.info(f"Instance {path_to_vm} is now running.")
|
|
48
|
+
else:
|
|
49
|
+
# For all other states (terminated, pending, etc.), log a warning
|
|
50
|
+
logger.warning(f"Instance {path_to_vm} is in state '{state}' and cannot be started.")
|
|
51
|
+
|
|
52
|
+
except ClientError as e:
|
|
53
|
+
logger.error(f"Failed to start the AWS VM {path_to_vm}: {str(e)}")
|
|
54
|
+
raise
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_ip_address(self, path_to_vm: str) -> str:
|
|
58
|
+
logger.info("Getting AWS VM IP address...")
|
|
59
|
+
ec2_client = boto3.client('ec2', region_name=self.region)
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
response = ec2_client.describe_instances(InstanceIds=[path_to_vm])
|
|
63
|
+
for reservation in response['Reservations']:
|
|
64
|
+
for instance in reservation['Instances']:
|
|
65
|
+
private_ip_address = instance.get('PrivateIpAddress', '')
|
|
66
|
+
public_ip_address = instance.get('PublicIpAddress', '')
|
|
67
|
+
|
|
68
|
+
if public_ip_address:
|
|
69
|
+
vnc_url = f"http://{public_ip_address}:5910/vnc.html"
|
|
70
|
+
logger.info("="*80)
|
|
71
|
+
logger.info(f"๐ฅ๏ธ VNC Web Access URL: {vnc_url}")
|
|
72
|
+
logger.info(f"๐ก Public IP: {public_ip_address}")
|
|
73
|
+
logger.info(f"๐ Private IP: {private_ip_address}")
|
|
74
|
+
logger.info("="*80)
|
|
75
|
+
print(f"\n๐ VNC Web Access URL: {vnc_url}")
|
|
76
|
+
print(f"๐ Please open the above address in the browser for remote desktop access\n")
|
|
77
|
+
else:
|
|
78
|
+
logger.warning("No public IP address available for VNC access")
|
|
79
|
+
|
|
80
|
+
return private_ip_address
|
|
81
|
+
return '' # Return an empty string if no IP address is found
|
|
82
|
+
except ClientError as e:
|
|
83
|
+
logger.error(f"Failed to retrieve IP address for the instance {path_to_vm}: {str(e)}")
|
|
84
|
+
raise
|
|
85
|
+
|
|
86
|
+
def save_state(self, path_to_vm: str, snapshot_name: str):
|
|
87
|
+
logger.info("Saving AWS VM state...")
|
|
88
|
+
ec2_client = boto3.client('ec2', region_name=self.region)
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
image_response = ec2_client.create_image(InstanceId=path_to_vm, Name=snapshot_name)
|
|
92
|
+
image_id = image_response['ImageId']
|
|
93
|
+
logger.info(f"AMI {image_id} created successfully from instance {path_to_vm}.")
|
|
94
|
+
return image_id
|
|
95
|
+
except ClientError as e:
|
|
96
|
+
logger.error(f"Failed to create AMI from the instance {path_to_vm}: {str(e)}")
|
|
97
|
+
raise
|
|
98
|
+
|
|
99
|
+
def revert_to_snapshot(self, path_to_vm: str, snapshot_name: str):
|
|
100
|
+
logger.info(f"Reverting AWS VM to snapshot AMI: {snapshot_name}...")
|
|
101
|
+
ec2_client = boto3.client('ec2', region_name=self.region)
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
# Step 1: Retrieve the original instance details
|
|
105
|
+
instance_details = ec2_client.describe_instances(InstanceIds=[path_to_vm])
|
|
106
|
+
instance = instance_details['Reservations'][0]['Instances'][0]
|
|
107
|
+
security_groups = [sg['GroupId'] for sg in instance['SecurityGroups']]
|
|
108
|
+
subnet_id = instance['SubnetId']
|
|
109
|
+
instance_type = instance['InstanceType']
|
|
110
|
+
|
|
111
|
+
# Step 2: Terminate the old instance
|
|
112
|
+
ec2_client.terminate_instances(InstanceIds=[path_to_vm])
|
|
113
|
+
logger.info(f"Old instance {path_to_vm} has been terminated.")
|
|
114
|
+
|
|
115
|
+
# Step 3: Launch a new instance from the snapshot(AMI) with performance optimization
|
|
116
|
+
logger.info(f"Launching a new instance from AMI {snapshot_name}...")
|
|
117
|
+
|
|
118
|
+
run_instances_params = {
|
|
119
|
+
"MaxCount": 1,
|
|
120
|
+
"MinCount": 1,
|
|
121
|
+
"ImageId": snapshot_name,
|
|
122
|
+
"InstanceType": instance_type,
|
|
123
|
+
"EbsOptimized": True,
|
|
124
|
+
"NetworkInterfaces": [
|
|
125
|
+
{
|
|
126
|
+
"SubnetId": subnet_id,
|
|
127
|
+
"AssociatePublicIpAddress": True,
|
|
128
|
+
"DeviceIndex": 0,
|
|
129
|
+
"Groups": security_groups
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
"BlockDeviceMappings": [
|
|
133
|
+
{
|
|
134
|
+
"DeviceName": "/dev/sda1",
|
|
135
|
+
"Ebs": {
|
|
136
|
+
# "VolumeInitializationRate": 300
|
|
137
|
+
"VolumeSize": 30, # Size in GB
|
|
138
|
+
"VolumeType": "gp3", # General Purpose SSD
|
|
139
|
+
"Throughput": 1000,
|
|
140
|
+
"Iops": 4000 # Adjust IOPS as needed
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
new_instance = ec2_client.run_instances(**run_instances_params)
|
|
147
|
+
new_instance_id = new_instance['Instances'][0]['InstanceId']
|
|
148
|
+
logger.info(f"New instance {new_instance_id} launched from AMI {snapshot_name}.")
|
|
149
|
+
logger.info(f"Waiting for instance {new_instance_id} to be running...")
|
|
150
|
+
ec2_client.get_waiter('instance_running').wait(InstanceIds=[new_instance_id])
|
|
151
|
+
|
|
152
|
+
logger.info(f"Instance {new_instance_id} is ready.")
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
instance_details = ec2_client.describe_instances(InstanceIds=[new_instance_id])
|
|
156
|
+
instance = instance_details['Reservations'][0]['Instances'][0]
|
|
157
|
+
public_ip = instance.get('PublicIpAddress', '')
|
|
158
|
+
if public_ip:
|
|
159
|
+
vnc_url = f"http://{public_ip}:5910/vnc.html"
|
|
160
|
+
logger.info("="*80)
|
|
161
|
+
logger.info(f"๐ฅ๏ธ New Instance VNC Web Access URL: {vnc_url}")
|
|
162
|
+
logger.info(f"๐ก Public IP: {public_ip}")
|
|
163
|
+
logger.info(f"๐ New Instance ID: {new_instance_id}")
|
|
164
|
+
logger.info("="*80)
|
|
165
|
+
print(f"\n๐ New Instance VNC Web Access URL: {vnc_url}")
|
|
166
|
+
print(f"๐ Please open the above address in the browser for remote desktop access\n")
|
|
167
|
+
except Exception as e:
|
|
168
|
+
logger.warning(f"Failed to get VNC address for new instance {new_instance_id}: {e}")
|
|
169
|
+
|
|
170
|
+
return new_instance_id
|
|
171
|
+
|
|
172
|
+
except ClientError as e:
|
|
173
|
+
logger.error(f"Failed to revert to snapshot {snapshot_name} for the instance {path_to_vm}: {str(e)}")
|
|
174
|
+
raise
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def stop_emulator(self, path_to_vm, region=None):
|
|
178
|
+
logger.info(f"Stopping AWS VM {path_to_vm}...")
|
|
179
|
+
ec2_client = boto3.client('ec2', region_name=self.region)
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
ec2_client.terminate_instances(InstanceIds=[path_to_vm])
|
|
183
|
+
logger.info(f"Instance {path_to_vm} has been terminated.")
|
|
184
|
+
except ClientError as e:
|
|
185
|
+
logger.error(f"Failed to stop the AWS VM {path_to_vm}: {str(e)}")
|
|
186
|
+
raise
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import boto3
|
|
2
|
+
from botocore.exceptions import ClientError
|
|
3
|
+
import base64
|
|
4
|
+
import logging
|
|
5
|
+
import json
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from desktop_env.providers.base import Provider
|
|
9
|
+
from desktop_env.providers.aws.proxy_pool import get_global_proxy_pool, init_proxy_pool, ProxyInfo
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger("desktopenv.providers.aws.AWSProviderWithProxy")
|
|
12
|
+
logger.setLevel(logging.INFO)
|
|
13
|
+
|
|
14
|
+
WAIT_DELAY = 15
|
|
15
|
+
MAX_ATTEMPTS = 10
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AWSProviderWithProxy(Provider):
|
|
19
|
+
|
|
20
|
+
def __init__(self, region: str = None, proxy_config_file: str = None):
|
|
21
|
+
super().__init__(region)
|
|
22
|
+
self.current_proxy: Optional[ProxyInfo] = None
|
|
23
|
+
|
|
24
|
+
# ๅๅงๅไปฃ็ๆฑ
|
|
25
|
+
if proxy_config_file:
|
|
26
|
+
init_proxy_pool(proxy_config_file)
|
|
27
|
+
logger.info(f"Initialized proxy pool from {proxy_config_file}")
|
|
28
|
+
|
|
29
|
+
# ่ทๅไธไธไธชๅฏ็จไปฃ็
|
|
30
|
+
self._rotate_proxy()
|
|
31
|
+
|
|
32
|
+
def _rotate_proxy(self):
|
|
33
|
+
"""่ฝฎๆขๅฐไธไธไธชๅฏ็จไปฃ็"""
|
|
34
|
+
proxy_pool = get_global_proxy_pool()
|
|
35
|
+
self.current_proxy = proxy_pool.get_next_proxy()
|
|
36
|
+
|
|
37
|
+
if self.current_proxy:
|
|
38
|
+
logger.info(f"Switched to proxy: {self.current_proxy.host}:{self.current_proxy.port}")
|
|
39
|
+
else:
|
|
40
|
+
logger.warning("No proxy available, using direct connection")
|
|
41
|
+
|
|
42
|
+
def _generate_proxy_user_data(self) -> str:
|
|
43
|
+
"""็ๆๅ
ๅซไปฃ็้
็ฝฎ็user data่ๆฌ"""
|
|
44
|
+
if not self.current_proxy:
|
|
45
|
+
return ""
|
|
46
|
+
|
|
47
|
+
proxy_url = self._format_proxy_url(self.current_proxy)
|
|
48
|
+
|
|
49
|
+
user_data_script = f"""#!/bin/bash
|
|
50
|
+
# Configure system proxy
|
|
51
|
+
echo 'export http_proxy={proxy_url}' >> /etc/environment
|
|
52
|
+
echo 'export https_proxy={proxy_url}' >> /etc/environment
|
|
53
|
+
echo 'export HTTP_PROXY={proxy_url}' >> /etc/environment
|
|
54
|
+
echo 'export HTTPS_PROXY={proxy_url}' >> /etc/environment
|
|
55
|
+
|
|
56
|
+
# Configure apt proxy
|
|
57
|
+
cat > /etc/apt/apt.conf.d/95proxy << EOF
|
|
58
|
+
Acquire::http::Proxy "{proxy_url}";
|
|
59
|
+
Acquire::https::Proxy "{proxy_url}";
|
|
60
|
+
EOF
|
|
61
|
+
|
|
62
|
+
# Configure chrome/chromium proxy
|
|
63
|
+
mkdir -p /etc/opt/chrome/policies/managed
|
|
64
|
+
cat > /etc/opt/chrome/policies/managed/proxy.json << EOF
|
|
65
|
+
{{
|
|
66
|
+
"ProxyMode": "fixed_servers",
|
|
67
|
+
"ProxyServer": "{self.current_proxy.host}:{self.current_proxy.port}"
|
|
68
|
+
}}
|
|
69
|
+
EOF
|
|
70
|
+
|
|
71
|
+
# Configure chromium proxy (Ubuntu default)
|
|
72
|
+
mkdir -p /etc/chromium/policies/managed
|
|
73
|
+
cat > /etc/chromium/policies/managed/proxy.json << EOF
|
|
74
|
+
{{
|
|
75
|
+
"ProxyMode": "fixed_servers",
|
|
76
|
+
"ProxyServer": "{self.current_proxy.host}:{self.current_proxy.port}"
|
|
77
|
+
}}
|
|
78
|
+
EOF
|
|
79
|
+
|
|
80
|
+
# Configure firefox proxy - support multiple possible paths
|
|
81
|
+
for firefox_dir in /etc/firefox/policies /usr/lib/firefox/distribution/policies /etc/firefox-esr/policies; do
|
|
82
|
+
if [ -d "$(dirname "$firefox_dir")" ]; then
|
|
83
|
+
mkdir -p "$firefox_dir"
|
|
84
|
+
cat > "$firefox_dir/policies.json" << EOF
|
|
85
|
+
{{
|
|
86
|
+
"policies": {{
|
|
87
|
+
"Proxy": {{
|
|
88
|
+
"Mode": "manual",
|
|
89
|
+
"HTTPProxy": "{self.current_proxy.host}:{self.current_proxy.port}",
|
|
90
|
+
"HTTPSProxy": "{self.current_proxy.host}:{self.current_proxy.port}",
|
|
91
|
+
"UseHTTPProxyForAllProtocols": true
|
|
92
|
+
}}
|
|
93
|
+
}}
|
|
94
|
+
}}
|
|
95
|
+
EOF
|
|
96
|
+
break
|
|
97
|
+
fi
|
|
98
|
+
done
|
|
99
|
+
|
|
100
|
+
# Reload environment variables
|
|
101
|
+
source /etc/environment
|
|
102
|
+
|
|
103
|
+
# Log proxy configuration
|
|
104
|
+
echo "$(date): Configured proxy {self.current_proxy.host}:{self.current_proxy.port}" >> /var/log/proxy-setup.log
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
return base64.b64encode(user_data_script.encode()).decode()
|
|
108
|
+
|
|
109
|
+
def _format_proxy_url(self, proxy: ProxyInfo) -> str:
|
|
110
|
+
"""ๆ ผๅผๅไปฃ็URL"""
|
|
111
|
+
if proxy.username and proxy.password:
|
|
112
|
+
return f"{proxy.protocol}://{proxy.username}:{proxy.password}@{proxy.host}:{proxy.port}"
|
|
113
|
+
else:
|
|
114
|
+
return f"{proxy.protocol}://{proxy.host}:{proxy.port}"
|
|
115
|
+
|
|
116
|
+
def start_emulator(self, path_to_vm: str, headless: bool, *args, **kwargs):
|
|
117
|
+
logger.info("Starting AWS VM with proxy configuration...")
|
|
118
|
+
ec2_client = boto3.client('ec2', region_name=self.region)
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
# ๅฆๆๅฎไพๅทฒ็ปๅญๅจ๏ผ็ดๆฅๅฏๅจ
|
|
122
|
+
ec2_client.start_instances(InstanceIds=[path_to_vm])
|
|
123
|
+
logger.info(f"Instance {path_to_vm} is starting...")
|
|
124
|
+
|
|
125
|
+
# Wait for the instance to be in the 'running' state
|
|
126
|
+
waiter = ec2_client.get_waiter('instance_running')
|
|
127
|
+
waiter.wait(InstanceIds=[path_to_vm], WaiterConfig={'Delay': WAIT_DELAY, 'MaxAttempts': MAX_ATTEMPTS})
|
|
128
|
+
logger.info(f"Instance {path_to_vm} is now running.")
|
|
129
|
+
|
|
130
|
+
except ClientError as e:
|
|
131
|
+
logger.error(f"Failed to start the AWS VM {path_to_vm}: {str(e)}")
|
|
132
|
+
raise
|
|
133
|
+
|
|
134
|
+
def create_instance_with_proxy(self, image_id: str, instance_type: str,
|
|
135
|
+
security_groups: list, subnet_id: str) -> str:
|
|
136
|
+
"""ๅๅปบๅธฆๆไปฃ็้
็ฝฎ็ๆฐๅฎไพ"""
|
|
137
|
+
ec2_client = boto3.client('ec2', region_name=self.region)
|
|
138
|
+
|
|
139
|
+
user_data = self._generate_proxy_user_data()
|
|
140
|
+
|
|
141
|
+
run_instances_params = {
|
|
142
|
+
"MaxCount": 1,
|
|
143
|
+
"MinCount": 1,
|
|
144
|
+
"ImageId": image_id,
|
|
145
|
+
"InstanceType": instance_type,
|
|
146
|
+
"EbsOptimized": True,
|
|
147
|
+
"NetworkInterfaces": [
|
|
148
|
+
{
|
|
149
|
+
"SubnetId": subnet_id,
|
|
150
|
+
"AssociatePublicIpAddress": True,
|
|
151
|
+
"DeviceIndex": 0,
|
|
152
|
+
"Groups": security_groups
|
|
153
|
+
}
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if user_data:
|
|
158
|
+
run_instances_params["UserData"] = user_data
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
response = ec2_client.run_instances(**run_instances_params)
|
|
162
|
+
instance_id = response['Instances'][0]['InstanceId']
|
|
163
|
+
|
|
164
|
+
logger.info(f"Created new instance {instance_id} with proxy configuration")
|
|
165
|
+
|
|
166
|
+
logger.info(f"Waiting for instance {instance_id} to be running...")
|
|
167
|
+
ec2_client.get_waiter('instance_running').wait(InstanceIds=[instance_id])
|
|
168
|
+
logger.info(f"Instance {instance_id} is ready.")
|
|
169
|
+
|
|
170
|
+
try:
|
|
171
|
+
instance_details = ec2_client.describe_instances(InstanceIds=[instance_id])
|
|
172
|
+
instance = instance_details['Reservations'][0]['Instances'][0]
|
|
173
|
+
public_ip = instance.get('PublicIpAddress', '')
|
|
174
|
+
if public_ip:
|
|
175
|
+
vnc_url = f"http://{public_ip}:5910/vnc.html"
|
|
176
|
+
logger.info("="*80)
|
|
177
|
+
logger.info(f"๐ฅ๏ธ VNC Web Access URL: {vnc_url}")
|
|
178
|
+
logger.info(f"๐ก Public IP: {public_ip}")
|
|
179
|
+
logger.info(f"๐ Instance ID: {instance_id}")
|
|
180
|
+
if self.current_proxy:
|
|
181
|
+
logger.info(f"๐ Proxy: {self.current_proxy.host}:{self.current_proxy.port}")
|
|
182
|
+
logger.info("="*80)
|
|
183
|
+
print(f"\n๐ VNC Web Access URL: {vnc_url}")
|
|
184
|
+
if self.current_proxy:
|
|
185
|
+
print(f"๐ Current Proxy: {self.current_proxy.host}:{self.current_proxy.port}")
|
|
186
|
+
print(f"๐ Please open the above address in the browser for remote desktop access\n")
|
|
187
|
+
except Exception as e:
|
|
188
|
+
logger.warning(f"Failed to get VNC address for instance {instance_id}: {e}")
|
|
189
|
+
|
|
190
|
+
return instance_id
|
|
191
|
+
|
|
192
|
+
except ClientError as e:
|
|
193
|
+
logger.error(f"Failed to create instance with proxy: {str(e)}")
|
|
194
|
+
if self.current_proxy:
|
|
195
|
+
proxy_pool = get_global_proxy_pool()
|
|
196
|
+
proxy_pool.mark_proxy_failed(self.current_proxy)
|
|
197
|
+
self._rotate_proxy()
|
|
198
|
+
raise
|
|
199
|
+
|
|
200
|
+
def get_ip_address(self, path_to_vm: str) -> str:
|
|
201
|
+
logger.info("Getting AWS VM IP address...")
|
|
202
|
+
ec2_client = boto3.client('ec2', region_name=self.region)
|
|
203
|
+
|
|
204
|
+
try:
|
|
205
|
+
response = ec2_client.describe_instances(InstanceIds=[path_to_vm])
|
|
206
|
+
for reservation in response['Reservations']:
|
|
207
|
+
for instance in reservation['Instances']:
|
|
208
|
+
private_ip_address = instance.get('PrivateIpAddress', '')
|
|
209
|
+
public_ip_address = instance.get('PublicIpAddress', '')
|
|
210
|
+
|
|
211
|
+
if public_ip_address:
|
|
212
|
+
vnc_url = f"http://{public_ip_address}:5910/vnc.html"
|
|
213
|
+
logger.info("="*80)
|
|
214
|
+
logger.info(f"๐ฅ๏ธ VNC Web Access URL: {vnc_url}")
|
|
215
|
+
logger.info(f"๐ก Public IP: {public_ip_address}")
|
|
216
|
+
logger.info(f"๐ Private IP: {private_ip_address}")
|
|
217
|
+
if self.current_proxy:
|
|
218
|
+
logger.info(f"๐ Proxy: {self.current_proxy.host}:{self.current_proxy.port}")
|
|
219
|
+
logger.info("="*80)
|
|
220
|
+
print(f"\n๐ VNC Web Access URL: {vnc_url}")
|
|
221
|
+
if self.current_proxy:
|
|
222
|
+
print(f"๐ Current Proxy: {self.current_proxy.host}:{self.current_proxy.port}")
|
|
223
|
+
print(f"๐ Please open the above address in the browser for remote desktop access\n")
|
|
224
|
+
else:
|
|
225
|
+
logger.warning("No public IP address available for VNC access")
|
|
226
|
+
|
|
227
|
+
return private_ip_address
|
|
228
|
+
return ''
|
|
229
|
+
except ClientError as e:
|
|
230
|
+
logger.error(f"Failed to retrieve IP address for the instance {path_to_vm}: {str(e)}")
|
|
231
|
+
raise
|
|
232
|
+
|
|
233
|
+
def save_state(self, path_to_vm: str, snapshot_name: str):
|
|
234
|
+
logger.info("Saving AWS VM state...")
|
|
235
|
+
ec2_client = boto3.client('ec2', region_name=self.region)
|
|
236
|
+
|
|
237
|
+
try:
|
|
238
|
+
image_response = ec2_client.create_image(InstanceId=path_to_vm, Name=snapshot_name)
|
|
239
|
+
image_id = image_response['ImageId']
|
|
240
|
+
logger.info(f"AMI {image_id} created successfully from instance {path_to_vm}.")
|
|
241
|
+
return image_id
|
|
242
|
+
except ClientError as e:
|
|
243
|
+
logger.error(f"Failed to create AMI from the instance {path_to_vm}: {str(e)}")
|
|
244
|
+
raise
|
|
245
|
+
|
|
246
|
+
def revert_to_snapshot(self, path_to_vm: str, snapshot_name: str):
|
|
247
|
+
logger.info(f"Reverting AWS VM to snapshot: {snapshot_name}...")
|
|
248
|
+
ec2_client = boto3.client('ec2', region_name=self.region)
|
|
249
|
+
|
|
250
|
+
try:
|
|
251
|
+
# Get original instance details for config.
|
|
252
|
+
instance_details = ec2_client.describe_instances(InstanceIds=[path_to_vm])
|
|
253
|
+
instance = instance_details['Reservations'][0]['Instances'][0]
|
|
254
|
+
security_groups = [sg['GroupId'] for sg in instance['SecurityGroups']]
|
|
255
|
+
subnet_id = instance['SubnetId']
|
|
256
|
+
instance_type = instance['InstanceType']
|
|
257
|
+
|
|
258
|
+
# Terminate the old instance. This is a non-blocking call.
|
|
259
|
+
logger.info(f"Initiating termination for old instance {path_to_vm}...")
|
|
260
|
+
ec2_client.terminate_instances(InstanceIds=[path_to_vm])
|
|
261
|
+
logger.info(f"Old instance {path_to_vm} termination initiated.")
|
|
262
|
+
|
|
263
|
+
# Rotate to a new proxy
|
|
264
|
+
self._rotate_proxy()
|
|
265
|
+
|
|
266
|
+
# Create a new instance
|
|
267
|
+
new_instance_id = self.create_instance_with_proxy(
|
|
268
|
+
snapshot_name, instance_type, security_groups, subnet_id
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
# Note: VNC address is displayed within create_instance_with_proxy
|
|
272
|
+
logger.info(f"Successfully launched new instance {new_instance_id} for revert.")
|
|
273
|
+
|
|
274
|
+
return new_instance_id
|
|
275
|
+
|
|
276
|
+
except ClientError as e:
|
|
277
|
+
logger.error(f"Failed to revert to snapshot {snapshot_name} for the instance {path_to_vm}: {str(e)}")
|
|
278
|
+
raise
|
|
279
|
+
|
|
280
|
+
def stop_emulator(self, path_to_vm, region=None):
|
|
281
|
+
logger.info(f"Stopping AWS VM {path_to_vm}...")
|
|
282
|
+
ec2_client = boto3.client('ec2', region_name=self.region)
|
|
283
|
+
|
|
284
|
+
try:
|
|
285
|
+
ec2_client.stop_instances(InstanceIds=[path_to_vm])
|
|
286
|
+
waiter = ec2_client.get_waiter('instance_stopped')
|
|
287
|
+
waiter.wait(InstanceIds=[path_to_vm], WaiterConfig={'Delay': WAIT_DELAY, 'MaxAttempts': MAX_ATTEMPTS})
|
|
288
|
+
logger.info(f"Instance {path_to_vm} has been stopped.")
|
|
289
|
+
except ClientError as e:
|
|
290
|
+
logger.error(f"Failed to stop the AWS VM {path_to_vm}: {str(e)}")
|
|
291
|
+
raise
|
|
292
|
+
|
|
293
|
+
def get_current_proxy_info(self) -> Optional[dict]:
|
|
294
|
+
"""่ทๅๅฝๅไปฃ็ไฟกๆฏ"""
|
|
295
|
+
if self.current_proxy:
|
|
296
|
+
return {
|
|
297
|
+
'host': self.current_proxy.host,
|
|
298
|
+
'port': self.current_proxy.port,
|
|
299
|
+
'protocol': self.current_proxy.protocol,
|
|
300
|
+
'failed_count': self.current_proxy.failed_count
|
|
301
|
+
}
|
|
302
|
+
return None
|
|
303
|
+
|
|
304
|
+
def force_rotate_proxy(self):
|
|
305
|
+
"""ๅผบๅถ่ฝฎๆขไปฃ็"""
|
|
306
|
+
logger.info("Force rotating proxy...")
|
|
307
|
+
if self.current_proxy:
|
|
308
|
+
proxy_pool = get_global_proxy_pool()
|
|
309
|
+
proxy_pool.mark_proxy_failed(self.current_proxy)
|
|
310
|
+
self._rotate_proxy()
|
|
311
|
+
|
|
312
|
+
def get_proxy_stats(self) -> dict:
|
|
313
|
+
"""่ทๅไปฃ็ๆฑ ็ป่ฎกไฟกๆฏ"""
|
|
314
|
+
proxy_pool = get_global_proxy_pool()
|
|
315
|
+
return proxy_pool.get_stats()
|