Refactor VM provisioning pipeline into focused services and add IP pool
management
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user