Posted on
Artificial Intelligence

Artificial Intelligence for KVM Administration

Author
  • User
    linuxbash
    Posts by this author
    Posts by this author

Artificial Intelligence for KVM Administration: Smarter Bash for libvirt and QEMU

If you spend your days juggling virsh, XML snippets, snapshots, and capacity spreadsheets, you know that keeping a KVM host tidy is half muscle memory and half war‑stories. The twist: AI can turn natural language into safe virsh commands, summarize health state from noisy logs, and point out capacity risks before they bite. In this post, you’ll learn how to plug AI into your Bash workflow to make KVM administration faster, safer, and more explainable.

What you’ll get:

  • Why AI is a good fit for KVM ops today

  • A minimal, reproducible setup (Debian/Ubuntu, RHEL/Fedora, SUSE)

  • 3–5 actionable, real-world scripts that put AI to work for libvirt/QEMU

  • A practical, no‑nonsense wrap‑up with next steps

Why this matters

  • KVM is everywhere. libvirt, QEMU, and virt-install are the backbone for everything from homelabs to enterprise private clouds. That’s a lot of repetitive CLI.

  • The data is already there. domstats, dmesg, sysstat—your host is a telemetry goldmine. AI can connect the dots and suggest what to do next.

  • LLMs are operable from Bash. With a local model (e.g., via Ollama) or a cloud API, you can wrap “explain and plan” logic around commands you already trust.

  • You stay in control. Treat AI as a planning/explanation engine. Keep guardrails on execution and require confirmation. No blind runs.

Prerequisites and installation

The examples below assume a Linux host with KVM/libvirt and a local or remote LLM endpoint. We’ll install:

  • KVM stack: qemu-kvm, libvirt, virt-install, virt-manager, bridge-utils

  • Helpers: jq, bc, sysstat, curl, genisoimage, cloud-utils

  • Optional: Ollama for local models

Note: Use sudo where needed.

Debian/Ubuntu (apt)

sudo apt update
sudo apt install -y \
  qemu-kvm libvirt-daemon-system libvirt-clients virtinst virt-manager \
  bridge-utils cloud-utils genisoimage jq bc sysstat curl

# Enable and start services
sudo systemctl enable --now libvirtd virtlogd

# Add your user to libvirt and kvm groups
sudo usermod -aG libvirt,kvm "$USER"

# Activate new groups in current shell (or log out/in)
newgrp libvirt

(Optional) Enable sysstat collection so sar has historical data:

sudo sed -i 's/^ENABLED="false"/ENABLED="true"/' /etc/default/sysstat 2>/dev/null || true
sudo systemctl enable --now sysstat

RHEL/CentOS/Fedora (dnf)

sudo dnf install -y @virtualization libvirt qemu-kvm virt-install virt-manager \
  bridge-utils genisoimage cloud-utils-growpart jq bc sysstat curl

sudo systemctl enable --now libvirtd virtlogd

sudo usermod -aG libvirt,kvm "$USER"
newgrp libvirt

sudo systemctl enable --now sysstat

openSUSE/SLES (zypper)

sudo zypper install -y \
  qemu-kvm libvirt libvirt-daemon libvirt-client virt-install virt-manager \
  bridge-utils genisoimage cloud-utils jq bc sysstat curl

sudo systemctl enable --now libvirtd virtlogd

sudo usermod -aG libvirt,kvm "$USER"
newgrp libvirt

sudo systemctl enable --now sysstat

Optional: Local LLM with Ollama

This lets you run prompts without sending data to a cloud service.

curl -fsSL https://ollama.com/install.sh | sh
ollama pull llama3:8b

By default, Ollama listens on http://127.0.0.1:11434. You can swap in a different model later.

Security note: Always review what you paste from AI. Every execution step below has a dry‑run and a confirmation gate.


Actionable #1: Natural language → safe virsh (nl2virsh)

Turn “what I mean” into a vetted virsh command, with an explanation, a risk summary, and a confirmation prompt before anything runs.

Save as nl2virsh:

#!/usr/bin/env bash
# nl2virsh: Translate natural language into a safe virsh command via an LLM.
# Requires: jq, curl, libvirt, and either Ollama (default) or OPENAI_API_KEY set.

