autoverse-cli 0.28.3__py3-none-any.whl → 0.29.1.dev409__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.
- {autoverse_cli-0.28.3.dist-info → autoverse_cli-0.29.1.dev409.dist-info}/METADATA +22 -1
- {autoverse_cli-0.28.3.dist-info → autoverse_cli-0.29.1.dev409.dist-info}/RECORD +10 -10
- avrs/app_version.py +1 -1
- avrs/race_cloud.py +100 -15
- avrs/race_cloud_cfg_util.py +4 -30
- avrs/race_cloud_util.py +76 -35
- {autoverse_cli-0.28.3.dist-info → autoverse_cli-0.29.1.dev409.dist-info}/WHEEL +0 -0
- {autoverse_cli-0.28.3.dist-info → autoverse_cli-0.29.1.dev409.dist-info}/entry_points.txt +0 -0
- {autoverse_cli-0.28.3.dist-info → autoverse_cli-0.29.1.dev409.dist-info}/licenses/LICENSE +0 -0
- {autoverse_cli-0.28.3.dist-info → autoverse_cli-0.29.1.dev409.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: autoverse-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.29.1.dev409
|
|
4
4
|
Summary: The Autoverse CLI
|
|
5
5
|
Author-email: Dan Kamrath <dan.kamrath@autonomalabs.com>
|
|
6
6
|
License: # End-User License Agreement (EULA) of the Autonoma AutoVerse CLI
|
|
@@ -52,10 +52,31 @@ The new implementation of the Autoverse CLI
|
|
|
52
52
|
|
|
53
53
|
## Installation
|
|
54
54
|
|
|
55
|
+
### Stable Release (Recommended)
|
|
55
56
|
```bash
|
|
56
57
|
pip install autoverse-cli
|
|
57
58
|
```
|
|
58
59
|
|
|
60
|
+
### Development Versions
|
|
61
|
+
|
|
62
|
+
Development versions are published from feature branches and follow the format `X.Y.Z.devN`.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Install the latest development version
|
|
66
|
+
pip install autoverse-cli --pre
|
|
67
|
+
|
|
68
|
+
# Install a specific development version
|
|
69
|
+
pip install autoverse-cli==0.29.1.dev123
|
|
70
|
+
|
|
71
|
+
# Check available versions (including dev)
|
|
72
|
+
pip index versions autoverse-cli
|
|
73
|
+
|
|
74
|
+
# Upgrade to latest dev version
|
|
75
|
+
pip install --upgrade --pre autoverse-cli
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
> **Note:** `pip install autoverse-cli` (without `--pre`) always installs the latest stable release from `main`. Development versions require the `--pre` flag explicitly.
|
|
79
|
+
|
|
59
80
|
## Shell Completion
|
|
60
81
|
|
|
61
82
|
To enable tab completion for the `avrs` command, run:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
autoverse_cli-0.
|
|
1
|
+
autoverse_cli-0.29.1.dev409.dist-info/licenses/LICENSE,sha256=d4eWXho-u18HkBsX4K21uHX_bBb2UXZSrJdsb7Z_JlM,2647
|
|
2
2
|
avrs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
avrs/app_version.py,sha256=
|
|
3
|
+
avrs/app_version.py,sha256=Vb1DRkLyCKiKGLD9odhFnqePSW-VWpkduU5iQjFLvrw,864
|
|
4
4
|
avrs/argparse_help.py,sha256=EoEaohGXZXqJvs1dFEzbo9vh47CYdHdSY2Im2Ps2iFo,945
|
|
5
5
|
avrs/avrs.py,sha256=pQHc8KLAD0hCxpBoNbXOgj2Et3qiqxSMKyAXFLwWFjE,5787
|
|
6
6
|
avrs/can_tool.py,sha256=IBoctKobBJ3wCq4ZdtuXuXH4AMEUxazCfYT6YP0Luw8,7161
|
|
@@ -8,11 +8,11 @@ avrs/can_tool_util.py,sha256=G3q21dTGMLc09SDq_RieoW7kSn-kXPkXxPDjavPh4hQ,7710
|
|
|
8
8
|
avrs/cfg.py,sha256=kMn08Z4Ms1PAu2-U1QI8weEJeIVEppP4A4_tFfwnjtg,2924
|
|
9
9
|
avrs/launcher.py,sha256=uk7xG2JTLJv7FJMDo1diwc-zmTvUxXJME9-0ctrZRuk,11352
|
|
10
10
|
avrs/launcher_util.py,sha256=zXUbJByn9M-ErkNM6DvLCFtUkueYH-R0dehjQj0aYEs,6879
|
|
11
|
-
avrs/race_cloud.py,sha256=
|
|
11
|
+
avrs/race_cloud.py,sha256=Pl8YrefTFJzhax2qFEEQVrZtezANbqNX7pxOcsjkirA,23728
|
|
12
12
|
avrs/race_cloud_bridge_can.py,sha256=K3is7GFzWo2KBojr6V65kz138UfkVFXXofVxSAziArY,2888
|
|
13
|
-
avrs/race_cloud_cfg_util.py,sha256=
|
|
13
|
+
avrs/race_cloud_cfg_util.py,sha256=TF_gsP7c_39MKs1CyzeXkTH-Fwyd8wLkFkMbqQVmdeo,12066
|
|
14
14
|
avrs/race_cloud_fwd_api.py,sha256=jB4kt6Shu9Pj96WPVjK790PZjucYxmuHjjVKczQfyZI,1620
|
|
15
|
-
avrs/race_cloud_util.py,sha256=
|
|
15
|
+
avrs/race_cloud_util.py,sha256=0wGVEwnJi5ndZyiBoIp-4QdojhAshr082cUJVjE8a8s,16805
|
|
16
16
|
avrs/shell_completion.py,sha256=_IM85ADKQHYNMlTa1_nhnYWsgdOzRQ7tIFHxAT6EWWA,4298
|
|
17
17
|
avrs/simconfig.py,sha256=UZc4FrxVHUkk2e9IglSpuojXkaL_nPrJW7trbEGgOOo,2907
|
|
18
18
|
avrs/simconfig_util.py,sha256=ronvXg9Mraz60K99ABBvo_YDI1_rWphUWUn_s_voTKw,6044
|
|
@@ -44,8 +44,8 @@ avrs/requests/toggle_hud.py,sha256=sV5t5QZc4uvRihPVk8jEKZiQNsyF9tsUwq5b4jjZplc,3
|
|
|
44
44
|
avrs/requests/vd.py,sha256=Rd0mCc89m2yKjftVf0cMHMFbJG4iFd09KzrI-DsCLgI,2606
|
|
45
45
|
avrs/requests/vehicle_input.py,sha256=R1b1xkRtOBkwyU6OSN4bWVlYsIUroZG0WKbYbc4pEH8,729
|
|
46
46
|
avrs/requests/vehicle_replay.py,sha256=7HPfVLjSLNbOyzU77Bz29qpHqRsMUAIWvnwvnNiw2nk,15026
|
|
47
|
-
autoverse_cli-0.
|
|
48
|
-
autoverse_cli-0.
|
|
49
|
-
autoverse_cli-0.
|
|
50
|
-
autoverse_cli-0.
|
|
51
|
-
autoverse_cli-0.
|
|
47
|
+
autoverse_cli-0.29.1.dev409.dist-info/METADATA,sha256=63780yqs6dL_q95_hsfp4xHIMcWfFkT5-cBtZ55kNGU,4588
|
|
48
|
+
autoverse_cli-0.29.1.dev409.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
49
|
+
autoverse_cli-0.29.1.dev409.dist-info/entry_points.txt,sha256=Cb9qsUyU5AKkklehCcvtfT0-N3SXbUEqvjze4iEU5kE,40
|
|
50
|
+
autoverse_cli-0.29.1.dev409.dist-info/top_level.txt,sha256=-AJO2e4MCVej6hY0U84pu5NfMeMW5qaAPSMisDT5rmA,5
|
|
51
|
+
autoverse_cli-0.29.1.dev409.dist-info/RECORD,,
|
avrs/app_version.py
CHANGED
avrs/race_cloud.py
CHANGED
|
@@ -2,6 +2,7 @@ import subprocess
|
|
|
2
2
|
import base64
|
|
3
3
|
import logging
|
|
4
4
|
import time
|
|
5
|
+
import ipaddress
|
|
5
6
|
from avrs.cfg import *
|
|
6
7
|
from avrs.race_cloud_util import *
|
|
7
8
|
from avrs.race_cloud_cfg_util import *
|
|
@@ -11,6 +12,47 @@ from avrs.util import *
|
|
|
11
12
|
from avrs.requests.request import AvrsApiRequest
|
|
12
13
|
from argparse import RawTextHelpFormatter
|
|
13
14
|
|
|
15
|
+
|
|
16
|
+
def is_valid_public_ip(value):
|
|
17
|
+
"""Check if a string is a valid public IP address (not private, not loopback, not reserved)."""
|
|
18
|
+
try:
|
|
19
|
+
ip = ipaddress.ip_address(value)
|
|
20
|
+
# Reject private, loopback, reserved, multicast, and link-local addresses
|
|
21
|
+
return not (ip.is_private or ip.is_loopback or ip.is_reserved or
|
|
22
|
+
ip.is_multicast or ip.is_link_local)
|
|
23
|
+
except ValueError:
|
|
24
|
+
return False
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def parse_sim_target(value):
|
|
28
|
+
"""
|
|
29
|
+
Parse sim_target argument: can be a legacy index (0-5) or a public IP address.
|
|
30
|
+
Returns (is_valid, parsed_value, is_ip).
|
|
31
|
+
"""
|
|
32
|
+
# First, check if it's a legacy integer index (0-5)
|
|
33
|
+
try:
|
|
34
|
+
index = int(value)
|
|
35
|
+
if 0 <= index <= 5:
|
|
36
|
+
return (True, index, False)
|
|
37
|
+
else:
|
|
38
|
+
return (False, "Legacy index must be between 0 and 5", False)
|
|
39
|
+
except ValueError:
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
# Check if it's a valid public IP address
|
|
43
|
+
try:
|
|
44
|
+
ip = ipaddress.ip_address(value)
|
|
45
|
+
if is_valid_public_ip(value):
|
|
46
|
+
# Valid public IP address
|
|
47
|
+
return (True, value, True)
|
|
48
|
+
else:
|
|
49
|
+
# IP is private, loopback, or otherwise not public
|
|
50
|
+
return (False, "IP address must be public (not private, loopback, or reserved)", False)
|
|
51
|
+
except ValueError:
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
return (False, "Must be a legacy index (0-5) or a valid public IP address", False)
|
|
55
|
+
|
|
14
56
|
class AvrsRaceCloud(AvrsApiRequest):
|
|
15
57
|
def __init__(self, parser, cfg):
|
|
16
58
|
self.cfg = cfg
|
|
@@ -24,10 +66,9 @@ class AvrsRaceCloud(AvrsApiRequest):
|
|
|
24
66
|
'connect',
|
|
25
67
|
help='connect to an instance for cloud racing')
|
|
26
68
|
connect_parser.add_argument(
|
|
27
|
-
'
|
|
28
|
-
type
|
|
29
|
-
|
|
30
|
-
help='the index of the simulator instance to connect to')
|
|
69
|
+
'sim_target',
|
|
70
|
+
type=str,
|
|
71
|
+
help='simulator target: legacy index (0-5) or PUBLIC IP address of the server instance (private IPs not allowed)')
|
|
31
72
|
connect_parser.add_argument(
|
|
32
73
|
'team_name',
|
|
33
74
|
help='the name of the team to race under')
|
|
@@ -74,10 +115,9 @@ class AvrsRaceCloud(AvrsApiRequest):
|
|
|
74
115
|
'sim-ctrl',
|
|
75
116
|
help='control the sim program (start, stop, restart, reset)')
|
|
76
117
|
sim_ctrl_parser.add_argument(
|
|
77
|
-
'
|
|
78
|
-
type=
|
|
79
|
-
|
|
80
|
-
help='the index of the simulator instance to apply the action')
|
|
118
|
+
'sim_target',
|
|
119
|
+
type=str,
|
|
120
|
+
help='simulator target: legacy index (0-5) or PUBLIC IP address of the server instance (private IPs not allowed)')
|
|
81
121
|
sim_ctrl_parser.add_argument(
|
|
82
122
|
'action',
|
|
83
123
|
choices=['start', 'stop', 'restart', 'reset-connection', 'get-log'],
|
|
@@ -169,15 +209,29 @@ class AvrsRaceCloud(AvrsApiRequest):
|
|
|
169
209
|
def race_connect(self, args):
|
|
170
210
|
logger = logging.getLogger('avrs')
|
|
171
211
|
|
|
212
|
+
print('[DEBUG] ===== RACE CONNECT START =====')
|
|
213
|
+
print('[DEBUG] sim_target argument: {}'.format(args.sim_target))
|
|
214
|
+
|
|
215
|
+
# Validate sim_target (can be legacy index 0-5 or public IP only)
|
|
216
|
+
is_valid, parsed_value, is_ip = parse_sim_target(args.sim_target)
|
|
217
|
+
print('[DEBUG] parse_sim_target result: is_valid={}, parsed_value={}, is_ip={}'.format(
|
|
218
|
+
is_valid, parsed_value, is_ip))
|
|
219
|
+
|
|
220
|
+
if not is_valid:
|
|
221
|
+
print('invalid sim_target: {}'.format(parsed_value))
|
|
222
|
+
return
|
|
223
|
+
|
|
172
224
|
# make api call to begin connection
|
|
173
225
|
our_ip = get_local_instance_ip()
|
|
226
|
+
print('[DEBUG] Local instance IP (our_ip): {}'.format(our_ip))
|
|
174
227
|
|
|
175
228
|
if our_ip == '127.0.0.1':
|
|
176
229
|
print('this machines IP was returned as localhost. was this run on the cloud instance?')
|
|
177
230
|
return
|
|
178
231
|
|
|
179
|
-
|
|
180
|
-
|
|
232
|
+
target_display = args.sim_target if is_ip else 'index {}'.format(parsed_value)
|
|
233
|
+
logger.info('starting race-cloud connect for team {} to {}'.format(
|
|
234
|
+
args.team_name, target_display))
|
|
181
235
|
print('connecting to race with team name: {}'.format(args.team_name))
|
|
182
236
|
|
|
183
237
|
if args.no_restart_sim:
|
|
@@ -217,7 +271,7 @@ class AvrsRaceCloud(AvrsApiRequest):
|
|
|
217
271
|
|
|
218
272
|
connection_request = {
|
|
219
273
|
'action': 'connect',
|
|
220
|
-
'
|
|
274
|
+
'sim_id': str(parsed_value), # Can be index (0-5) or public IP address only
|
|
221
275
|
'ip_to_reserve': our_ip,
|
|
222
276
|
'team_name': args.team_name,
|
|
223
277
|
'sim_id_override': args.instance_id,
|
|
@@ -226,11 +280,18 @@ class AvrsRaceCloud(AvrsApiRequest):
|
|
|
226
280
|
'config_data': vcfg_data
|
|
227
281
|
}
|
|
228
282
|
|
|
283
|
+
print('[DEBUG] ===== CONNECTION REQUEST =====')
|
|
284
|
+
print('[DEBUG] sim_id (parsed): {}'.format(parsed_value))
|
|
285
|
+
print('[DEBUG] our_ip: {}'.format(our_ip))
|
|
286
|
+
print('[DEBUG] team_name: {}'.format(args.team_name))
|
|
287
|
+
print('[DEBUG] instance_id override: {}'.format(args.instance_id))
|
|
288
|
+
|
|
229
289
|
ok, response = call_race_cloud_api(connection_request)
|
|
230
290
|
if not ok:
|
|
231
291
|
print('connect api error: {}'.format(response))
|
|
232
292
|
return
|
|
233
293
|
|
|
294
|
+
print('[DEBUG] API call succeeded, parsing response...')
|
|
234
295
|
ok, rbody, sim_ip = get_api_script_response(response)
|
|
235
296
|
if not ok:
|
|
236
297
|
print(rbody)
|
|
@@ -242,9 +303,27 @@ class AvrsRaceCloud(AvrsApiRequest):
|
|
|
242
303
|
|
|
243
304
|
slot_info = {}
|
|
244
305
|
for k, v in rbody.items():
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
306
|
+
logger.info('Processing response for key: {}'.format(k))
|
|
307
|
+
logger.info('stdout: {}'.format(v.get('stdout', '')))
|
|
308
|
+
logger.info('stderr: {}'.format(v.get('stderr', '')))
|
|
309
|
+
|
|
310
|
+
stdout = v.get('stdout', '').strip()
|
|
311
|
+
stderr = v.get('stderr', '').strip()
|
|
312
|
+
|
|
313
|
+
if not stdout:
|
|
314
|
+
print('Error: No stdout from remote command')
|
|
315
|
+
if stderr:
|
|
316
|
+
print('stderr: {}'.format(stderr))
|
|
317
|
+
return
|
|
318
|
+
|
|
319
|
+
try:
|
|
320
|
+
slot_info = json.loads(stdout)
|
|
321
|
+
except json.JSONDecodeError as e:
|
|
322
|
+
print('Error parsing JSON from stdout: {}'.format(e))
|
|
323
|
+
print('stdout content: {}'.format(stdout[:500])) # Print first 500 chars
|
|
324
|
+
if stderr:
|
|
325
|
+
print('stderr: {}'.format(stderr))
|
|
326
|
+
return
|
|
248
327
|
|
|
249
328
|
if not slot_info['ok']:
|
|
250
329
|
print('issue reserving slot: {}'.format(slot_info['msg']))
|
|
@@ -406,6 +485,12 @@ class AvrsRaceCloud(AvrsApiRequest):
|
|
|
406
485
|
def remote_sim_ctrl(self, args):
|
|
407
486
|
logger = logging.getLogger('avrs')
|
|
408
487
|
|
|
488
|
+
# Validate sim_target (can be legacy index 0-5 or public IP only)
|
|
489
|
+
is_valid, parsed_value, is_ip = parse_sim_target(args.sim_target)
|
|
490
|
+
if not is_valid:
|
|
491
|
+
print('invalid sim_target: {}'.format(parsed_value))
|
|
492
|
+
return
|
|
493
|
+
|
|
409
494
|
# go ahead and reset local connection as well
|
|
410
495
|
if args.action == 'reset-connection':
|
|
411
496
|
reset_race_cloud_connection()
|
|
@@ -414,7 +499,7 @@ class AvrsRaceCloud(AvrsApiRequest):
|
|
|
414
499
|
|
|
415
500
|
reset_request = {
|
|
416
501
|
'action': args.action,
|
|
417
|
-
'
|
|
502
|
+
'sim_id': str(parsed_value), # Can be index (0-5) or public IP address only
|
|
418
503
|
'ip_to_reserve': our_ip,
|
|
419
504
|
'sim_id_override': args.instance_id,
|
|
420
505
|
'ensure_instance_is_running': False
|
avrs/race_cloud_cfg_util.py
CHANGED
|
@@ -78,13 +78,13 @@ def register_received_vehicle(team_name, slot, cfg_data, bsu_vcan, kistler_vcan,
|
|
|
78
78
|
if eav24 is None:
|
|
79
79
|
return (False, 'no eav24 payload found')
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
logger.info('disabling perception for received vehicle config')
|
|
82
|
+
eav24['body']['bLidarEnabled'] = False
|
|
83
83
|
eav24['body']['bCameraEnabled'] = False
|
|
84
84
|
eav24['body']['bRadarEnabled'] = False
|
|
85
|
-
|
|
85
|
+
eav24['body']['bPublishGroundTruth'] = False
|
|
86
86
|
eav24['body']['bPublishInputs'] = False
|
|
87
|
-
eav24['body']['bRenderHudInWorld'] =
|
|
87
|
+
eav24['body']['bRenderHudInWorld'] = True
|
|
88
88
|
|
|
89
89
|
# do not disable HUD
|
|
90
90
|
#logger.info('disabling hud for received vehicle config')
|
|
@@ -208,32 +208,6 @@ def register_received_vehicle(team_name, slot, cfg_data, bsu_vcan, kistler_vcan,
|
|
|
208
208
|
logger.info("opponentGroundTruthDsd rate was > 100. clamping")
|
|
209
209
|
gtc["body"]["opponentGroundTruthDsd"]["rateHz"] = 100
|
|
210
210
|
|
|
211
|
-
# clamp lidar rates
|
|
212
|
-
for payload in cfg_object['payloads']:
|
|
213
|
-
if payload['typeName'].lower() == 'genericlidaripd':
|
|
214
|
-
lidar_rate = payload.get("body", {}).get("sensorDesc", {}).get("dataStream", {}).get("rateHz", 0)
|
|
215
|
-
if lidar_rate > 20:
|
|
216
|
-
instance_name = payload.get("body", {}).get("componentConfig", {}).get("instanceName", "unknown")
|
|
217
|
-
logger.info("lidar {} rate was > 20. clamping".format(instance_name))
|
|
218
|
-
payload["body"]["sensorDesc"]["dataStream"]["rateHz"] = 20
|
|
219
|
-
|
|
220
|
-
# clamp camera rates
|
|
221
|
-
for payload in cfg_object['payloads']:
|
|
222
|
-
if payload['typeName'].lower() == 'camerasensor':
|
|
223
|
-
camera_rate = payload.get("body", {}).get("sensorDesc", {}).get("dataStream", {}).get("rateHz", 0)
|
|
224
|
-
if camera_rate > 20:
|
|
225
|
-
instance_name = payload.get("body", {}).get("componentConfig", {}).get("instanceName", "unknown")
|
|
226
|
-
logger.info("camera {} rate was > 20. clamping".format(instance_name))
|
|
227
|
-
payload["body"]["sensorDesc"]["dataStream"]["rateHz"] = 20
|
|
228
|
-
|
|
229
|
-
# clamp radar rates
|
|
230
|
-
for payload in cfg_object['payloads']:
|
|
231
|
-
if payload['typeName'].lower() == 'radarsensor':
|
|
232
|
-
radar_rate = payload.get("body", {}).get("sensorDesc", {}).get("dataStream", {}).get("rateHz", 0)
|
|
233
|
-
if radar_rate > 20:
|
|
234
|
-
instance_name = payload.get("body", {}).get("componentConfig", {}).get("instanceName", "unknown")
|
|
235
|
-
logger.info("radar {} rate was > 20. clamping".format(instance_name))
|
|
236
|
-
payload["body"]["sensorDesc"]["dataStream"]["rateHz"] = 20
|
|
237
211
|
|
|
238
212
|
# do not allow default object name (collision)
|
|
239
213
|
if cfg_object['name'] == 'eav24':
|
avrs/race_cloud_util.py
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import json
|
|
3
|
-
import http.client
|
|
4
3
|
import logging
|
|
4
|
+
import boto3
|
|
5
|
+
from botocore.auth import SigV4Auth
|
|
6
|
+
from botocore.awsrequest import AWSRequest
|
|
7
|
+
import urllib.request
|
|
5
8
|
from avrs.util import *
|
|
6
9
|
|
|
7
10
|
BASH_KILL_PROCESS_SCRIPT = '''
|
|
@@ -360,45 +363,83 @@ def call_race_cloud_api(body):
|
|
|
360
363
|
logger = logging.getLogger('avrs')
|
|
361
364
|
logger.info('calling race-cloud api with body: {}'.format(body))
|
|
362
365
|
|
|
363
|
-
|
|
366
|
+
url = 'https://9gca6018p0.execute-api.us-east-1.amazonaws.com/backend/race-connect'
|
|
367
|
+
region = 'us-east-1'
|
|
364
368
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
'x-api-key': '7aQ83sJ89Q2DZ8NdIi9aUTBuUS2uyix5QoDwrl1j'
|
|
369
|
-
}
|
|
370
|
-
body = json.dumps(body).encode('utf-8')
|
|
371
|
-
connection.request('POST', '/beta/connect', body, headers)
|
|
372
|
-
response = connection.getresponse()
|
|
373
|
-
if response.status != 200:
|
|
374
|
-
return (False, 'response had status code {}'.format(response))
|
|
375
|
-
return (True, response.read().decode('utf-8'))
|
|
369
|
+
print('[DEBUG] ===== RACE CLOUD API CALL =====')
|
|
370
|
+
print('[DEBUG] URL: {}'.format(url))
|
|
371
|
+
print('[DEBUG] Request body: {}'.format(json.dumps(body, indent=2)))
|
|
376
372
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
373
|
+
# Get credentials from boto3 session (uses IAM role on EC2)
|
|
374
|
+
session = boto3.Session()
|
|
375
|
+
credentials = session.get_credentials()
|
|
376
|
+
|
|
377
|
+
if credentials is None:
|
|
378
|
+
print('[DEBUG] ERROR: No AWS credentials found!')
|
|
379
|
+
return (False, 'No AWS credentials available')
|
|
382
380
|
|
|
383
|
-
|
|
384
|
-
if
|
|
385
|
-
decoded = json.loads(decoded)
|
|
381
|
+
print('[DEBUG] AWS credentials found: access_key={}...'.format(
|
|
382
|
+
credentials.access_key[:10] if credentials.access_key else 'None'))
|
|
386
383
|
|
|
387
|
-
|
|
384
|
+
# Prepare the request body
|
|
385
|
+
body_json = json.dumps(body)
|
|
388
386
|
|
|
389
|
-
|
|
390
|
-
|
|
387
|
+
# Create AWS request for signing
|
|
388
|
+
request = AWSRequest(
|
|
389
|
+
method='POST',
|
|
390
|
+
url=url,
|
|
391
|
+
data=body_json,
|
|
392
|
+
headers={'Content-Type': 'application/json'}
|
|
393
|
+
)
|
|
391
394
|
|
|
392
|
-
|
|
393
|
-
|
|
395
|
+
# Sign the request with SigV4
|
|
396
|
+
SigV4Auth(credentials, 'execute-api', region).add_auth(request)
|
|
394
397
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
398
|
+
print('[DEBUG] Request headers after signing:')
|
|
399
|
+
for k, v in request.headers.items():
|
|
400
|
+
# Don't print full auth header for security
|
|
401
|
+
if k.lower() == 'authorization':
|
|
402
|
+
print('[DEBUG] {}: {}...'.format(k, v[:50]))
|
|
403
|
+
else:
|
|
404
|
+
print('[DEBUG] {}: {}'.format(k, v))
|
|
405
|
+
|
|
406
|
+
# Make the request using urllib
|
|
407
|
+
try:
|
|
408
|
+
req = urllib.request.Request(
|
|
409
|
+
url,
|
|
410
|
+
data=body_json.encode('utf-8'),
|
|
411
|
+
headers=dict(request.headers),
|
|
412
|
+
method='POST'
|
|
413
|
+
)
|
|
414
|
+
print('[DEBUG] Sending request...')
|
|
415
|
+
with urllib.request.urlopen(req) as response:
|
|
416
|
+
response_body = response.read().decode('utf-8')
|
|
417
|
+
print('[DEBUG] Response status: {}'.format(response.status))
|
|
418
|
+
print('[DEBUG] Response body (first 500 chars): {}'.format(response_body[:500]))
|
|
419
|
+
return (True, response_body)
|
|
420
|
+
except urllib.error.HTTPError as e:
|
|
421
|
+
error_body = e.read().decode('utf-8')
|
|
422
|
+
print('[DEBUG] HTTP ERROR!')
|
|
423
|
+
print('[DEBUG] Status code: {}'.format(e.code))
|
|
424
|
+
print('[DEBUG] Reason: {}'.format(e.reason))
|
|
425
|
+
print('[DEBUG] Headers: {}'.format(dict(e.headers)))
|
|
426
|
+
print('[DEBUG] Body: {}'.format(error_body))
|
|
427
|
+
logger.error('API error - status: {}, body: {}'.format(e.code, error_body))
|
|
428
|
+
return (False, 'response had status code {} - {}'.format(e.code, error_body))
|
|
402
429
|
except Exception as e:
|
|
403
|
-
|
|
404
|
-
|
|
430
|
+
print('[DEBUG] EXCEPTION: {} - {}'.format(type(e).__name__, str(e)))
|
|
431
|
+
logger.error('API request failed: {}'.format(str(e)))
|
|
432
|
+
return (False, 'request failed: {}'.format(str(e)))
|
|
433
|
+
|
|
434
|
+
def get_api_script_response(raw):
|
|
435
|
+
logger = logging.getLogger('avrs')
|
|
436
|
+
# Parse API Gateway response, then parse the body which is also JSON string
|
|
437
|
+
api_response = json.loads(raw)
|
|
438
|
+
body_str = api_response['body']
|
|
439
|
+
# Body is a JSON string from Lambda Proxy integration
|
|
440
|
+
decoded = json.loads(body_str) if isinstance(body_str, str) else body_str
|
|
441
|
+
logger.info('race cloud api response: {}'.format(decoded))
|
|
442
|
+
if decoded['script_response']['statusCode'] != 200:
|
|
443
|
+
return (False, 'inner response had bad status code {}'.format(decoded))
|
|
444
|
+
#print(decoded)
|
|
445
|
+
return (True, json.loads(decoded['script_response']['body']), decoded['sim_private_ip'])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|