1111
1212Author: [Your Name]
1313Created: [Date]
14+
15+ IMPORTANT - kcli Commands:
16+ kcli requires root/sudo privileges for VM operations.
17+ Always use get_kcli_prefix() from dag_helpers.py:
18+
19+ from dag_helpers import get_kcli_prefix
20+ KCLI = get_kcli_prefix() # Returns "sudo " or "" based on environment
21+
22+ Then use f-strings in commands:
23+ command=f"{KCLI}kcli list vm"
1424"""
1525
1626from datetime import datetime , timedelta
1727from airflow import DAG
1828from airflow .operators .bash import BashOperator
1929from airflow .operators .python import BranchPythonOperator , PythonOperator
2030
31+ # Import helpers for portable DAGs
32+ # See dag_helpers.py for documentation
33+ from dag_helpers import get_ssh_conn_id , get_kcli_prefix
34+
35+ # =============================================================================
36+ # kcli Prefix Configuration
37+ # =============================================================================
38+ # IMPORTANT: kcli requires root privileges. This prefix adds "sudo " when
39+ # running as a non-root user (e.g., in CI/CD pipelines).
40+ # Controlled by QUBINODE_KCLI_SUDO env var or auto-detected from SSH user.
41+ KCLI = get_kcli_prefix ()
42+
2143# =============================================================================
2244# DAG Configuration
2345# =============================================================================
@@ -186,36 +208,38 @@ def log_completion(**context) -> None:
186208
187209# Task: Create resources
188210# ADR-0046: Wrap kcli/virsh commands in SSH
211+ # NOTE: Uses KCLI prefix for sudo support in CI/CD environments
189212create_resources = BashOperator (
190213 task_id = "create_resources" ,
191- bash_command = """
214+ bash_command = f """
192215 echo "========================================"
193216 echo "Creating Resources"
194217 echo "========================================"
195218
196- VM_NAME="{{ params.vm_name }}"
197- VM_PROFILE="{{ params.vm_profile }}"
198- TARGET_SERVER="{{ params.target_server }}"
219+ VM_NAME="{{{{ params.vm_name }} }}"
220+ VM_PROFILE="{{{{ params.vm_profile }} }}"
221+ TARGET_SERVER="{{{{ params.target_server }} }}"
199222
200223 # Generate VM name if not provided
201224 if [ -z "$VM_NAME" ]; then
202- VM_NAME="${VM_PROFILE}-$(date +%s | md5sum | head -c 5)"
225+ VM_NAME="${{ VM_PROFILE} }-$(date +%s | md5sum | head -c 5)"
203226 echo "[INFO] Generated VM name: $VM_NAME"
204227 fi
205228
206229 # ADR-0046: Execute kcli via SSH
230+ # Using KCLI prefix for sudo support when running as non-root
207231 echo "[INFO] Creating VM via kcli..."
208232 ssh -o StrictHostKeyChecking=no -o LogLevel=ERROR root@localhost \
209- "kcli create vm -p ${VM_PROFILE} ${VM_NAME}" || {
233+ "{ KCLI } kcli create vm -p ${{ VM_PROFILE}} ${{ VM_NAME}} " || { {
210234 echo "[ERROR] Failed to create VM"
211235 exit 1
212- }
236+ }}
213237
214238 # Wait for VM to get IP
215239 echo "[INFO] Waiting for VM to get IP address..."
216- for i in {1..30}; do
240+ for i in {{ 1..30} }; do
217241 VM_IP=$(ssh -o StrictHostKeyChecking=no root@localhost \
218- "kcli info vm ${VM_NAME} 2>/dev/null | grep '^ip:' | awk '{print \\ $2}' | head -1")
242+ "{ KCLI } kcli info vm ${{ VM_NAME}} 2>/dev/null | grep '^ip:' | awk '{{ print \\ $2} }' | head -1")
219243 if [ -n "$VM_IP" ]; then
220244 echo "[OK] VM IP: $VM_IP"
221245 break
@@ -239,19 +263,19 @@ def log_completion(**context) -> None:
239263# Task: Delete resources
240264delete_resources = BashOperator (
241265 task_id = "delete_resources" ,
242- bash_command = """
266+ bash_command = f """
243267 echo "========================================"
244268 echo "Deleting Resources"
245269 echo "========================================"
246270
247- VM_NAME="{{ params.vm_name }}"
248- VM_PROFILE="{{ params.vm_profile }}"
271+ VM_NAME="{{{{ params.vm_name }} }}"
272+ VM_PROFILE="{{{{ params.vm_profile }} }}"
249273
250274 # Find VM by profile if name not provided
251275 if [ -z "$VM_NAME" ]; then
252276 echo "[INFO] Looking for VMs matching profile: $VM_PROFILE"
253277 VM_NAME=$(ssh -o StrictHostKeyChecking=no root@localhost \
254- "kcli list vm 2>/dev/null | grep ${VM_PROFILE} | tail -1 | awk '{print \\ $2}'" 2>/dev/null)
278+ "{ KCLI } kcli list vm 2>/dev/null | grep ${{ VM_PROFILE}} | tail -1 | awk '{{ print \\ $2} }'" 2>/dev/null)
255279 fi
256280
257281 if [ -z "$VM_NAME" ]; then
@@ -262,10 +286,10 @@ def log_completion(**context) -> None:
262286 # ADR-0046: Execute kcli via SSH
263287 echo "[INFO] Deleting VM: $VM_NAME"
264288 ssh -o StrictHostKeyChecking=no -o LogLevel=ERROR root@localhost \
265- "kcli delete vm ${VM_NAME} -y" || {
289+ "{ KCLI } kcli delete vm ${{ VM_NAME}} -y" || { {
266290 echo "[ERROR] Failed to delete VM"
267291 exit 1
268- }
292+ }}
269293
270294 echo ""
271295 echo "[OK] VM $VM_NAME deleted successfully"
@@ -276,25 +300,25 @@ def log_completion(**context) -> None:
276300# Task: Check status
277301status_resources = BashOperator (
278302 task_id = "status_resources" ,
279- bash_command = """
303+ bash_command = f """
280304 echo "========================================"
281305 echo "Resource Status"
282306 echo "========================================"
283307
284- VM_NAME="{{ params.vm_name }}"
285- VM_PROFILE="{{ params.vm_profile }}"
308+ VM_NAME="{{{{ params.vm_name }} }}"
309+ VM_PROFILE="{{{{ params.vm_profile }} }}"
286310
287311 # List VMs matching profile
288312 echo "[INFO] VMs matching profile '$VM_PROFILE':"
289313 ssh -o StrictHostKeyChecking=no -o LogLevel=ERROR root@localhost \
290- "kcli list vm 2>/dev/null | grep -E 'Name|${VM_PROFILE}'" || echo "No VMs found"
314+ "{ KCLI } kcli list vm 2>/dev/null | grep -E 'Name|${{ VM_PROFILE} }'" || echo "No VMs found"
291315
292316 # If specific VM name provided, show details
293317 if [ -n "$VM_NAME" ]; then
294318 echo ""
295319 echo "[INFO] Details for VM: $VM_NAME"
296320 ssh -o StrictHostKeyChecking=no -o LogLevel=ERROR root@localhost \
297- "kcli info vm ${VM_NAME} 2>/dev/null" || echo "VM not found"
321+ "{ KCLI } kcli info vm ${{ VM_NAME} } 2>/dev/null" || echo "VM not found"
298322 fi
299323
300324 echo ""
0 commit comments