Skip to content

Commit b5b3a41

Browse files
committed
Release 0.4.0-2
1 parent b3bf4d1 commit b5b3a41

4 files changed

Lines changed: 273 additions & 40 deletions

File tree

firecracker.sh

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
usage() {
5+
cat <<'EOF'
6+
Usage: firecracker.sh <action> <payload.json> [timeout]
7+
8+
Actions: create, start, stop, reboot, delete, status, recover, console
9+
EOF
10+
}
11+
12+
fail() {
13+
echo "{\"status\":\"error\",\"error\":\"$1\"}"
14+
exit 1
15+
}
16+
17+
[[ $# -lt 2 ]] && { usage; fail "Missing action or payload file"; }
18+
19+
ACTION="$1"
20+
PAYLOAD_FILE="$2"
21+
TIMEOUT="${3:-${FC_AGENT_TIMEOUT:-30}}"
22+
23+
[[ -r "$PAYLOAD_FILE" ]] || fail "Payload file not found or unreadable"
24+
if ! [[ "$TIMEOUT" =~ ^[0-9]+$ ]]; then
25+
fail "Timeout must be an integer"
26+
fi
27+
28+
RAW_PAYLOAD="$(<"$PAYLOAD_FILE")"
29+
30+
mapfile -t FIELDS < <(
31+
jq -r '[
32+
(.vm_name // ."cloudstack.vm.details".name // ."cloudstack.vm.details".uuid // "vm"),
33+
(.host_url // .externaldetails.host.url // ""),
34+
(.host_port // .externaldetails.host.port // .externaldetails.host.agent_port // 8000),
35+
(.host_username // .externaldetails.host.username // .externaldetails.host.user // .externaldetails.host.login // .username // ""),
36+
(.host_password // .externaldetails.host.password // .externaldetails.host.pass // .password // ""),
37+
(.host_token // .externaldetails.host.token // .externaldetails.host.agent_token // ""),
38+
((.skip_ssl_verification // .host_skip_ssl_verification // .externaldetails.host.skip_ssl_verification // "false") | ascii_downcase),
39+
(.ca_bundle // .externaldetails.host.ca_bundle // .externaldetails.host.ca_cert // ""),
40+
(.client_cert // .externaldetails.host.client_cert // ""),
41+
(.client_key // .externaldetails.host.client_key // "")
42+
] | @tsv' <<<"$RAW_PAYLOAD"
43+
)
44+
45+
IFS=$'\t' read -r VM_NAME HOST_URL HOST_PORT HOST_USERNAME HOST_PASSWORD HOST_TOKEN SKIP_VERIFY CA_BUNDLE CLIENT_CERT CLIENT_KEY <<<"${FIELDS[0]}"
46+
47+
[[ -z "$HOST_URL" ]] && fail "host_url or externaldetails.host.url is required"
48+
if [[ "$VM_NAME" =~ [^A-Za-z0-9-] ]]; then
49+
fail "Invalid VM name '$VM_NAME'. Only alphanumeric characters and dashes are allowed."
50+
fi
51+
52+
if [[ "$HOST_URL" != *"://"* ]]; then
53+
HOST_URL="http://$HOST_URL"
54+
HAS_PORT=false
55+
else
56+
authority="${HOST_URL#*://}"
57+
authority="${authority%%/*}"
58+
if [[ "$authority" == *:* ]]; then
59+
HAS_PORT=true
60+
else
61+
HAS_PORT=false
62+
fi
63+
fi
64+
65+
BASE="${HOST_URL%/}"
66+
if [[ $HAS_PORT == false ]]; then
67+
BASE="${BASE}:${HOST_PORT}"
68+
fi
69+
API_BASE="${BASE%/}/v1"
70+
71+
declare -a CURL_OPTS
72+
CURL_OPTS=(-sS -w '\n%{http_code}')
73+
74+
if [[ "$SKIP_VERIFY" == "true" || "$SKIP_VERIFY" == "1" ]]; then
75+
CURL_OPTS+=(-k)
76+
fi
77+
if [[ -n "$CA_BUNDLE" ]]; then
78+
CURL_OPTS+=(--cacert "$CA_BUNDLE")
79+
fi
80+
if [[ -n "$CLIENT_CERT" ]]; then
81+
if [[ -n "$CLIENT_KEY" ]]; then
82+
CURL_OPTS+=(--cert "$CLIENT_CERT" --key "$CLIENT_KEY")
83+
else
84+
CURL_OPTS+=(--cert "$CLIENT_CERT")
85+
fi
86+
fi
87+
88+
declare -a CURL_HEADERS
89+
CURL_HEADERS=(-H "Accept: application/json")
90+
if [[ -n "$HOST_TOKEN" ]]; then
91+
CURL_HEADERS+=(-H "Authorization: Bearer $HOST_TOKEN")
92+
fi
93+
if [[ -n "$HOST_USERNAME" ]]; then
94+
CURL_OPTS+=(-u "${HOST_USERNAME}:${HOST_PASSWORD}")
95+
fi
96+
97+
call_api() {
98+
local method="$1"
99+
local path="$2"
100+
local body="${3:-}"
101+
local args=("${CURL_OPTS[@]}" -X "$method" "${CURL_HEADERS[@]}")
102+
if [[ -n "$body" ]]; then
103+
args+=(-H "Content-Type: application/json" -d "$body")
104+
fi
105+
106+
local response
107+
if ! response=$(curl "${args[@]}" "$API_BASE$path"); then
108+
fail "HTTP request failed"
109+
fi
110+
local http_code="${response##*$'\n'}"
111+
local body_content="${response%$'\n'$http_code}"
112+
113+
if [[ "$http_code" =~ ^[45] ]]; then
114+
local err
115+
err=$(jq -r '.error // .message // @json' <<<"$body_content" 2>/dev/null || echo "$body_content")
116+
fail "Agent error ($http_code): $err"
117+
fi
118+
119+
echo "$body_content"
120+
}
121+
122+
payload_with_spec() {
123+
jq -c --argjson timeout "$TIMEOUT" '{spec: ., timeout: $timeout}' "$PAYLOAD_FILE"
124+
}
125+
126+
timeout_payload() {
127+
jq -n --argjson timeout "$TIMEOUT" '{timeout: $timeout}'
128+
}
129+
130+
case "$ACTION" in
131+
create)
132+
call_api POST "/vms" "$(payload_with_spec)"
133+
;;
134+
start)
135+
call_api POST "/vms/${VM_NAME}/start" "$(payload_with_spec)"
136+
;;
137+
stop)
138+
call_api POST "/vms/${VM_NAME}/stop" "$(timeout_payload)"
139+
;;
140+
reboot)
141+
call_api POST "/vms/${VM_NAME}/reboot" "$(timeout_payload)"
142+
;;
143+
delete)
144+
call_api DELETE "/vms/${VM_NAME}"
145+
;;
146+
status)
147+
call_api GET "/vms/${VM_NAME}/status"
148+
;;
149+
recover)
150+
call_api POST "/vms/${VM_NAME}/recover" "$(payload_with_spec)"
151+
;;
152+
console)
153+
call_api POST "/vms/${VM_NAME}/console"
154+
;;
155+
*)
156+
usage
157+
fail "Invalid action '$ACTION'"
158+
;;
159+
esac

host-agent/api/handlers.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -670,12 +670,22 @@ def _safe_int(value, default):
670670
storage = StorageSpec(
671671
driver="file", volume_file=Path(storage_volume_dir) / f"{vm.name}.img"
672672
)
673-
# Determine networking driver based on CloudStack networking configuration
674-
# Default to linux-bridge-vlan for VLAN-based networking
675-
net_driver = "linux-bridge-vlan"
676-
net_bridge = self.agent_defaults.get("net", {}).get("host_bridge", "")
677-
net_host_bridge = self.agent_defaults.get("net", {}).get("host_bridge", "")
678-
net_uplink = self.agent_defaults.get("net", {}).get("uplink", "")
673+
# Determine networking driver based on payload or agent defaults
674+
net_defaults = self.agent_defaults.get("net", {}) or {}
675+
net_payload = obj.get("net") or external_details.get("net") or {}
676+
677+
def _norm(value: Any, default: str = "") -> str:
678+
if isinstance(value, str):
679+
return value.strip()
680+
if value is None:
681+
return default
682+
return str(value)
683+
684+
raw_driver = net_payload.get("driver") or net_defaults.get("driver") or "linux-bridge-vlan"
685+
net_driver = _norm(raw_driver, "linux-bridge-vlan") or "linux-bridge-vlan"
686+
net_bridge = _norm(net_payload.get("bridge"), net_defaults.get("bridge") or net_defaults.get("host_bridge", ""))
687+
net_host_bridge = _norm(net_payload.get("host_bridge"), net_defaults.get("host_bridge", ""))
688+
net_uplink = _norm(net_payload.get("uplink"), net_defaults.get("uplink", ""))
679689
net = NetSpec(driver=net_driver, bridge=net_bridge, nics=nics, host_bridge=net_host_bridge, uplink=net_uplink)
680690
return Spec(vm=vm, host=host, vmext=vmext, storage=storage, net=net)
681691

host-agent/debian/changelog

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
firecracker-cloudstack-agent (0.4.0-2) unstable; urgency=medium
2+
3+
* Fix network driver selection so CloudStack payloads/defaults can request
4+
ovs-vlan instead of hardcoded linux-bridge-vlan.
5+
* Improve the VNC console bridge (custom geometry, logging, color tweaks)
6+
and ship a shell-based client helper (`firecracker.sh`) mirroring the
7+
Proxmox extension workflow.
8+
9+
-- Marco Sinhoreli <msinhore@gmail.com> Tue, 11 Nov 2025 15:00:00 +0000
10+
111
firecracker-cloudstack-agent (0.4.0-1) unstable; urgency=medium
212

313
* Add a VNC console bridge leveraging Xvfb/xterm/x11vnc and expose it via
@@ -8,6 +18,13 @@ firecracker-cloudstack-agent (0.4.0-1) unstable; urgency=medium
818
required x11vnc/xterm/xvfb packages as dependencies.
919

1020
-- Marco Sinhoreli <msinhore@gmail.com> Fri, 17 Oct 2025 10:15:00 +0000
21+
firecracker-cloudstack-agent (0.3.2-2) unstable; urgency=medium
22+
23+
* Hotfix: honor `net.driver` from CloudStack payloads or agent defaults
24+
instead of hardcoding linux-bridge-vlan, so ovs-vlan deployments work
25+
without manual patches on v0.3.2.
26+
27+
-- Marco Sinhoreli <msinhore@gmail.com> Tue, 11 Nov 2025 14:05:00 +0000
1128

1229
firecracker-cloudstack-agent (0.3.2-1) unstable; urgency=medium
1330

0 commit comments

Comments
 (0)