1818
1919parse_json () {
2020 local json_string=" $1 "
21- echo " $json_string " | jq ' .' > /dev/null || { echo ' {"error": "Invalid JSON input"}' ; exit 1; }
21+ echo " $json_string " | jq ' .' > /dev/null || { echo ' {"status": " error", "error": "Invalid JSON input"}' ; exit 1; }
2222
2323 local -A details
2424 while IFS=" =" read -r key value; do
@@ -112,9 +112,14 @@ call_proxmox_api() {
112112 curl_opts+=(-d " $data " )
113113 fi
114114
115- # echo curl "${curl_opts[@]}" "https://${url}:8006/api2/json${path}" >&2
116115 response=$( curl " ${curl_opts[@]} " " https://${url} :8006/api2/json${path} " )
116+ local status=$?
117+ if [[ $status -ne 0 ]]; then
118+ echo " {\" errors\" :{\" curl\" :\" API call failed with status $status : $( echo " $response " | jq -Rsa . | jq -r .) \" }}"
119+ return $status
120+ fi
117121 echo " $response "
122+ return 0
118123}
119124
120125wait_for_proxmox_task () {
@@ -129,7 +134,7 @@ wait_for_proxmox_task() {
129134 local now
130135 now=$( date +%s)
131136 if (( now - start_time > timeout )) ; then
132- echo ' {"error":"Timeout while waiting for async task"}'
137+ echo ' {"status": "error", " error":"Timeout while waiting for async task"}'
133138 exit 1
134139 fi
135140
@@ -139,7 +144,7 @@ wait_for_proxmox_task() {
139144 if [[ -z " $status_response " || " $status_response " == * ' "errors":' * ]]; then
140145 local msg
141146 msg=$( echo " $status_response " | jq -r ' .message // "Unknown error"' )
142- echo " {\" error\" : \" $msg \" }"
147+ echo " {\" status \" : \" error\" , \" error \" : \" $msg \" }"
143148 exit 1
144149 fi
145150
@@ -285,6 +290,86 @@ status() {
285290 echo " {\" status\" : \" success\" , \" power_state\" : \" $powerstate \" }"
286291}
287292
293+ get_node_host () {
294+ check_required_fields node
295+ local net_json host
296+
297+ if ! net_json=" $( call_proxmox_api GET " /nodes/${node} /network" ) " ; then
298+ echo " "
299+ return 1
300+ fi
301+
302+ # Prefer a static non-bridge IP
303+ host=" $( echo " $net_json " | jq -r '
304+ .data
305+ | map(select(
306+ (.type // "") != "bridge" and
307+ (.type // "") != "bond" and
308+ (.method // "") == "static" and
309+ ((.address // .cidr // "") != "")
310+ ))
311+ | map(.address // (.cidr | split("/")[0]))
312+ | .[0] // empty
313+ ' 2> /dev/null) "
314+
315+ # Fallback: first interface with a CIDR
316+ if [[ -z " $host " ]]; then
317+ host=" $( echo " $net_json " | jq -r '
318+ .data
319+ | map(select((.cidr // "") != ""))
320+ | map(.cidr | split("/")[0])
321+ | .[0] // empty
322+ ' 2> /dev/null) "
323+ fi
324+
325+ echo " $host "
326+ }
327+
328+ get_console () {
329+ check_required_fields node vmid
330+
331+ local api_resp port ticket
332+ if ! api_resp=" $( call_proxmox_api POST " /nodes/${node} /qemu/${vmid} /vncproxy" ) " ; then
333+ echo " $api_resp " | jq -c ' {status:"error", error:(.errors.curl // (.errors|tostring))}'
334+ exit 1
335+ fi
336+
337+ port=" $( echo " $api_resp " | jq -re ' .data.port // empty' 2> /dev/null || true) "
338+ ticket=" $( echo " $api_resp " | jq -re ' .data.ticket // empty' 2> /dev/null || true) "
339+
340+ if [[ -z " $port " || -z " $ticket " ]]; then
341+ jq -n --arg raw " $api_resp " \
342+ ' {status:"error", error:"Proxmox response missing port/ticket", upstream:$raw}'
343+ exit 1
344+ fi
345+
346+ # Derive host from node’s network info
347+ local host
348+ host=" $( get_node_host) "
349+ if [[ -z " $host " ]]; then
350+ jq -n --arg msg " Could not determine host IP for node $node " \
351+ ' {status:"error", error:$msg}'
352+ exit 1
353+ fi
354+
355+ jq -n \
356+ --arg host " $host " \
357+ --arg port " $port " \
358+ --arg password " $ticket " \
359+ --argjson passwordonetimeuseonly true \
360+ ' {
361+ status: "success",
362+ message: "Console retrieved",
363+ console: {
364+ host: $host,
365+ port: $port,
366+ password: $password,
367+ passwordonetimeuseonly: $passwordonetimeuseonly,
368+ protocol: "vnc"
369+ }
370+ }'
371+ }
372+
288373list_snapshots () {
289374 snapshot_response=$( call_proxmox_api GET " /nodes/${node} /qemu/${vmid} /snapshot" )
290375 echo " $snapshot_response " | jq '
@@ -356,7 +441,12 @@ parameters_file="$2"
356441wait_time=$3
357442
358443if [[ -z " $action " || -z " $parameters_file " ]]; then
359- echo ' {"error":"Missing required arguments"}'
444+ echo ' {"status": "error", "error": "Missing required arguments"}'
445+ exit 1
446+ fi
447+
448+ if [[ ! -r " $parameters_file " ]]; then
449+ echo ' {"status": "error", "error": "File not found or unreadable"}'
360450 exit 1
361451fi
362452
@@ -396,6 +486,9 @@ case $action in
396486 status)
397487 status
398488 ;;
489+ getconsole)
490+ get_console
491+ ;;
399492 ListSnapshots)
400493 list_snapshots
401494 ;;
@@ -409,7 +502,7 @@ case $action in
409502 delete_snapshot
410503 ;;
411504 * )
412- echo ' {"error": "Invalid action"}'
505+ echo ' {"status": " error", "error": "Invalid action"}'
413506 exit 1
414507 ;;
415508esac
0 commit comments