set -euo pipefail

MODEL="${MODEL:-llama3:8b}"        # Ollama model
OPENAI_MODEL="${OPENAI_MODEL:-gpt-4o-mini}"
LLM_PROVIDER="${LLM_PROVIDER:-auto}"  # auto|ollama|openai

allowlist_regex='^(virsh (list( --all)?|dominfo|start|shutdown|reboot|suspend|resume|autostart( --(set|unset))? [A-Za-z0-9._:-]+|autostart [A-Za-z0-9._:-]+|snapshot-(create-as|revert|list)( .*)?|domstats( .*)?|domblkinfo|domblkstat( .*)?|cpu-stats( .*)?|setvcpus( .*)?|setmem( .*)?))$'

usage() {
  echo "Usage: $0 \"describe the action, e.g. 'snapshot vm dev1 and enable autostart'\""
  echo "Env: LLM_PROVIDER=auto|ollama|openai, MODEL (ollama), OPENAI_MODEL, OPENAI_API_KEY"
}

if [[ $# -lt 1 ]]; then usage; exit 2; fi
PROMPT="$*"

system_prompt='You are an expert Linux KVM/libvirt assistant. 

- Output STRICT JSON with keys: cmd, explain, risk.

- Propose exactly ONE safe virsh command that is idempotent when possible.

- Prefer read-only or configuration-only flags over live-modifying ones unless explicitly requested.

- Never power off or destroy unless user asked clearly.

- When setting sizes, use KiB units for setmem and include --config.

- Example JSON: {"cmd":"virsh autostart dev1","explain":"...","risk":"low"}'

user_prompt="User request: \"$PROMPT\"
Constraints:

- Return only JSON.

- Limit to a single virsh command from this allowlist: list, dominfo, start, shutdown, reboot, suspend, resume, autostart, snapshot-create-as, snapshot-revert, snapshot-list, domstats, domblkinfo, domblkstat, cpu-stats, setvcpus, setmem (with --config).

- If action is ambiguous, pick the safest read-only command that helps clarify the next step (e.g., dominfo)."

call_ollama() {
  curl -fsS http://127.0.0.1:11434/api/generate \
    -H "Content-Type: application/json" \
    -d "$(jq -nc --arg m "$MODEL" --arg p "$system_prompt\n\n$user_prompt" \
      '{model:$m, prompt:$p, stream:false, options:{temperature:0.1}}')" \
    | jq -r '.response'
}

call_openai() {
  if [[ -z "${OPENAI_API_KEY:-}" ]]; then
    echo "OPENAI_API_KEY not set." >&2; exit 1
  fi
  curl -fsS https://api.openai.com/v1/chat/completions \
    -H "Authorization: Bearer $OPENAI_API_KEY" \
    -H "Content-Type: application/json" \
    -d "$(jq -nc --arg m "$OPENAI_MODEL" \
      --arg sp "$system_prompt" --arg up "$user_prompt" \
      '{model:$m, response_format:{type:"json_object"},
        messages:[{role:"system",content:$sp},{role:"user",content:$up}],
        temperature:0.1}')" \
    | jq -r '.choices[0].message.content'
}

detect_provider() {
  if [[ "$LLM_PROVIDER" == "ollama" ]]; then echo "ollama"; return; fi
  if [[ "$LLM_PROVIDER" == "openai" ]]; then echo "openai"; return; fi
  # auto
  if curl -fsS http://127.0.0.1:11434/api/tags >/dev/null 2>&1; then
    echo "ollama"; return
  fi
  if [[ -n "${OPENAI_API_KEY:-}" ]]; then
    echo "openai"; return
  fi
  echo "none"
}

prov="$(detect_provider)"
if [[ "$prov" == "none" ]]; then
  echo "No LLM available. Install Ollama or set OPENAI_API_KEY." >&2
  exit 1
fi

resp="$(
  if [[ "$prov" == "ollama" ]]; then call_ollama
  else call_openai
  fi
)"

