Definição da Topologia de Rede
O script automatiza a criação de redes virtuais utiliazndo libvirt, com base em uma configuração modular. A estrutura principal é definida por variáveis que podem ser ajsutadas conforme necessário:
# Estrutura: nome_da_rede: bridge_name,método_forward,cidr_inicial
# forward pode ser: nat, route ou isolated
REDES=(
'ansible_edge: asedgebr0,nat'
'smartcity_edge: scedgebr0,route'
'smartcity_cloud: sccloudbr0,nat'
'ansible_cloud: ascloudbr0,route'
)
# Mapeamento de interfaces por host e índice de interface
declare -A MAC_INTERFACES
MAC_INTERFACES=(
[ansible_edge]='EDGE1:1 EDGE2:1 EDGE3:1 HUB:1'
[smartcity_edge]='EDGE1:2 EDGE2:2 EDGE3:2 HUB:2'
[smartcity_cloud]='CLOUD:2 HUB:3'
[ansible_cloud]='CLOUD:1'
)
IP_BASE=192.168.124.2
MODO_PADRAO=route
# Armazena listas de endereços MAC gerados para cada host
declare -A LISTA_MACS=([EDGE1]="" [EDGE2]="" [EDGE3]="" [HUB]="" [CLOUD]="")
Ferramentas de Análise da Estrutura de Rede
Funções auxiliares extraem informações das strings de configuração:
function analisar_rede() {
local entrada="$1" chave="$2"
IFS=':, ' read -ra partes <<< "$entrada"
partes[2]=${partes[2]:-nat}
declare -A campos=([nome]=0 [bridge]=1 [modo]=2 [cidr]=3)
local indice=${campos[$chave]:-$chave}
echo "${partes[$indice]}"
}
function obter_nome_rede() {
echo "${1%:*}"
}
function buscar_config_rede() {
local var_lista="$1" nome_rede="$2"
for idx in $(eval echo "\${!$var_lista[@]}"); do
local valor=$(eval echo "\${$var_lista[$idx]}")
if [[ "${valor%:*}" == "$nome_rede" ]]; then
echo "$valor"
return
fi
done
}
function extrair_ip() {
grep -oE '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' <<< "$1" | head -n1
}
function obter_ip_rede() {
local modo=$(analisar_rede "$1" modo)
local candidato=$(extrair_ip "$modo")
local ip_final=${candidato:-$(analisar_rede "$1" cidr)}
extrair_ip "$ip_final"
}
function obter_modo_rede() {
local modo=$(analisar_rede "$1" modo)
local tem_ip=$(extrair_ip "$modo")
[[ -n "$tem_ip" ]] && modo=$(analisar_rede "$1" cidr)
echo "${modo:-nat}"
}
Geração de Endereços MAC Únicos
Utiliza Python para criar endereços MAC válidos no padrão reservado para virtualização (00:16:3e):
function gerar_mac() {
python3 <<EOF
import random
def novo_mac():
octetos = [0x00, 0x16, 0x3e,
random.randint(0x00, 0x7f),
random.randint(0x00, 0xff),
random.randint(0x00, 0xff)]
return ':'.join(f"{byte:02x}" for byte in octetos)
print(novo_mac())
EOF
}
Criação do Pool de Endereços MAC
Gera múltiplos MACs para cada host e salva no ambiente:
for host in "${!LISTA_MACS[@]}"; do
for i in {1..5}; do
LISTA_MACS[$host]+="$(gerar_mac) "
done
echo "export ${host}_MACS=\"${LISTA_MACS[$host]}\"" >> ~/.bashrc
done
Manipulação de Interfaces de Rede
Recupera os MACs associados a uma rede específica:
function listar_macs_rede() {
local mapa_mac="$1" nome_rede="$2"
local indices=$(eval echo "\${$mapa_mac[$nome_rede]}")
local resultado=()
for item in $indices; do
local host="${item%%:*}" idx="${item##*:}"
local lista_var="${host}_MACS"
local macs_brutos=$(eval echo "\${$lista_var}")
local mac_array=($macs_brutos)
resultado+=("${mac_array[idx]}")
done
echo "${resultado[@]}"
}
Geração de Nomes e IPs
Cria nomes de hosts padronizados e sequências de IP:
function gerar_lista_hosts() {
local mapa_mac="$1" rede="$2"
local indices=$(eval echo "\${$mapa_mac[$rede]}")
echo "${indices//:/.}" | tr '[:upper:]' '[:lower:]'
}
function incrementar_subrede() {
local subrede="${1%.*.*}.$((${1#*.*.}%.* + $2))).${1##*.}"
echo "$subrede"
}
function gerar_ips() {
local mapa_mac="$1" rede="$2" base_ip="$3"
local hosts=($(gerar_lista_hosts "$mapa_mac" "$rede"))
local ips=()
for i in "${!hosts[@]}"; do
ips+=("$(incrementar_subrede "$base_ip" "$i")")
done
echo "${ips[@]}"
}
Configuração DHCP
Gera entradas XML para alocação estática de IP via DHCP:
function elemento_dhcp() {
local mac="$1" nome_host="$2" dominio="$3" ip="$4"
local dom_real="${dominio/_/.}"
echo " <host mac=\"$mac\" name=\"${nome_host}.${dom_real}.com\" ip=\"$ip\"/>"
}
function gerar_blocos_dhcp() {
local mapa_mac="$1" rede="$2" ip_inicial="$3"
local hosts=($(gerar_lista_hosts "$mapa_mac" "$rede"))
local macs=($(listar_macs_rede "$mapa_mac" "$rede"))
local ips=($(gerar_ips "$mapa_mac" "$rede" "$ip_inicial"))
local saida=()
for i in "${!hosts[@]}"; do
saida+=("$(elemento_dhcp "${macs[i]}" "${hosts[i]}" "$rede" "${ips[i]}")")
done
IFS=$'\n'; echo "${saida[*]}"
}
Geração de XML de Rede
Produz arquivos XML para definição de redes no libvirt:
function criar_xml_rede() {
local nome_rede="$1" bridge="$2" ip_base="$3" dhcp="$4" modo="$5"
local mascara="255.255.255.0"
local gateway="${ip_base%.*}.1"
local faixa_inicio="${ip_base%.*}.2"
local faixa_fim="${ip_base%.*}.192"
mkdir -p /tmp/vir_network
cat > "/tmp/vir_network/$nome_rede.xml" << EOF
<network>
<name>$nome_rede</name>
<bridge name='$bridge'/>
<forward mode='$modo'/>
<ip address='$gateway' netmask='$mascara'>
<dhcp>
<range start='$faixa_inicio' end='$faixa_fim'/>
$dhcp
</dhcp>
</ip>
</network>
EOF
}
function gerar_todas_redes() {
local var_lista="$1" var_mac="$2" ip_base="$3" modo_global="$4"
ip_base=${ip_base:-192.168.124.2}
modo_global=${modo_global:-$MODO_PADRAO}
for indice in $(eval echo "\${!$var_lista[@]}"); do
local config=$(eval echo "\${$var_lista[$indice]}")
local nome_rede=$(obter_nome_rede "$config")
local bridge=$(analisar_rede "$config" bridge)
local ip_rede=$(incrementar_subrede "$ip_base" "$indice")
local ip_definido=$(obter_ip_rede "$config")
ip_rede=${ip_definido:-$ip_rede}
local blocos_dhcp=$(gerar_blocos_dhcp "$var_mac" "$nome_rede" "$ip_rede")
local modo_rede=$(obter_modo_rede "$config")
criar_xml_rede "$nome_rede" "$bridge" "$ip_rede" "$blocos_dhcp" "${modo_global:-$modo_rede}"
done
}
Operações de Rede
Comandos básicos para ativar e remover redes:
virsh net-create /tmp/vir_network/ansible_edge.xml
virsh net-list --all
virsh net-destroy ansible_edge
Configuração de Máquina Virtual
Define parâmetros iniciais para a VM:
NOME_VM=smartcity_cloud
MEMORIA_GB=10
CPUS=6
EMULADOR=/usr/libexec/qemu-kvm
DISCO_IMAGEM=/var/lib/libvirt/images/cloud.qcow2
Interface com Hardware Virtual
Recupera dados de interface de um host específico:
function obter_interfaces_host() {
local mapa_mac="$1" hostname="$2"
local maiusculo=${hostname^^}
local saida=()
for rede in $(eval echo "\${!$mapa_mac[@]}"); do
local valor=$(eval echo "\${$mapa_mac[$rede]}")
local correspondencia=$(grep -o "$maiusculo:[0-9]*" <<< "$valor")
[[ $? -ne 0 ]] && continue
local indice=${correspondencia#*:}
local mac_var="${maiusculo}_MACS"
local lista_mac=$(eval echo "\${$mac_var}")
local array_mac=($lista_mac)
local endereco_mac=${array_mac[indice]}
saida+=("$maiusculo $indice $rede $endereco_mac,")
done
IFS=$'\n' sorted=($(sort <<<"${saida[*]}")); unset IFS
echo "${sorted[@]}"
}
function obter_bridge_rede() {
local var_lista="$1" nome="$2"
for idx in $(eval echo "\${!$var_lista[@]}"); do
local valor=$(eval echo "\${$var_lista[$idx]}")
if [[ "${valor%:*}" == "$nome" ]]; then
echo "$(analisar_rede "$valor" bridge)"
return
fi
done
}
Geração de XML de Interface
Produz trechos XML para interfaces de rede da VM:
function criar_xml_interface() {
local tipo_rede="$1" mac="$2" bridge="$3"
local modelo=${MODELO_IFC:-rtl8139}
cat << EOF
<interface type='network'>
<mac address='$mac'/>
<source network='$tipo_rede' bridge='$bridge'/>
<model type='$modelo'/>
</interface>
EOF
}
function gerar_xml_interfaces() {
local mapa_mac="$1" host="$2" map_rede="$3"
local dados=($(obter_interfaces_host "$mapa_mac" "$host"))
local saida=()
IFS=',' read -ra itens <<< "${dados[*]}"
for item in "${itens[@]}"; do
[[ -z "$item" ]] && continue
local partes=($item)
local bridge=$(obter_bridge_rede "$map_rede" "${partes[2]}")
local xml=$(criar_xml_interface "${partes[2]}" "${partes[3]}" "$bridge")
saida+=("$xml")
done
IFS=$'\n'; echo "${saida[*]}"
}
Modelo Completo de VM
Gera o arquivo XML completo para definição da máquina virtual:
function gerar_xml_vm() {
local mapa_mac="$1" nome_host="$2" map_rede="$3"
[[ -z "$DISCO_ORIGEM" ]] && return 1
[[ -z "$NOME_VM" ]] && return 1
MEMORIA_GB=${MEMORIA_GB:-10}
CPUS=${CPUS:-4}
DISCO_IMAGEM=${DISCO_ORIGEM%/*}/$2.${DISCO_ORIGEM#*.}
cp "$DISCO_ORIGEM" "$DISCO_IMAGEM"
virt-edit -a "$DISCO_IMAGEM" /etc/hostname -e "s/.*/$2/"
local script_ifcfg=$(gerar_script_interface)
virt-edit -a "$DISCO_IMAGEM" /etc/sysconfig/network-scripts/ifcfg-ens192 -e 's/BOOTPROTO=.*/BOOTPROTO=dhcp/'
mkdir -p /tmp/vir_domain
local interfaces=$(gerar_xml_interfaces "$mapa_mac" "$nome_host" "$map_rede")
cat > "/tmp/vir_domain/${NOME_VM}.xml" << EOF
<domain type='kvm'>
<name>$NOME_VM</name>
<memory unit='GiB'>$MEMORIA_GB</memory>
<vcpu placement='static'>$CPUS</vcpu>
<os>
<type arch='x86_64' machine='pc-i440fx-rhel7.0.0'>hvm</type>
<boot dev='hd'/>
</os>
<features>
<acpi/><apic/>
</features>
<devices>
<emulator>$EMULADOR</emulator>
<disk type='file' device='disk'>
<driver name='qemu' type='qcow2'/>
<source file='$DISCO_IMAGEM'/>
<target dev='vda' bus='virtio'/>
</disk>
$interfaces
<serial type='pty'><target port='0'/></serial>
<console type='pty' target_type='serial'/>
</devices>
</domain>
EOF
}
Inicialização da VM
Fluxo final para criar e iniciar a instância:
HOST=cloud
DISCO_ORIGEM=/var/lib/libvirt/images/base-centos7.qcow2
NOME_VM=smartcity_$HOST
MEMORIA_GB=10
CPUS=6
gerar_xml_vm NET_MACS $HOST NET_NAMES
virsh define /tmp/vir_domain/${NOME_VM}.xml
virsh start $NOME_VM
virsh list --all
Redimensionamento de Disco
Expande automaticamente partições LVM dentro da VM:
function redimensionar_lvm() {
local alvo="$1" novo_tamanho="$2"
local disco_img=$(virsh dumpxml "$alvo" | xmllint --xpath "//disk/source/@file" - 2>/dev/null | cut -d'"' -f2)
local info_fs=$(virt-filesystems --long -h --all -a "$disco_img")
local particao=$(echo "$info_fs" | grep pv | tail -n1 | awk '{print $1}')
local volume_grupo=$(echo "$info_fs" | grep vg | tail -n1 | awk '{print $1}')
local volume_logico=$(echo "$info_fs" | grep "$volume_grupo/root" | awk '{print $1}')
local sistema_arq=$(echo "$info_fs" | grep filesystem | grep "$volume_logico" | awk '{print $3}')
local comando_fs=$( [[ "$sistema_arq" == "xfs" ]] && echo "xfs_growfs" || echo "resize2fs" )
local imagem_temp=${disco_img%/*}/temp_${RANDOM}.${disco_img##*.}
qemu-img create -f qcow2 -o preallocation=metadata "$imagem_temp" "$novo_tamanho"
virt-resize --expand "$particao" --LV-expand "$volume_logico" "$disco_img" "$imagem_temp"
cat > /tmp/ajuste.sh << SCRIPT
#!/bin/bash
lvextend -l +100%FREE $volume_logico
$comando_fs $volume_logico
SCRIPT
virt-customize -a "$imagem_temp" --run /tmp/ajuste.sh
mv "$imagem_temp" "$disco_img" && echo "Disco redimensionado com sucesso."
}
Injeção de Chaves SSH
Permite acesso automático sem senha:
function injetar_chave_ssh() {
local dominio="$1" usuario="${2:-root}" caminho_chave="${3:-~/.ssh/id_rsa.pub}"
virt-customize -d "$dominio" --ssh-inject "$usuario:file:$caminho_chave"
}