diff --git a/plugins/proxmox/proxmox.plugin.zsh b/plugins/proxmox/proxmox.plugin.zsh new file mode 100644 index 000000000..5b822086d --- /dev/null +++ b/plugins/proxmox/proxmox.plugin.zsh @@ -0,0 +1,326 @@ +#!/bin/zsh + +# Proxmox VE Oh My Zsh Plugin +# Description: Convenient functions for managing Proxmox VE via API + +# Configuration variables (set these in your .zshrc) +# export PROXMOX_HOST="your-proxmox-server.com" +# export PROXMOX_USER="root@pam" # or user@realm +# export PROXMOX_PASSWORD="your-password" +# export PROXMOX_NODE="your-node-name" # optional, defaults to first available node + +# Colors for output +PVE_GREEN='\033[0;32m' +PVE_RED='\033[0;31m' +PVE_YELLOW='\033[1;33m' +PVE_BLUE='\033[0;34m' +PVE_NC='\033[0m' # No Color + +# Check if required variables are set +_pve_check_config() { + if [[ -z "$PROXMOX_HOST" || -z "$PROXMOX_USER" || -z "$PROXMOX_PASSWORD" ]]; then + echo "${PVE_RED}Error: Please set PROXMOX_HOST, PROXMOX_USER, and PROXMOX_PASSWORD environment variables${PVE_NC}" + echo "Example:" + echo "export PROXMOX_HOST=\"proxmox.example.com\"" + echo "export PROXMOX_USER=\"root@pam\"" + echo "export PROXMOX_PASSWORD=\"your-password\"" + return 1 + fi + return 0 +} + +# Get authentication ticket +_pve_get_ticket() { + if ! _pve_check_config; then + return 1 + fi + + local response=$(curl -s -k -d "username=$PROXMOX_USER&password=$PROXMOX_PASSWORD" \ + "https://$PROXMOX_HOST:8006/api2/json/access/ticket" 2>/dev/null) + + if [[ $? -ne 0 ]] || [[ -z "$response" ]]; then + echo "${PVE_RED}Error: Failed to connect to Proxmox server${PVE_NC}" >&2 + return 1 + fi + + echo "$response" | grep -o '"ticket":"[^"]*"' | cut -d'"' -f4 +} + +# Get CSRF token +_pve_get_csrf() { + if ! _pve_check_config; then + return 1 + fi + + local response=$(curl -s -k -d "username=$PROXMOX_USER&password=$PROXMOX_PASSWORD" \ + "https://$PROXMOX_HOST:8006/api2/json/access/ticket" 2>/dev/null) + + echo "$response" | grep -o '"CSRFPreventionToken":"[^"]*"' | cut -d'"' -f4 +} + +# Get default node if not set +_pve_get_node() { + if [[ -n "$PROXMOX_NODE" ]]; then + echo "$PROXMOX_NODE" + return + fi + + local ticket=$(_pve_get_ticket) + if [[ -z "$ticket" ]]; then + return 1 + fi + + local nodes=$(curl -s -k -H "Authorization: PVEAuthCookie=$ticket" \ + "https://$PROXMOX_HOST:8006/api2/json/nodes" 2>/dev/null) + + echo "$nodes" | grep -o '"node":"[^"]*"' | head -1 | cut -d'"' -f4 +} + +# List all VMs +pve-list() { + echo "${PVE_BLUE}Fetching VM list...${PVE_NC}" + + local ticket=$(_pve_get_ticket) + if [[ -z "$ticket" ]]; then + return 1 + fi + + local node=$(_pve_get_node) + if [[ -z "$node" ]]; then + echo "${PVE_RED}Error: Could not determine node${PVE_NC}" + return 1 + fi + + local vms=$(curl -s -k -H "Authorization: PVEAuthCookie=$ticket" \ + "https://$PROXMOX_HOST:8006/api2/json/nodes/$node/qemu" 2>/dev/null) + + if [[ -z "$vms" ]]; then + echo "${PVE_RED}Error: Failed to fetch VM list${PVE_NC}" + return 1 + fi + + echo "${PVE_GREEN}VMs on node $node:${PVE_NC}" + echo "ID\tName\t\tStatus\t\tMemory\tCPUs" + echo "──────────────────────────────────────────────" + + echo "$vms" | grep -E '"(vmid|name|status|mem|cpus)"' | \ + awk -F'"' ' + /vmid/ { id = $4 } + /name/ { name = $4 } + /status/ { status = $4 } + /mem/ { mem = $4 } + /cpus/ { cpus = $4; printf "%s\t%-12s\t%s\t\t%sMB\t%s\n", id, name, status, mem, cpus } + ' +} + +# Start a VM +pve-start() { + if [[ -z "$1" ]]; then + echo "${PVE_RED}Usage: pve-start ${PVE_NC}" + return 1 + fi + + echo "${PVE_BLUE}Starting VM $1...${PVE_NC}" + + local ticket=$(_pve_get_ticket) + local csrf=$(_pve_get_csrf) + local node=$(_pve_get_node) + + if [[ -z "$ticket" || -z "$csrf" || -z "$node" ]]; then + return 1 + fi + + local result=$(curl -s -k -X POST \ + -H "Authorization: PVEAuthCookie=$ticket" \ + -H "CSRFPreventionToken: $csrf" \ + "https://$PROXMOX_HOST:8006/api2/json/nodes/$node/qemu/$1/status/start" 2>/dev/null) + + if echo "$result" | grep -q '"success":1'; then + echo "${PVE_GREEN}VM $1 start command sent successfully${PVE_NC}" + else + echo "${PVE_RED}Failed to start VM $1${PVE_NC}" + return 1 + fi +} + +# Stop a VM +pve-stop() { + if [[ -z "$1" ]]; then + echo "${PVE_RED}Usage: pve-stop ${PVE_NC}" + return 1 + fi + + echo "${PVE_BLUE}Stopping VM $1...${PVE_NC}" + + local ticket=$(_pve_get_ticket) + local csrf=$(_pve_get_csrf) + local node=$(_pve_get_node) + + if [[ -z "$ticket" || -z "$csrf" || -z "$node" ]]; then + return 1 + fi + + local result=$(curl -s -k -X POST \ + -H "Authorization: PVEAuthCookie=$ticket" \ + -H "CSRFPreventionToken: $csrf" \ + "https://$PROXMOX_HOST:8006/api2/json/nodes/$node/qemu/$1/status/stop" 2>/dev/null) + + if echo "$result" | grep -q '"success":1'; then + echo "${PVE_GREEN}VM $1 stop command sent successfully${PVE_NC}" + else + echo "${PVE_RED}Failed to stop VM $1${PVE_NC}" + return 1 + fi +} + +# Shutdown a VM gracefully +pve-shutdown() { + if [[ -z "$1" ]]; then + echo "${PVE_RED}Usage: pve-shutdown ${PVE_NC}" + return 1 + fi + + echo "${PVE_BLUE}Shutting down VM $1...${PVE_NC}" + + local ticket=$(_pve_get_ticket) + local csrf=$(_pve_get_csrf) + local node=$(_pve_get_node) + + if [[ -z "$ticket" || -z "$csrf" || -z "$node" ]]; then + return 1 + fi + + local result=$(curl -s -k -X POST \ + -H "Authorization: PVEAuthCookie=$ticket" \ + -H "CSRFPreventionToken: $csrf" \ + "https://$PROXMOX_HOST:8006/api2/json/nodes/$node/qemu/$1/status/shutdown" 2>/dev/null) + + if echo "$result" | grep -q '"success":1'; then + echo "${PVE_GREEN}VM $1 shutdown command sent successfully${PVE_NC}" + else + echo "${PVE_RED}Failed to shutdown VM $1${PVE_NC}" + return 1 + fi +} + +# Get VM status +pve-status() { + if [[ -z "$1" ]]; then + echo "${PVE_RED}Usage: pve-status ${PVE_NC}" + return 1 + fi + + local ticket=$(_pve_get_ticket) + local node=$(_pve_get_node) + + if [[ -z "$ticket" || -z "$node" ]]; then + return 1 + fi + + local status=$(curl -s -k -H "Authorization: PVEAuthCookie=$ticket" \ + "https://$PROXMOX_HOST:8006/api2/json/nodes/$node/qemu/$1/status/current" 2>/dev/null) + + if [[ -z "$status" ]]; then + echo "${PVE_RED}Failed to get status for VM $1${PVE_NC}" + return 1 + fi + + echo "${PVE_GREEN}Status for VM $1:${PVE_NC}" + echo "$status" | grep -E '"(status|name|cpus|mem|uptime)"' | \ + awk -F'"' ' + /status/ && !/qmpstatus/ { printf "Status: %s\n", $4 } + /name/ { printf "Name: %s\n", $4 } + /cpus/ { printf "CPUs: %s\n", $4 } + /mem/ { printf "Memory: %sMB\n", $4 } + /uptime/ { printf "Uptime: %s seconds\n", $4 } + ' +} + +# Show cluster status +pve-cluster() { + echo "${PVE_BLUE}Fetching cluster status...${PVE_NC}" + + local ticket=$(_pve_get_ticket) + if [[ -z "$ticket" ]]; then + return 1 + fi + + local nodes=$(curl -s -k -H "Authorization: PVEAuthCookie=$ticket" \ + "https://$PROXMOX_HOST:8006/api2/json/nodes" 2>/dev/null) + + if [[ -z "$nodes" ]]; then + echo "${PVE_RED}Failed to fetch cluster status${PVE_NC}" + return 1 + fi + + echo "${PVE_GREEN}Cluster Nodes:${PVE_NC}" + echo "Node\t\tStatus\tCPU%\tMemory%\tUptime" + echo "──────────────────────────────────────────────" + + echo "$nodes" | grep -E '"(node|status|cpu|mem|uptime)"' | \ + awk -F'"' ' + /node/ { node = $4 } + /status/ { status = $4 } + /cpu/ { cpu = sprintf("%.1f", $4*100) } + /mem/ { mem = $4 } + /uptime/ { uptime = $4; printf "%-12s\t%s\t%s%%\t%.1f%%\t%s\n", node, status, cpu, mem*100, uptime } + ' +} + +# Show help +pve-help() { + echo "${PVE_GREEN}Proxmox VE Oh My Zsh Plugin Commands:${PVE_NC}" + echo "" + echo "${PVE_BLUE}Configuration:${PVE_NC}" + echo " Set these environment variables in your .zshrc:" + echo " export PROXMOX_HOST=\"proxmox.example.com\"" + echo " export PROXMOX_USER=\"root@pam\"" + echo " export PROXMOX_PASSWORD=\"your-password\"" + echo " export PROXMOX_NODE=\"node-name\" # optional" + echo "" + echo "${PVE_BLUE}Commands:${PVE_NC}" + echo " pve-list List all VMs" + echo " pve-start Start a VM" + echo " pve-stop Force stop a VM" + echo " pve-shutdown Gracefully shutdown a VM" + echo " pve-status Get VM status" + echo " pve-cluster Show cluster status" + echo " pve-help Show this help" +} + +# Aliases for convenience +alias pvels='pve-list' +alias pvestart='pve-start' +alias pvestop='pve-stop' +alias pvestatus='pve-status' +alias pvecluster='pve-cluster' + +# Tab completion for VM IDs +_pve_vm_completion() { + if ! _pve_check_config >/dev/null 2>&1; then + return + fi + + local ticket=$(_pve_get_ticket 2>/dev/null) + if [[ -z "$ticket" ]]; then + return + fi + + local node=$(_pve_get_node 2>/dev/null) + if [[ -z "$node" ]]; then + return + fi + + local vms=$(curl -s -k -H "Authorization: PVEAuthCookie=$ticket" \ + "https://$PROXMOX_HOST:8006/api2/json/nodes/$node/qemu" 2>/dev/null) + + if [[ -n "$vms" ]]; then + local vmids=($(echo "$vms" | grep -o '"vmid":[0-9]*' | cut -d':' -f2)) + compadd $vmids + fi +} + +# Register completions +compdef _pve_vm_completion pve-start pve-stop pve-shutdown pve-status + +echo "${PVE_GREEN}Proxmox VE plugin loaded! Type 'pve-help' for usage information.${PVE_NC}"