cmd="$(jq -r '.cmd // empty' <<<"$resp" 2>/dev/null || true)"
explain="$(jq -r '.explain // ""' <<<"$resp" 2>/dev/null || true)"
risk="$(jq -r '.risk // ""' <<<"$resp" 2>/dev/null || true)"

if [[ -z "$cmd" ]]; then
  echo "Model did not return a command. Raw output:" >&2
  echo "$resp" >&2
  exit 1
fi

if ! [[ "$cmd" =~ $allowlist_regex ]]; then
  echo "Refusing to run a non-allowlisted command:"
  echo "  $cmd"
  echo "Explanation: $explain"
  echo "Risk: $risk"
  exit 1
fi

echo "Proposed: $cmd"
[[ -n "$explain" ]] && echo "Explain: $explain"
[[ -n "$risk" ]] && echo "Risk: $risk"
read -r -p "Execute? [y/N] " ans
if [[ "$ans" =~ ^[Yy]$ ]]; then
  eval "$cmd"
else
  echo "Aborted."
fi

Example:

nl2virsh "For VM dev1, create a snapshot named pre-upgrade and enable autostart"

You’ll see the proposed command (e.g., “virsh snapshot-create-as dev1 pre-upgrade --atomic” or “virsh autostart dev1”) plus an explanation and risk rating, and a confirmation prompt.

Tip: For memory changes, the prompt instructs the model to use KiB for setmem and --config (persistent, not live). You can always refuse to run and copy the command to tweak.


Actionable #2: AI‑assisted capacity planning from live inventory

This script collects a quick inventory and host resources, then asks the LLM to:

  • summarize utilization,

  • flag overcommit risks,

  • suggest consolidation or tuning steps.

Save as kvm-capacity-report.sh:

#!/usr/bin/env bash
# kvm-capacity-report.sh: Summarize host & VM inventory, get AI capacity guidance.
# Requires: jq, curl, and an LLM (Ollama or OPENAI_API_KEY), plus libvirt.

set -euo pipefail

MODEL="${MODEL:-llama3:8b}"
OPENAI_MODEL="${OPENAI_MODEL:-gpt-4o-mini}"
LLM_PROVIDER="${LLM_PROVIDER:-auto}"

detect_provider() {
  if [[ "$LLM_PROVIDER" == "ollama" ]]; then echo "ollama"; return; fi
  if [[ "$LLM_PROVIDER" == "openai" ]]; then echo "openai"; return; fi
  if curl -fsS http://127.0.0.1:11434/api/tags >/dev/null 2>&1; then echo "ollama"; return; fi
  if [[ -n "${OPENAI_API_KEY:-}" ]]; then echo "openai"; return; fi
  echo "none"
}

call_ollama() {
  curl -fsS http://127.0.0.1:11434/api/generate \
    -H "Content-Type: application/json" \
    -d "$(jq -nc --arg m "$MODEL" --arg p "$1" \
      '{model:$m, prompt:$p, stream:false, options:{temperature:0.1}}')" \
  | jq -r '.response'
}

call_openai() {
  curl -fsS https://api.openai.com/v1/chat/completions \
    -H "Authorization: Bearer $OPENAI_API_KEY" \
    -H "Content-Type: application/json" \
    -d "$(jq -nc --arg m "$OPENAI_MODEL" --arg p "$1" \
      '{model:$m, messages:[{role:"user",content:$p}], temperature:0.1}')" \
  | jq -r '.choices[0].message.content'
}

prov="$(detect_provider)"
if [[ "$prov" == "none" ]]; then
  echo "No LLM available. Install Ollama or set OPENAI_API_KEY." >&2
  exit 1
fi

# Collect inventory
HOST="$(hostnamectl 2>/dev/null || echo "$(hostname)")"
CPU="$(lscpu 2>/dev/null || echo N/A)"
MEM="$(free -m 2>/dev/null || echo N/A)"
DISK="$(lsblk -o NAME,FSTYPE,SIZE,TYPE,MOUNTPOINT 2>/dev/null || echo N/A)"
POOLS="$(virsh pool-list --all 2>/dev/null || echo N/A)"
VMS="$(virsh list --all 2>/dev/null || echo N/A)"

