The Latest Gartner® Magic Quadrant™Hyperconverged Infrastructure Software
Code: Select all
param($addr="10.10.10.1", $port=3261, $user="root", $password="starwind",
$addr2="10.10.10.2", $port2=$port, $user2=$user, $password2=$password,
#common
$initMethod="Clear",
$size=12,
$sectorSize=512,
$failover=0,
$bmpType=1,
$bmpStrategy=0,
#primary node
$imagePath="My Computer\B\LUNs",
$imageName="masterImg21",
$createImage=$true,
$storageName="",
$targetAlias="targetha21",
$poolName="pool1",
$syncSessionCount=1,
$aluaOptimized=$true,
$cacheMode="none",
$cacheSize=0,
$syncInterface="#p2={0}:3260" -f $addr2,
$hbInterface="",
$createTarget=$true,
$bmpFolderPath="",
#secondary node
$imagePath2="My Computer\B\LUNs",
$imageName2="partnerImg22",
$createImage2=$true,
$storageName2="",
$targetAlias2="partnerha22",
$poolName2="pool1",
$syncSessionCount2=1,
$aluaOptimized2=$false,
$cacheMode2=$cacheMode,
$cacheSize2=$cacheSize,
$syncInterface2="#p1={0}:3260" -f $addr,
$hbInterface2="",
$createTarget2=$true,
$bmpFolderPath2=""
)
Import-Module StarWindX
try
{
Enable-SWXLog
$server = New-SWServer -host $addr -port $port -user $user -password $password
$server.Connect()
$firstNode = new-Object Node
$firstNode.HostName = $addr
$firstNode.HostPort = $port
$firstNode.Login = $user
$firstNode.Password = $password
$firstNode.ImagePath = $imagePath
$firstNode.ImageName = $imageName
$firstNode.Size = $size
$firstNode.CreateImage = $createImage
$firstNode.StorageName = $storageName
$firstNode.TargetAlias = $targetAlias
$firstNode.SyncInterface = $syncInterface
$firstNode.HBInterface = $hbInterface
$firstNode.PoolName = $poolName
$firstNode.SyncSessionCount = $syncSessionCount
$firstNode.ALUAOptimized = $aluaOptimized
$firstNode.CacheMode = $cacheMode
$firstNode.CacheSize = $cacheSize
$firstNode.FailoverStrategy = $failover
$firstNode.CreateTarget = $createTarget
$firstNode.BitmapStoreType = $bmpType
$firstNode.BitmapStrategy = $bmpStrategy
$firstNode.BitmapFolderPath = $bmpFolderPath
#
# device sector size. Possible values: 512 or 4096(May be incompatible with some clients!) bytes.
#
$firstNode.SectorSize = $sectorSize
$secondNode = new-Object Node
$secondNode.HostName = $addr2
$secondNode.HostPort = $port2
$secondNode.Login = $user2
$secondNode.Password = $password2
$secondNode.ImagePath = $imagePath2
$secondNode.ImageName = $imageName2
$secondNode.CreateImage = $createImage2
$secondNode.StorageName = $storageName2
$secondNode.TargetAlias = $targetAlias2
$secondNode.SyncInterface = $syncInterface2
$secondNode.HBInterface = $hbInterface2
$secondNode.SyncSessionCount = $syncSessionCount2
$secondNode.ALUAOptimized = $aluaOptimized2
$secondNode.CacheMode = $cacheMode2
$secondNode.CacheSize = $cacheSize2
$secondNode.FailoverStrategy = $failover
$secondNode.CreateTarget = $createTarget2
$secondNode.BitmapFolderPath = $bmpFolderPath2
$device = Add-HADevice -server $server -firstNode $firstNode -secondNode $secondNode -initMethod $initMethod
while ($device.SyncStatus -ne [SwHaSyncStatus]::SW_HA_SYNC_STATUS_SYNC)
{
$syncPercent = $device.GetPropertyValue("ha_synch_percent")
Write-Host "Synchronizing: $($syncPercent)%" -foreground yellow
Start-Sleep -m 2000
$device.Refresh()
}
}
catch
{
Write-Host $_ -foreground red
}
finally
{
$server.Disconnect()
}
Code: Select all
[string]$PrimaryNodeSyncIPAddress = "10.10.10.1", #The IP address of the synchronization interface on the primary node
[string]$PrimaryNodeHeartbeatIPAddress = "10.255.255.3", #The IP address of the heartbeat interface on the primary node
[string]$SecondaryNodeSyncIPAddress = "10.10.10.2", #The IP address of the synchronization interface on the secondary node
[string]$SecondaryNodeHeartbeatIPAddress = "10.255.255.4", #The IP address of the heartbeat interface on the secondary node
.....
$firstNode.SyncInterface = "#p2=$($SecondaryNodeSyncIPAddress):3260"
$firstNode.HBInterface = "#p2=$($SecondaryNodeHeartbeatIPAddress):3260"
.....
$secondNode.SyncInterface = "#p1=$($PrimaryNodeSyncIPAddress):3260"
$secondNode.HBInterface = "#p1=$($PrimaryNodeHeartbeatIPAddress):3260"
Code: Select all
param(
#StarWind node settings
[string]$PrimaryNode,
[string]$SecondaryNode,
[ValidateRange(1,65535)]
[int]$Port = 3261,
#Authentication
[string]$Username = "root",
[string]$Password = "starwind",
#LUN creation mode
[ValidateSet("HA", "Flat")]
[string]$LUNType = "HA",
[string]$HATargetSharedPrefix,
#Image settings
[Parameter(Mandatory=$true)]
[string]$ImageName,
[string]$ImagePath = "My computer\B\LUNs",
[long]$ImageSize = 5GB, #starwind expects an integer representing megabytes. We'll convert this number later.
[ValidateSet(512, 4096)]
[int]$SectorSize = 512, #4096 for Hyper-V/Windows, 512 for VMware
[ValidateSet("Disabled", "WriteBack", "WriteThrough")]
[string]$CacheMode = "Disabled", # None (safest), wb = WriteBack (data loss on power failure), wt = WriteThrough (caches reads only)
[ValidateRange(0, 1099511627776)]
[int]$CacheSize = 0,
[bool]$CreateImage = $true, # Really not a reason to ever set as false
#HA settings
[ValidateSet("Clear","NotSynchronize","SyncFromFirst","SyncFromSecond","SyncFromThird")]
[string]$InitMethod = "Clear", #Clear = blank both; NotSynchronize = don't init; SyncFromFirst; SyncFromSecond; SyncFromThird
[ValidateSet("Heartbeat", "NodeMajority")]
[string]$FailoverStrategy = "Heartbeat", #0 = Heartbeat, 1 = Node Majority
[ValidateSet("RAM", "Disk")]
[string]$JournalType = "Disk", # 1 = RAM (fastest, risky), 2 = Disk (variable speed, safer)
[ValidateSet("OnFail", "Always")]
[string]$JournalStrategy = "OnFail", # 1 = Write on fail (fast, slower during failures, mostly safe), 2 = Always write (always slower, safest)
[string]$JournalPath = "$($ImagePath)\$($ImageName)-JL",
[string]$TargetAlias = $ImageName,
[switch]$DisableAutomaticSynchronization,
[string]$PoolName = "pool1",
[string]$StorageName = "",
[int]$SyncSessionCount = 1,
[int]$NUMANode = 0,
[bool]$ALUAOptimized = $true,
[string]$PrimaryNodeSyncIPAddress = "10.10.10.1", #The IP address of the synchronization interface on the primary node
[string]$PrimaryNodeHeartbeatIPAddress = "10.255.255.3", #The IP address of the heartbeat interface on the primary node
[string]$SecondaryNodeSyncIPAddress = "10.10.10.2", #The IP address of the synchronization interface on the secondary node
[string]$SecondaryNodeHeartbeatIPAddress = "10.255.255.4", #The IP address of the heartbeat interface on the secondary node
[bool]$CreateTarget = $true,
[switch]$FeedbackEnabled
)
###
# Local Variables
###
# We use these to store StarWind-friendly equivalents of the user-friendly values collected in the parameters
$varPrimaryNodeIP = $null
$varSecondaryNodeIP = $null
$varInitMethod = $InitMethod
$varSectorSize = $SectorSize
$varFailoverStrategy = 0
$varJournalType = 2
$varJournalStrategy = 1
$varCacheMode = "none"
#####
# Validation
#####
#### We do all our math as long, since the powershell friendly notations are 64-bit integers
#4K Can't do < 2MB
if($ImageSize -le 2MB -and $SectorSize -eq 4096) { throw "It is not supported to create images smaller than 2MB with 4K sector size." }
#Prevent buffer overflow when converting long to int.
#Technically the boundary is not exactly 1.89PB, it's ~1.91PB, but we play safe here
#1.89PB turns into 2029372047MB after we later convert to int32, which is safely under the upper bound of int32
if($ImageSize -gt 1.89PB) {
throw "The ImageSize specified is too large. The maximum size accepted is 1.89 petabytes."
}
#Volumes under 10GB are generally meaningless, so if we get a request for a tiny one, verify it's intentional
if($ImageSize -lt 10GB) {
$ConfirmSize = Read-Host -Prompt "The size you specified is $($ImageSize / 1MB)MB. This is less than 10GB. Some modern filesystems, like NTFS, may struggle with this volume size. Do you want to proceed with this size? [y/N]"
if($ConfirmSize.ToLower() -ne "y") { $ImageSize = Read-Host -Prompt "Please enter a new image size. Image size must be an integer." }
}
### Since we're done using the PowerShell notation now, we need to make something StarWind will understand
#Convert friendly image size input to starwind notation
#StarWind requires an int value measured as MB
#Constants: 1TB = [long]1099511627776; 1MB = [int]1048576
#Example: 1TB / 1MB = 1048576MB --> Bytes: 1099511627776 bytes / 1048576 bytes = 1048576 megabytes
#So in the above example, this conversion will turn the long value of 1TB entered by the user into an integer value 1048576 understood by StarWind
[int]$ImageSize = [int]($ImageSize / 1MB)
###
# Only HA images
###
if($LUNType -eq "HA") {
#PrimaryNode, test the name provided by the user is actually a server
if($PrimaryNode -eq $null) { throw "HA LUNs require at least two nodes. Please specify a value for PrimaryNode." }
$Resolver = (Resolve-DnsName $PrimaryNode -Type A -DnsOnly -ErrorAction SilentlyContinue).IPAddress
if($Resolver -eq $null) {
throw "Unable to resolve the name supplied in PrimaryNode. This must be a valid DNS name resolving to another StarWind cluster node."
} else { $varPrimaryNodeIP = $Resolver }
#SecondaryNode, test the name provided by the user is actually a server
if($SecondaryNode -eq $null) { throw "HA LUNs require at least two nodes. Please specify a value for SecondaryNode." }
$Resolver = (Resolve-DnsName $SecondaryNode -Type A -DnsOnly -ErrorAction SilentlyContinue).IPAddress
if($Resolver -eq $null) {
throw "Unable to resolve the name supplied in SecondaryNode. This must be a valid DNS name resolving to another StarWind cluster node."
} else {
$varSecondaryNodeIP = $Resolver
}
}
###
# Only local flat images
###
if($LUNType -eq "Flat") {
#PrimaryNode, test the name provided by the user is actually a server
if($PrimaryNode -eq $null) { throw "HA LUNs require at least two nodes. Please specify a value for PrimaryNode." }
$Resolver = (Resolve-DnsName $PrimaryNode -Type A -DnsOnly -ErrorAction SilentlyContinue).IPAddress
if($Resolver -eq $null) {
throw "Unable to resolve the name supplied in PrimaryNode. This must be a valid DNS name resolving to another StarWind cluster node."
} else { $varPrimaryNodeIP = $Resolver }
}
###
# All Image Types
###
# StarWind expects none, wb, wt as string values, so we convert the enum to the intended string
switch ($CacheMode) {
"Disabled" {
$varCacheMode = "none"
}
"WriteBack" {
$varCacheMode = "wb"
}
"WriteThrough" {
$varCacheMode = "wt"
}
default {
$varCacheMode = "none"
}
}
Enable-SWXLog -level SW_LOG_LEVEL_DEBUG
$Server = New-SWServer -host $PrimaryNode -port $Port -user $Username -password $Password
$Server.Connect()
try
{
if($LUNType -eq "HA") {
$firstNode = new-Object Node
$firstNode.HostName = $varPrimaryNodeIP
$firstNode.HostPort = $Port
$firstNode.Login = $Username
$firstNode.Password = $Password
$firstNode.ImagePath = $ImagePath
$firstNode.ImageName = $ImageName
$firstNode.Size = $ImageSize
$firstNode.CreateImage = $true
$firstNode.StorageName = $StorageName
$firstNode.TargetAlias = $TargetAlias
$firstNode.CacheMode = $varCacheMode
$firstNode.CacheSize = $CacheSize
$firstNode.CreateTarget = $CreateTarget
$firstNode.SectorSize = $varSectorSize
$firstNode.SyncInterface = "#p2=$($SecondaryNodeSyncIPAddress):3260"
$firstNode.HBInterface = "#p2=$($SecondaryNodeHeartbeatIPAddress):3260"
$firstNode.PoolName = $PoolName
$firstNode.SyncSessionCount = $SyncSessionCount
$firstNode.ALUAOptimized = $ALUAOptimized
$firstNode.FailoverStrategy = $varFailoverStrategy
$firstNode.BitmapStoreType = $varJournalType
$firstNode.BitmapStrategy = $varJournalStrategy
$secondNode = new-Object Node
$secondNode.HostName = $SecondaryNode
$secondNode.HostPort = $Port
$secondNode.Login = $Username
$secondNode.Password = $Password
$secondNode.ImagePath = $ImagePath
$secondNode.ImageName = $ImageName
$secondNode.Size = $ImageSize
$secondNode.CreateImage = $CreateImage
$secondNode.StorageName = $StorageName
$secondNode.TargetAlias = $TargetAlias
$secondNode.CacheMode = $varCacheMode
$secondNode.CacheSize = $CacheSize
$secondNode.CreateTarget = $CreateTarget
$secondNode.SectorSize = $varSectorSize
$secondNode.SyncInterface = "#p1=$($PrimaryNodeSyncIPAddress):3260"
$secondNode.HBInterface = "#p1=$($PrimaryNodeHeartbeatIPAddress):3260"
$secondNode.PoolName = $PoolName
$secondNode.SyncSessionCount = $SyncSessionCount
$secondNode.ALUAOptimized = $ALUAOptimized
$secondNode.FailoverStrategy = $varFailoverStrategy
$secondNode.BitmapStrategy = $varJournalStrategy
$secondNode.BitmapStoreType = $varJournalType
# BitmapFolderPath is only needed if using disk-based journaling
if($JournalType -eq "Disk") {
$firstNode.BitmapFolderPath = $JournalPath
$secondNode.BitmapFolderPath = $JournalPath
#Convert StarWind path to Windows path
$TestPath = "\\{server}\$($JournalPath.Substring($JournalPath.IndexOf("\") + 1, 1))$\$($JournalPath.SubString($JournalPath.IndexOf("\") + 3))"
$PrimaryPath = $TestPath.Replace("{server}",$PrimaryNode)
$SecondaryPath = $TestPath.Replace("{server}",$SecondaryNode)
if((Test-Path $PrimaryPath) -eq $false) {
#BitmapFolderPath doesn't exist, try to create it
New-Item $PrimaryPath -ErrorAction Stop -ItemType Directory
}
if((Test-Path $SecondaryPath) -eq $false) {
#BitmapFolderPath doesn't exist, try to create it
New-Item $SecondaryPath -ErrorAction Stop -ItemType Directory
}
}
#If we have a defined HA target prefix, use it for simple multipathing
if($HATargetSharedPrefix -ne $null) {
$TargetName = "iqn.2008-08.com.starwindsoftware:$($HATargetSharedPrefix)-$($TargetAlias)"
$firstNode.TargetName = $TargetName
$secondNode.TargetName = $TargetName
}
if($DisableAutomaticSynchronization.IsPresent) {
$firstNode.AutoSynch = $false
$secondNode.AutoSynch = $false
} else {
$firstNode.AutoSynch = $true
$secondNode.AutoSynch = $true
}
if($FeedbackEnabled.IsPresent) {
Write-Host "Generated primary node:" -ForegroundColor Yellow
$firstNode |FL
Write-Host ""
Write-Host "Generated secondary node:" -ForegroundColor Yellow
$secondNode |FL
}
$device = Add-HADevice `
-server $Server `
-firstNode $firstNode `
-secondNode $secondNode `
-initMethod $varInitMethod
while ($device.SyncStatus -ne [SwHaSyncStatus]::SW_HA_SYNC_STATUS_SYNC)
{
$syncPercent = $device.GetPropertyValue("ha_synch_percent")
Write-Progress -Activity "Configuring HA for $($ImageName)" -CurrentOperation "Synchronizing nodes..." -PercentComplete $syncPercent
Start-Sleep -Seconds 10
$device.Refresh()
}
Write-Progress -Completed -Activity "Configuring HA for $($ImageName)"
Write-Host "Created HA device:" -ForegroundColor Yellow
$device |FL
}
if($LUNType -eq "Flat") {
New-ImageFile `
-server $Server `
-path $ImagePath `
-fileName $ImageName `
-size $ImageSize
$device = Add-ImageDevice `
-server $Server `
-path $ImagePath `
-fileName $ImageName `
-sectorSize $varSectorSize `
-NumaNode $NUMANode `
-CacheMode $varCacheMode `
-CacheSize $CacheSize
$target = New-Target `
-server $Server `
-alias $TargetAlias `
-devices $device.Name
}
}
catch
{
Write-Host $_ -foreground red
}
finally
{
if($Server.Connected) {
$Server.Disconnect()
}
}
if($Server.Connected) {
$Server.Disconnect()
}
Yes, Check out the script here viewtopic.php?f=5&t=6852&p=37208&hilit=HINT8#p37208.cypher2000 wrote: ↑Sun Sep 28, 2025 7:27 pmAlso, when you have a moment, can you tell me if there's a way from PowerShell to specify multiple heartbeat interfaces like there is in the UI Replication Manager wizard?
Code: Select all
Request to SAT-SAN-V1-01.HYPERNOC.IO ( 10.128.124.50 ) : 3261
-
control 0x000002772900C840 -AddPartner:"" -PartnerTargetName:"#p1=iqn.2008-08.com.starwindsoftware:sat-san-v1.hypernoc.io-sat-san-c1-quorum;#p2=iqn.2008-08.com.starwindsoftware:sat-san-v1.hypernoc.io-sat-san-c1-quorum" -Priority:"#p1=2;#p2=2" -nodeType:"#p1=1;#p2=1" -PartnerIP:"#p1=10.128.124.51:heartbeat:3260:1,10.255.255.4:heartbeat:3260:1;#p2=10.10.10.2:sync:3260:1" -AuthChapType:"#p1=none;#p2=none" -AuthChapLogin:"#p1=0b;#p2=0b" -AuthChapPassword:"#p1=0b;#p2=0b" -AuthMChapName:"#p1=0b;#p2=0b" -AuthMChapSecret:"#p1=0b;#p2=0b" -Replicator:"#p1=0;#p2=0" -typeBitmapStore:"#p1=1;#p2=1"
-
200 Failed: invalid partner info..