#!/usr/bin/env bash # # drbit-new-client.sh — provisiona uma VM nova para um cliente DRBIT # # Roda no shell do HOST PROXMOX. Não depende de nenhuma VM/template # pré-existente: baixa a imagem cloud oficial do Ubuntu 24.04, cria a VM # do zero (qm create + qm importdisk) e anexa um drive de cloud-init que, # no primeiro boot, clona o repo da intranet e roda install.sh. # # Ver wiki/projetos/intranet/provisionamento-novo-cliente.md para o # contexto arquitetural completo. # # Uso: # ./drbit-new-client.sh --nome ecp [--ip 192.168.1.80 --gw 192.168.1.1] [opções] # # Por padrão a VM sobe com DHCP (igual ao modelo community-scripts — fica a # critério de quem provisiona definir IP fixo depois). Se --ip for informado, # a VM já sobe com IP estático. # # ⚠️ Para clientes que vão virar Domínio AD (Samba 4 AD DC), o IP PRECISA # ser fixo — defina --ip/--gw aqui, ou configure manualmente depois via # netplan (ver wiki/infraestrutura/template-vm.md) antes do Setup Wizard # provisionar o AD. # # Opções: # --nome obrigatório — vira hostname srv- e nome da VM # --ip opcional — IP estático da VM (ex: 192.168.1.80). Sem isso, usa DHCP # --gw default: 192.168.1.1 (só usado com --ip) # --vmid default: próximo livre (pvesh get /cluster/nextid) # --cores default: 2 # --ram default: 2048 # --disk default: 32 # --bridge default: vmbr0 # --storage default: local-lvm # --ssh-key default: ~/.ssh/id_drbit_lab.pub # --repo-url default: https://git.drbit.tec.br/drbit/Dashboard.git # Repo privado — passe a URL com o token de provisionamento # embutido (usuário drbit-provisioning, token read-only): # https://drbit-provisioning:@git.drbit.tec.br/drbit/Dashboard.git # set -euo pipefail # ── defaults ────────────────────────────────────────────────────────────── GW="192.168.1.1" VMID="" CORES=2 RAM=2048 DISK=32 BRIDGE="vmbr0" STORAGE="local-lvm" SSH_KEY_FILE="$HOME/.ssh/id_drbit_lab.pub" UBUNTU_RELEASE="noble" IMG_URL="https://cloud-images.ubuntu.com/releases/${UBUNTU_RELEASE}/release/ubuntu-24.04-server-cloudimg-amd64.img" IMG_CACHE="/var/lib/vz/template/iso/ubuntu-24.04-server-cloudimg-amd64.img" REPO_URL="https://git.drbit.tec.br/drbit/Dashboard.git" NOME="" IP="" # ── helpers ─────────────────────────────────────────────────────────────── msg_info() { echo -e " \033[1;34m➜\033[0m $*"; } msg_ok() { echo -e " \033[1;32m✓\033[0m $*"; } msg_error() { echo -e " \033[1;31m✗\033[0m $*" >&2; } # ── parse args ──────────────────────────────────────────────────────────── while [[ $# -gt 0 ]]; do case "$1" in --nome) NOME="$2"; shift 2 ;; --ip) IP="$2"; shift 2 ;; --gw) GW="$2"; shift 2 ;; --vmid) VMID="$2"; shift 2 ;; --cores) CORES="$2"; shift 2 ;; --ram) RAM="$2"; shift 2 ;; --disk) DISK="$2"; shift 2 ;; --bridge) BRIDGE="$2"; shift 2 ;; --storage) STORAGE="$2"; shift 2 ;; --ssh-key) SSH_KEY_FILE="$2"; shift 2 ;; --repo-url) REPO_URL="$2"; shift 2 ;; *) msg_error "Argumento desconhecido: $1"; exit 1 ;; esac done # ── validações ──────────────────────────────────────────────────────────── if [[ -z "$NOME" ]]; then msg_error "Uso: $0 --nome [--ip --gw ] [opções]" exit 1 fi if ! command -v qm &>/dev/null; then msg_error "Comando 'qm' não encontrado — este script roda no host Proxmox." exit 1 fi if [[ ! -f "$SSH_KEY_FILE" ]]; then msg_error "Chave pública SSH não encontrada em: $SSH_KEY_FILE (use --ssh-key)" exit 1 fi HOSTNAME="srv-${NOME}" if [[ -z "$VMID" ]]; then VMID=$(pvesh get /cluster/nextid) fi if [[ -n "$IP" ]]; then msg_info "Provisionando VM '$HOSTNAME' (VMID $VMID, IP estático $IP)" else msg_info "Provisionando VM '$HOSTNAME' (VMID $VMID, IP via DHCP)" fi # ── 1. cache da imagem cloud oficial ───────────────────────────────────── if [[ -f "$IMG_CACHE" ]]; then msg_ok "Imagem Ubuntu 24.04 cloud já em cache: $IMG_CACHE" else msg_info "Baixando imagem Ubuntu 24.04 cloud (cloud-images.ubuntu.com)..." mkdir -p "$(dirname "$IMG_CACHE")" curl -fsSL -o "$IMG_CACHE" "$IMG_URL" msg_ok "Imagem baixada: $IMG_CACHE" fi # ── 2. garantir que a storage 'local' suporta snippets ─────────────────── CONTENT=$(awk '/^dir: local$/{f=1; next} f && /^\tcontent/ {print $2; exit} /^[a-z]/{f=0}' /etc/pve/storage.cfg) if [[ "$CONTENT" != *snippets* ]]; then msg_info "Habilitando 'snippets' na storage local..." pvesm set local --content "${CONTENT},snippets" msg_ok "Storage 'local' agora aceita snippets" else msg_ok "Storage 'local' já aceita snippets" fi # ── 3. gerar cloud-init user-data ──────────────────────────────────────── SSH_PUBKEY=$(cat "$SSH_KEY_FILE") SNIPPET_PATH="/var/lib/vz/snippets/drbit-${VMID}.yaml" mkdir -p /var/lib/vz/snippets msg_info "Gerando cloud-init user-data ($SNIPPET_PATH)..." cat > "$SNIPPET_PATH" </dev/null 2>&1; do sleep 5; done - git clone ${REPO_URL} /opt/drbit-intranet - bash /opt/drbit-intranet/install.sh EOF msg_ok "user-data gerado" # ── 4. criar VM ─────────────────────────────────────────────────────────── msg_info "Criando VM $VMID..." qm create "$VMID" \ -name "$HOSTNAME" \ -memory "$RAM" \ -cores "$CORES" \ -net0 "virtio,bridge=${BRIDGE}" \ -ostype l26 \ -scsihw virtio-scsi-pci \ -agent 1 msg_ok "VM criada" # ── 5. importar disco ──────────────────────────────────────────────────── msg_info "Importando imagem como disco..." qm importdisk "$VMID" "$IMG_CACHE" "$STORAGE" # o disco importado fica como "unused0" na config da VM DISK_REF=$(qm config "$VMID" | grep -oP '^unused0:\s*\K.*') if [[ -z "$DISK_REF" ]]; then msg_error "Não foi possível identificar o disco importado (unused0 não encontrado)." qm config "$VMID" exit 1 fi msg_info "Anexando disco ($DISK_REF)..." qm set "$VMID" -scsi0 "$DISK_REF" msg_ok "Disco anexado" msg_info "Redimensionando disco para ${DISK}G..." qm resize "$VMID" scsi0 "${DISK}G" msg_ok "Disco redimensionado (cloud-init faz growpart/resizefs no primeiro boot)" # ── 6. cloud-init drive + boot + rede ──────────────────────────────────── if [[ -n "$IP" ]]; then IPCONFIG="ip=${IP}/24,gw=${GW}" msg_info "Configurando cloud-init e rede (IP estático ${IP}/24, gw ${GW})..." else IPCONFIG="ip=dhcp" msg_info "Configurando cloud-init e rede (DHCP)..." fi qm set "$VMID" \ -ide2 "${STORAGE}:cloudinit" \ -boot order=scsi0 \ -serial0 socket -vga serial0 \ -ipconfig0 "$IPCONFIG" \ -nameserver "1.1.1.1 8.8.8.8" \ -cicustom "user=local:snippets/drbit-${VMID}.yaml" msg_ok "Cloud-init configurado" # ── 7. iniciar ──────────────────────────────────────────────────────────── msg_info "Iniciando VM..." qm start "$VMID" msg_ok "VM iniciada" # ── 8. descobrir IP (DHCP) e aguardar Setup Wizard responder ───────────── FINAL_IP="$IP" if [[ -z "$FINAL_IP" ]]; then msg_info "Aguardando guest agent informar o IP (DHCP, até 5min)..." DEADLINE=$((SECONDS + 300)) while (( SECONDS < DEADLINE )); do FINAL_IP=$(qm guest cmd "$VMID" network-get-interfaces 2>/dev/null \ | grep -oP '"ip-address"\s*:\s*"\K[0-9.]+' \ | grep -vE '^(127\.|169\.254\.)' \ | head -1 || true) [[ -n "$FINAL_IP" ]] && break sleep 10 done if [[ -z "$FINAL_IP" ]]; then msg_error "Não foi possível descobrir o IP via guest agent. Verifique com: qm guest cmd $VMID network-get-interfaces" exit 1 fi msg_ok "IP detectado via DHCP: $FINAL_IP" fi msg_info "Aguardando o Setup Wizard subir em http://${FINAL_IP}/setup (até 10min)..." DEADLINE=$((SECONDS + 600)) while (( SECONDS < DEADLINE )); do if curl -sf -o /dev/null "http://${FINAL_IP}/setup"; then msg_ok "Setup Wizard respondendo!" break fi sleep 10 done if ! curl -sf -o /dev/null "http://${FINAL_IP}/setup"; then msg_error "Timeout aguardando o Setup Wizard. Verifique a VM (qm terminal $VMID) e o cloud-init (/var/log/cloud-init-output.log)." exit 1 fi # ── 9. resumo ───────────────────────────────────────────────────────────── echo "" echo "──────────────────────────────────────────" echo " VM $VMID ($HOSTNAME) pronta!" echo "" echo " Acesse o Setup Wizard:" echo " http://${FINAL_IP}/setup" echo "" echo " SSH: ssh drbit@${FINAL_IP} (chave $SSH_KEY_FILE ou senha b1tadmin)" echo " ⚠️ Trocar a senha padrão 'b1tadmin' em produção." if [[ -z "$IP" ]]; then echo "" echo " ⚠️ IP via DHCP ($FINAL_IP). Para Domínio AD (Samba 4 AD DC), defina" echo " IP fixo antes de provisionar o AD pelo Setup Wizard:" echo " editar /etc/netplan/50-cloud-init.yaml (ou criar 99-static.yaml)" echo " → sudo netplan apply" fi echo "──────────────────────────────────────────"