vm_details=""
while read -r vm; do
  [[ -z "$vm" ]] && continue
  d1="$(virsh dominfo "$vm" 2>/dev/null || true)"
  ds="$(virsh domstats "$vm" --cpu --balloon --block 2>/dev/null || true)"
  vm_details+=$'\n'"### $vm"$'\n'"$d1"$'\n'"$ds"$'\n'
done < <(virsh list --name 2>/dev/null)

prompt=$(cat <<'EOF'
You are a senior capacity planner. Given host & VM inventory, write:
1) A 5–10 line summary of current capacity and notable risks (CPU, RAM, storage).
2) A short list of concrete actions (e.g., adjust vCPU counts, memory ballooning, storage pool growth).
3) A quick “next 30 days” risk outlook based on what you see (no hallucinations; stick to data).
Make it concise and directly actionable.
EOF
)

report=$(
  cat <<EOF
===== HOST =====
$HOST

===== CPU =====
$CPU

===== MEMORY =====
$MEM

===== DISK =====
$DISK

===== STORAGE POOLS =====
$POOLS

===== VMS =====
$VMS

===== PER-VM DETAILS =====
$vm_details
EOF
)

final_prompt="$prompt

DATA START
$report
DATA END
"

if [[ "$prov" == "ollama" ]]; then
  call_ollama "$final_prompt"
else
  call_openai "$final_prompt"
fi

Run it:

bash kvm-capacity-report.sh

You’ll get a short, targeted plan like “Pool default is at 87%—expand by 200G or migrate volumes; dev VMs overcommitting vCPU 4:1—reduce idle vCPUs; ballooning unused RAM on qa-*; watch disk IO on vm-logs.”

Pro tip: Pipe this output into a ticket or run it nightly and diff changes.


Actionable #3: Health check summarizer for KVM/libvirt

This script grabs common pain points—kernel modules, libvirtd, bridges, and recent KVM errors—and asks the AI for a prioritized troubleshooting checklist.

Save as kvm-health.sh:

#!/usr/bin/env bash
# kvm-health.sh: Gather KVM/libvirt host health signals, get AI triage steps.
# Requires: curl, jq, LLM (Ollama or OPENAI_API_KEY)

set -euo pipefail

MODEL="${MODEL:-llama3:8b}"
OPENAI_MODEL="${OPENAI_MODEL:-gpt-4o-mini}"
LLM_PROVIDER="${LLM_PROVIDER:-auto}"

detect_provider() {
  if [[ "$LLM_PROVIDER" == "ollama" ]]; then echo "ollama"; return; fi
  if [[ "$LLM_PROVIDER" == "openai" ]]; then echo "openai"; return; fi
  if curl -fsS http://127.0.0.1:11434/api/tags >/dev/null 2>&1; then echo "ollama"; return; fi
  if [[ -n "${OPENAI_API_KEY:-}" ]]; then echo "openai"; return; fi
  echo "none"
}

call_ollama() {
  curl -fsS http://127.0.0.1:11434/api/generate \
    -H "Content-Type: application/json" \
    -d "$(jq -nc --arg m "$MODEL" --arg p "$1" \
      '{model:$m, prompt:$p, stream:false, options:{temperature:0.1}}')" \
  | jq -r '.response'
}

call_openai() {
  curl -fsS https://api.openai.com/v1/chat/completions \
    -H "Authorization: Bearer $OPENAI_API_KEY" \
    -H "Content-Type: application/json" \
    -d "$(jq -nc --arg m "$OPENAI_MODEL" --arg p "$1" \
      '{model:$m, messages:[{role:"user",content:$p}], temperature:0.1}')" \
  | jq -r '.choices[0].message.content'
}

prov="$(detect_provider)"
if [[ "$prov" == "none" ]]; then
  echo "No LLM available. Install Ollama or set OPENAI_API_KEY." >&2
  exit 1
fi

mod="$(lsmod | egrep 'kvm|vhost' || true)"
svc="$(systemctl --no-pager --full status libvirtd 2>&1 | tail -n +1 | sed -n '1,80p')"
bridges="$(ip -brief link 2>/dev/null | egrep 'virbr|br-|bridge' || true; \
           brctl show 2>/dev/null || true; \
           nmcli connection show 2>/dev/null | egrep 'bridge|virbr|br-' || true)"
