Refactor VM provisioning pipeline into focused services and add IP pool

management
This commit is contained in:
baodev0
2026-05-29 12:03:35 +07:00
parent 6bb4a47bfb
commit 7ae237ee9d
32 changed files with 1170 additions and 1200 deletions
+92 -69
View File
@@ -1,85 +1,108 @@
# assignIPLinux.ps1
# Deploy to: C:\CLISave\AssignIP\assignIPLinux.ps1 on the vSphere Windows host
#
# Usage: powershell -ExecutionPolicy Bypass -NonInteractive -File assignIPLinux.ps1 \
# <vm_id> <ip> <gateway> <cidr> <vcenter_host> <vcenter_user> <vcenter_password> <guest_password>
#
# Output: SUCCESS | FAIL (exactly one line)
param(
[string]$VCenter,
[string]$VCUser,
[string]$VCPass,
[string]$VMName,
[string]$IP,
[int]$MaskBits,
[string]$Gateway,
[string]$DNS,
[string]$GuestUser,
[string]$GuestPassword
[Parameter(Mandatory=$true)][string]$VmId,
[Parameter(Mandatory=$true)][string]$Ip,
[Parameter(Mandatory=$true)][string]$Gateway,
[Parameter(Mandatory=$true)][string]$Cidr,
[Parameter(Mandatory=$true)][string]$VCenterHost,
[Parameter(Mandatory=$true)][string]$VCenterUser,
[Parameter(Mandatory=$true)][string]$VCenterPassword,
[Parameter(Mandatory=$true)][string]$GuestPassword
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
try {
Import-Module VMware.PowerCLI -ErrorAction Stop
Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false | Out-Null
Import-Module VMware.VimAutomation.Core -ErrorAction Stop
Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false | Out-Null
Set-PowerCLIConfiguration -Scope User -ParticipateInCEIP $false -Confirm:$false | Out-Null
Connect-VIServer -Server $VCenter -User $VCUser -Password $VCPass | Out-Null
Connect-VIServer -Server $VCenterHost -User $VCenterUser -Password $VCenterPassword -Force -ErrorAction Stop | Out-Null
$vm = Get-VM -Name $VMName -ErrorAction Stop
$vm = Get-VM -Id "VirtualMachine-$VmId" -ErrorAction Stop
if ($vm.PowerState -ne 'PoweredOn') {
throw "VM is powered off"
}
# Bash script executed inside the Linux guest via VMware Tools (Invoke-VMScript)
$bashScript = @"
#!/bin/bash
set -e
if ($vm.ExtensionData.Guest.ToolsStatus -notin @('toolsOk','toolsOld')) {
throw "VMware Tools not ready"
}
# Find the first usable non-loopback interface
IFACE=`$(ip -o link show | awk -F': ' '{print `$2}' | grep -Ev '^(lo|vir|docker|br-)' | head -1)
if [ -z "`$IFACE" ]; then
echo "ERROR: no usable network interface found" >&2
exit 1
fi
$sec = ConvertTo-SecureString $GuestPassword -AsPlainText -Force
$cred = New-Object pscredential($GuestUser, $sec)
# Apply static IP immediately (survives until reboot without persistence layer)
ip addr flush dev `$IFACE 2>/dev/null || true
ip addr add $Ip/$Cidr dev `$IFACE
ip link set `$IFACE up
ip route del default 2>/dev/null || true
ip route add default via $Gateway dev `$IFACE
# ✅ Detect interface chuẩn (chỉ lấy 1 dòng)
$detectScript = "ip route | awk '/default/ {print \$5; exit}'"
$ifaceRes = Invoke-VMScript -VM $vm -ScriptText $detectScript -GuestCredential $cred -ScriptType Bash
$iface = ($ifaceRes.ScriptOutput.Trim() -split "`n")[0].Trim()
# Persist via NetworkManager (RHEL/CentOS/Ubuntu 20+)
if command -v nmcli &>/dev/null; then
CON_NAME=`$(nmcli -t -f NAME con show 2>/dev/null | head -1)
if [ -n "`$CON_NAME" ]; then
nmcli con mod "`$CON_NAME" \
ipv4.method manual \
ipv4.addresses "$Ip/$Cidr" \
ipv4.gateway "$Gateway" 2>/dev/null || true
nmcli con up "`$CON_NAME" 2>/dev/null || true
fi
elif [ -d /etc/netplan ]; then
# Netplan fallback (Ubuntu 18+ without NM)
cat > /etc/netplan/99-static.yaml << 'YAML'
network:
version: 2
ethernets:
IFACE_PLACEHOLDER:
dhcp4: false
addresses: [IP_PLACEHOLDER/CIDR_PLACEHOLDER]
gateway4: GATEWAY_PLACEHOLDER
YAML
sed -i "s/IFACE_PLACEHOLDER/`$IFACE/g;s|IP_PLACEHOLDER/CIDR_PLACEHOLDER|$Ip/$Cidr|g;s/GATEWAY_PLACEHOLDER/$Gateway/g" /etc/netplan/99-static.yaml
netplan apply 2>/dev/null || true
elif [ -d /etc/sysconfig/network-scripts ]; then
# RHEL/CentOS legacy ifcfg
cat > /etc/sysconfig/network-scripts/ifcfg-`$IFACE << EOF
DEVICE=`$IFACE
BOOTPROTO=static
ONBOOT=yes
IPADDR=$Ip
PREFIX=$Cidr
GATEWAY=$Gateway
EOF
ifdown `$IFACE 2>/dev/null || true
ifup `$IFACE 2>/dev/null || true
fi
if ([string]::IsNullOrWhiteSpace($iface)) {
throw "Cannot detect network interface"
}
# ✅ CHANGE IP (clean)
$script = @"
ip addr flush dev $iface
ip addr add $IP/$MaskBits dev $iface
ip route replace default via $Gateway
echo "nameserver $DNS" > /etc/resolv.conf
echo SUCCESS
"@
Invoke-VMScript -VM $vm -ScriptText $script -GuestCredential $cred -ScriptType Bash | Out-Null
Start-Sleep -Seconds 2
# ✅ VERIFY chỉ lấy IP (không dính text rác)
$verifyScript = "hostname -I | awk '{print \$1}'"
$verifyRes = Invoke-VMScript -VM $vm -ScriptText $verifyScript -GuestCredential $cred -ScriptType Bash
$actualIp = ($verifyRes.ScriptOutput.Trim() -split "`n")[0].Trim()
if ($actualIp -eq $IP) {
$status = "ok"
$message = "IP updated successfully"
} else {
$status = "error"
$message = "IP mismatch: $actualIp"
}
Disconnect-VIServer -Confirm:$false | Out-Null
[pscustomobject]@{
status = $status
ip = $actualIp
iface = $iface
message = $message
} | ConvertTo-Json -Depth 3
$result = Invoke-VMScript -VM $vm `
-ScriptText $bashScript `
-GuestUser "root" `
-GuestPassword $GuestPassword `
-ScriptType Bash `
-ErrorAction Stop
if ($result.ScriptOutput -match "SUCCESS") {
Write-Output "SUCCESS"
} else {
Write-Output "FAIL"
}
} catch {
Disconnect-VIServer -Confirm:$false | Out-Null
[pscustomobject]@{
status = "error"
message = $_.Exception.Message
} | ConvertTo-Json -Depth 3
Write-Output "FAIL"
Write-Output $_.Exception.Message
} finally {
Disconnect-VIServer -Confirm:$false -ErrorAction SilentlyContinue | Out-Null
}