err="$(dmesg --ctime --level=err,warn 2>/dev/null | egrep -i 'kvm|virtio|vfio|iommu|qemu' | tail -n 200 || true)"
logs="$(journalctl -u libvirtd --no-pager -n 200 2>/dev/null || true)"

prompt=$(cat <<'EOF'
You are diagnosing a KVM/libvirt host. Based on the signals below:

- List the top 5 issues or risks, each with a one-line fix or check.

- If things look healthy, say so and suggest one preventive hardening step.

- Keep it terse and CLI-oriented.
EOF
)

blob=$(
  cat <<EOF
===== MODULES (kvm, vhost) =====
$mod

===== LIBVIRTD STATUS =====
$svc

===== BRIDGES / NETWORK =====
$bridges

===== KERNEL ERR/WARN (KVM-related) =====
$err

===== LIBVIRTD LOGS (last 200 lines) =====
$logs
EOF
)

final_prompt="$prompt

DATA START
$blob
DATA END
"

if [[ "$prov" == "ollama" ]]; then
  call_ollama "$final_prompt"
else
  call_openai "$final_prompt"
fi

Run it:

bash kvm-health.sh

You’ll get a short triage list like “vhost_net missing—modprobe vhost_net; default NAT network down—virsh net-start default; IOMMU warnings—check kernel params; libvirtd socket errors—reset lingering PID, etc.”


Actionable #4 (bonus): Explain a libvirt XML snippet

When you inherit a host with mysterious XML tweaks, ask the AI to explain them in plain terms.

Example one-liner:

virsh dumpxml myvm | sed -n '1,200p' | awk 'NR<=200' > /tmp/myvm.xml
cat /tmp/myvm.xml | curl -fsS http://127.0.0.1:11434/api/generate \
  -H "Content-Type: application/json" \
  -d "$(jq -nc --arg m "llama3:8b" \
        --arg p "Explain this libvirt domain XML for a Linux admin. Highlight CPUs, NUMA, disk cache modes, and any performance/safety implications. Keep it under 20 lines.\n\n$(cat /tmp/myvm.xml)" \
        '{model:$m, prompt:$p, stream:false, options:{temperature:0.1}}')" \
  | jq -r '.response'

Swap in your favorite model or use OPENAI_API_KEY with the chat completions endpoint.


Real-world example end-to-end

Scenario: You’re about to patch dev1. You want a snapshot and to ensure it autostarts after reboot.

  • Ask for the command:

    nl2virsh "Create an atomic snapshot named pre-patch of vm dev1, then make sure it autostarts"
    
  • Confirm and execute if it looks right.

  • After patching, run a capacity report:

    bash kvm-capacity-report.sh
    

    Use the recommendations to clean up old snapshots or right-size memory.

  • If errors crop up, run:

    bash kvm-health.sh
    

    Apply the top fix and rerun.


Best practices and safety

  • Keep execution behind a confirmation gate. AI plans; you decide.

  • Constrain commands with allowlists. Expand slowly as you gain trust.

  • Prefer persistent config changes (e.g., setmem --config) over live edits unless needed.

  • Log every AI suggestion and the final command you ran for auditability.

  • For sensitive environments, use a local LLM (Ollama) and scrub data in prompts.


Conclusion and next steps

AI won’t replace your KVM skills—it accelerates them. Start by wrapping common virsh tasks with nl2virsh, use capacity and health summaries to surface what matters, and incrementally let AI draft plans you confirm. Your future self will thank you the next time you’re juggling maintenance windows.

Call to action:

  • Install the prerequisites (apt/dnf/zypper above).

  • Drop the three scripts into /usr/local/bin and make them executable.

  • Start with read-only prompts (“show dominfo, list snapshots”), then expand.

  • Consider running Ollama locally for privacy and speed.

Have a favorite KVM task you want AI to handle? Try writing a prompt around your existing Bash and add the same allowlist + confirm pattern.