Octopus.Script exported 2017-06-02 by tbrasch belongs to ‘F5’ category.
F5 - Enable, Disable, or Force Offline multiple pools with optional wait for connections to drop
Parameters
When steps based on the template are included in a project’s deployment process, the parameters below can be set.
Run Condition
RunCondition =
Boolean value or expression that determines if this step should run.
Wait for connections to drop to 0?
WaitForConnections = True
If checked, the deployment won’t continue until current connections on the node = 0
Maximum wait time in seconds
MaxWaitTime = 300
Maximum wait time (in seconds) before killing connections and moving on.
LTM Status
LtmStatus = Disabled
State member will be put into (Enabled, Disabled, Offline)
-
Disabled continues to process persistent and active connections. It can accept new connections only if the connections belong to an existing persistence session.
-
Offline allows existing connections to time out, but no new connections are allowed.
List of LTM info
LtmInfo =
Enter list of all Pools, IPs, and Ports. Each set delimited by carriage returns, each value delimited by pipe (|). Here is an example:
Pool_192.168.103.226_443|192.168.103.174|443
Pool_192.168.103.226_80|192.168.103.174|80
LTM Host name
HostName =
null
Kill connections when less than or equal to
ConnectionCount = 0
The default is to wait until there are no connections left on the node. If you don’t want to wait for zero connections before deploying, you can put a value here and when that number of connections is reached the deployment will happen killing the rest of the connected sessions.
LTM username
Username =
Credential used to access F5 Soap API
LTM password
Password =
Credential used to access F5 Soap API
Script body
Steps based on this template will execute the following PowerShell script.
#region Verify variables
#Verify RunCondition can be converted to boolean.
$runCondition = $false
If ([string]::IsNullOrEmpty($OctopusParameters['RunCondition'])){
Throw "Run Condition cannot be null."
}
Else{
Try{
$runCondition = [System.Convert]::ToBoolean($OctopusParameters['RunCondition'])
Write-Host ("Run Condition: '" + $OctopusParameters['RunCondition'] + "' converts to boolean: " + $runCondition + ".")
#If run condition evaluates to false, just return/stop processing.
If (!$runCondition){
Write-Host "Skipping step."
return
}
}
Catch{
Throw "Cannot convert Run Condition: '" + $OctopusParameters['RunCondition'] + "' to boolean value."
}
}
#No need to verify WaitForConnections as this is a checkbox and will always have a boolean value. Report value back for logging.
Write-Host ("Wait for connections to drop to 0: " + $OctopusParameters['WaitForConnections'])
#Verify MaxWaitTime can be converted to integer.
If ([string]::IsNullOrEmpty($OctopusParameters['MaxWaitTime'])){
Throw "Maximum wait time in seconds cannot be null."
}
[int]$maxWaitTime = 0
[bool]$result = [int]::TryParse($OctopusParameters['MaxWaitTime'], [ref]$maxWaitTime )
If ($result){
Write-Host ("Maximum wait time in seconds: " + $maxWaitTime)
}
Else{
Throw "Cannot convert Maximum wait time in seconds: '" + $OctopusParameters['MaxWaitTime'] + "' to integer."
}
#No need to verify LtmStatus as this is a drop down box and will always have a value. Report back for logging.
Write-Host ("LTM Status: " + $OctopusParameters['LtmStatus'])
<#
Verify List of LTM info.
LTM Info should contain a list of all Pools, IPs, and Ports. Each set should be delmited by carriage returns, each valude delimited by pipe (|).
Here is an example:
Pool_192.168.103.226_443|192.168.103.174|443
Pool_192.168.103.226_80|192.168.103.174|80
#>
If ([string]::IsNullOrEmpty($OctopusParameters['LtmInfo'])){
Throw "List of LTM info cannot be null."
}
#Write out LTM info. If the project is using variables (and it most likely is), it may be difficult to debug without seeing what it evaluated to.
Write-Host ("List of LTM info: " + [Environment]::NewLine + $OctopusParameters['LtmInfo'])
$f5Pools = ($OctopusParameters['LtmInfo']).Split([Environment]::NewLine)
Foreach ($f5Pool in $f5Pools){
#Validate 3 values are passed in per line.
$poolInfo = $f5Pool.Split("|")
If ($poolInfo.Count -ne 3){
Throw ("Invalid pool info. Expecting 'PoolName|IpAddress|Port': '" + $f5Pool + "'.")
}
#Validate that each value is not null.
Foreach ($f5Parm in $poolInfo){
If ([string]::IsNullOrEmpty($f5Parm)){
Throw ("Invalid pool info. Expecting 'PoolName|IpAddress|Port': '" + $f5Pool + "'. None can be empty.")
}
}
#Validate IP Address (second value).
If ( !($poolInfo[1] -as [ipaddress]) ){
Throw ("Invalid IP Address: '" + $poolInfo[1] + "'.")
}
#Validate Port (third value).
[int]$port = 0
[bool]$result = [int]::TryParse($poolInfo[2], [ref]$port )
If ( !($result) ){
Throw ("Invalid port (expecting integer): '" + $poolInfo[2] + "'.")
}
}
#Verify HostName is not null.
If ([string]::IsNullOrEmpty($OctopusParameters['HostName'])){
Throw "LTM Host name cannot be null."
}
Write-Host ("LTM Host: " + $OctopusParameters['HostName'])
#Verify Username is not null.
If ([string]::IsNullOrEmpty($OctopusParameters['Username'])){
Throw "LTM username cannot be null."
}
Write-Host ("Username: " + $OctopusParameters['Username'])
#Verify Password is not null.
If ([string]::IsNullOrEmpty($OctopusParameters['Password'])){
Throw "LTM password cannot be null."
}
#Verify ConnectionCount can be converted to integer.
If ([string]::IsNullOrEmpty($OctopusParameters['ConnectionCount'])){
Throw "Kill connections when less than or equal to cannot be null."
}
[int]$killConnectionWhenLE = 0
[bool]$result = [int]::TryParse($OctopusParameters['ConnectionCount'], [ref]$killConnectionWhenLE )
If ($result){
Write-Host ("Kill connections when less than or equal to: " + $killConnectionWhenLE)
}
Else{
Throw "Cannot convert Kill connections when less than or equal to: '" + $OctopusParameters['ConnectionCount'] + "' to integer."
}
#endregion
#region Functions
Function Set-F5PoolState{
param(
$f5Pools,
[switch]$forceOffline
)
Foreach ($f5Pool in $f5Pools){
$poolInfo = $f5Pool.Split("|")
$poolName = $poolInfo[0]
$ipAddress = $poolInfo[1]
$port = $poolInfo[2]
$member = ($ipAddress + ":" + $port)
$state = $OctopusParameters['LtmStatus']
If ($forceOffline){
$state = "Offline"
}
Write-Host "Setting '$ipAddress' to '$state' in '$poolName' pool."
Set-F5.LTMPoolMemberState -Pool $poolName -Member $member -state $state
}
}
Function Wait-ConnectionCount(){
param(
$f5Pools,
[int]$maxWaitTime,
[int]$connectionCount
)
#Start stop watch now.
$elapsed = [System.Diagnostics.Stopwatch]::StartNew()
Foreach ($f5Pool in $f5Pools){
$poolInfo = $f5Pool.Split("|")
$poolName = $poolInfo[0]
$ipAddress = $poolInfo[1]
$port = $poolInfo[2]
$MemberDef = New-Object -TypeName iControl.CommonIPPortDefinition
$MemberDef.address = $ipAddress
$MemberDef.port = $port
$MemberDefAofA = New-Object -TypeName "iControl.CommonIPPortDefinition[][]" 1,1
$MemberDefAofA[0][0] = $MemberDef
$cur_connections = 100
Write-Host ("Pool name: " + $poolName)
Write-Host ("IP Address: " + $ipAddress)
Write-Host ("Port: " + $port)
While ($cur_connections -gt $connectionCount -and $elapsed.ElapsedMilliseconds -lt ($maxWaitTime * 1000)){
$MemberStatisticsA = (Get-F5.iControl).LocalLBPoolMember.get_statistics( (, $poolName), $MemberDefAofA)
$MemberStatisticEntry = $MemberStatisticsA[0].statistics[0]
$Statistics = $MemberStatisticEntry.statistics
Foreach ($Statistic in $Statistics){
$type = $Statistic.type;
$value = $Statistic.value;
If ( $type -eq "STATISTIC_SERVER_SIDE_CURRENT_CONNECTIONS" ){
#Just use the low value. Odds are there aren't over 2^32 current connections. If your site is this big, you'll have to convert this to a 64 bit number.
$cur_connections = $value.low;
Write-Host "Current Connections: $cur_connections"
}
}
If ($cur_connections -gt $connectionCount -and $elapsed.ElapsedMilliseconds -lt ($maxWaitTime * 1000)){
Start-Sleep -Seconds 5
}
}
}
}
#endregion
#region Process
#Load the F5 powershell iControl snapin
Add-PSSnapin iControlSnapin
Initialize-F5.iControl -HostName $OctopusParameters['HostName'] -Username $OctopusParameters['Username'] -Password $OctopusParameters['Password']
Set-F5PoolState -f5Pools $f5Pools
If (($OctopusParameters['LtmStatus'] -ne "Enabled") -and ($OctopusParameters['WaitForConnections'] -eq "True"))
{
Write-Host "Waiting for connections to drain before deploying. This could take a while..."
Wait-ConnectionCount -f5Pools $f5Pools -maxWaitTime $maxWaitTime -connectionCount $killConnectionWhenLE
#We have now waited the desired amount, go ahead and force offline and move on with deployment.
Set-F5PoolState -f5Pools $f5Pools -forceOffline
}
#endregion
Provided under the Apache License version 2.0.
To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.
{
"Id": "b3d76f46-e074-47ff-b800-3020c3f47866",
"Name": "F5 - Enable or Disable Multiple Pools",
"Description": "F5 - Enable, Disable, or Force Offline multiple pools with optional wait for connections to drop",
"Version": 2,
"ExportedAt": "2017-06-02T14:54:52.840Z",
"ActionType": "Octopus.Script",
"Author": "tbrasch",
"Parameters": [
{
"Id": "83c45726-806c-4d06-8ae3-22b13b89d5a7",
"Name": "RunCondition",
"Label": "Run Condition",
"HelpText": "Boolean value or expression that determines if this step should run.",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
},
"Links": {}
},
{
"Id": "fb610f88-0d64-40cf-a74d-0bb8405755c8",
"Name": "WaitForConnections",
"Label": "Wait for connections to drop to 0?",
"HelpText": "If checked, the deployment won't continue until current connections on the node = 0",
"DefaultValue": "True",
"DisplaySettings": {
"Octopus.ControlType": "Checkbox"
},
"Links": {}
},
{
"Id": "af92387f-ba2d-4b44-a8f1-7ebccf01032a",
"Name": "MaxWaitTime",
"Label": "Maximum wait time in seconds",
"HelpText": "Maximum wait time (in seconds) before killing connections and moving on.",
"DefaultValue": "300",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
},
"Links": {}
},
{
"Id": "d8c955b3-6c75-49cf-9396-25dc0fffb611",
"Name": "LtmStatus",
"Label": "LTM Status",
"HelpText": "State member will be put into (Enabled, Disabled, Offline) \n\n- **Disabled** continues to process persistent and active connections. It can accept new connections only if the connections belong to an existing persistence session. \n\n- **Offline** allows existing connections to time out, but no new connections are allowed.",
"DefaultValue": "Disabled",
"DisplaySettings": {
"Octopus.ControlType": "Select",
"Octopus.SelectOptions": "Enabled|Enable\nDisabled|Disable\nOffline|Forced Offline"
},
"Links": {}
},
{
"Id": "57b114a3-fa6a-40d6-9c42-38ba80127fca",
"Name": "LtmInfo",
"Label": "List of LTM info",
"HelpText": "Enter list of all Pools, IPs, and Ports. Each set delimited by carriage returns, each value delimited by pipe (|). Here is an example:\n\nPool\\_192.168.103.226\\_443|192.168.103.174|443 \nPool\\_192.168.103.226\\_80|192.168.103.174|80",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "MultiLineText"
},
"Links": {}
},
{
"Id": "3fdbf9bc-9ee8-4610-9c34-24f1975cf8c7",
"Name": "HostName",
"Label": "LTM Host name",
"HelpText": null,
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
},
"Links": {}
},
{
"Id": "61480ba0-f715-46ef-b5ad-1c6b6f580273",
"Name": "ConnectionCount",
"Label": "Kill connections when less than or equal to",
"HelpText": "The default is to wait until there are no connections left on the node. If you don't want to wait for zero connections before deploying, you can put a value here and when that number of connections is reached the deployment will happen killing the rest of the connected sessions.",
"DefaultValue": "0",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
},
"Links": {}
},
{
"Id": "d0f276b6-ce27-4006-b3c2-00c669bfc42d",
"Name": "Username",
"Label": "LTM username",
"HelpText": "Credential used to access F5 Soap API",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "SingleLineText"
},
"Links": {}
},
{
"Id": "921b6a08-0ffe-4945-91c3-8520a3052ce9",
"Name": "Password",
"Label": "LTM password",
"HelpText": "Credential used to access F5 Soap API",
"DefaultValue": "",
"DisplaySettings": {
"Octopus.ControlType": "Sensitive"
},
"Links": {}
}
],
"Properties": {
"Octopus.Action.Script.Syntax": "PowerShell",
"Octopus.Action.Script.ScriptSource": "Inline",
"Octopus.Action.RunOnServer": "false",
"Octopus.Action.Script.ScriptBody": "#region Verify variables\n\n#Verify RunCondition can be converted to boolean.\n$runCondition = $false\nIf ([string]::IsNullOrEmpty($OctopusParameters['RunCondition'])){\n Throw \"Run Condition cannot be null.\"\n}\nElse{\n Try{\n $runCondition = [System.Convert]::ToBoolean($OctopusParameters['RunCondition'])\n Write-Host (\"Run Condition: '\" + $OctopusParameters['RunCondition'] + \"' converts to boolean: \" + $runCondition + \".\")\n\n #If run condition evaluates to false, just return/stop processing.\n If (!$runCondition){\n Write-Host \"Skipping step.\"\n return\n }\n }\n Catch{\n Throw \"Cannot convert Run Condition: '\" + $OctopusParameters['RunCondition'] + \"' to boolean value.\"\n }\n}\n\n#No need to verify WaitForConnections as this is a checkbox and will always have a boolean value. Report value back for logging.\nWrite-Host (\"Wait for connections to drop to 0: \" + $OctopusParameters['WaitForConnections'])\n\n#Verify MaxWaitTime can be converted to integer.\nIf ([string]::IsNullOrEmpty($OctopusParameters['MaxWaitTime'])){\n Throw \"Maximum wait time in seconds cannot be null.\"\n}\n\n[int]$maxWaitTime = 0\n[bool]$result = [int]::TryParse($OctopusParameters['MaxWaitTime'], [ref]$maxWaitTime )\n\nIf ($result){\n Write-Host (\"Maximum wait time in seconds: \" + $maxWaitTime)\n}\nElse{\n Throw \"Cannot convert Maximum wait time in seconds: '\" + $OctopusParameters['MaxWaitTime'] + \"' to integer.\"\n}\n\n#No need to verify LtmStatus as this is a drop down box and will always have a value. Report back for logging.\nWrite-Host (\"LTM Status: \" + $OctopusParameters['LtmStatus'])\n\n<#\nVerify List of LTM info.\nLTM Info should contain a list of all Pools, IPs, and Ports. Each set should be delmited by carriage returns, each valude delimited by pipe (|).\nHere is an example:\nPool_192.168.103.226_443|192.168.103.174|443 \nPool_192.168.103.226_80|192.168.103.174|80\n#>\nIf ([string]::IsNullOrEmpty($OctopusParameters['LtmInfo'])){\n Throw \"List of LTM info cannot be null.\"\n}\n#Write out LTM info. If the project is using variables (and it most likely is), it may be difficult to debug without seeing what it evaluated to.\nWrite-Host (\"List of LTM info: \" + [Environment]::NewLine + $OctopusParameters['LtmInfo'])\n$f5Pools = ($OctopusParameters['LtmInfo']).Split([Environment]::NewLine)\nForeach ($f5Pool in $f5Pools){\n #Validate 3 values are passed in per line.\n $poolInfo = $f5Pool.Split(\"|\")\n If ($poolInfo.Count -ne 3){\n Throw (\"Invalid pool info. Expecting 'PoolName|IpAddress|Port': '\" + $f5Pool + \"'.\")\n }\n \n #Validate that each value is not null.\n Foreach ($f5Parm in $poolInfo){\n If ([string]::IsNullOrEmpty($f5Parm)){\n Throw (\"Invalid pool info. Expecting 'PoolName|IpAddress|Port': '\" + $f5Pool + \"'. None can be empty.\")\n }\n }\n \n #Validate IP Address (second value).\n If ( !($poolInfo[1] -as [ipaddress]) ){\n Throw (\"Invalid IP Address: '\" + $poolInfo[1] + \"'.\")\n }\n\n #Validate Port (third value).\n [int]$port = 0\n [bool]$result = [int]::TryParse($poolInfo[2], [ref]$port )\n \n If ( !($result) ){\n Throw (\"Invalid port (expecting integer): '\" + $poolInfo[2] + \"'.\")\n }\n}\n\n#Verify HostName is not null.\nIf ([string]::IsNullOrEmpty($OctopusParameters['HostName'])){\n Throw \"LTM Host name cannot be null.\"\n}\nWrite-Host (\"LTM Host: \" + $OctopusParameters['HostName'])\n\n#Verify Username is not null.\nIf ([string]::IsNullOrEmpty($OctopusParameters['Username'])){\n Throw \"LTM username cannot be null.\"\n}\nWrite-Host (\"Username: \" + $OctopusParameters['Username'])\n\n#Verify Password is not null.\nIf ([string]::IsNullOrEmpty($OctopusParameters['Password'])){\n Throw \"LTM password cannot be null.\"\n}\n\n#Verify ConnectionCount can be converted to integer.\nIf ([string]::IsNullOrEmpty($OctopusParameters['ConnectionCount'])){\n Throw \"Kill connections when less than or equal to cannot be null.\"\n}\n\n[int]$killConnectionWhenLE = 0\n[bool]$result = [int]::TryParse($OctopusParameters['ConnectionCount'], [ref]$killConnectionWhenLE )\n\nIf ($result){\n Write-Host (\"Kill connections when less than or equal to: \" + $killConnectionWhenLE)\n}\nElse{\n Throw \"Cannot convert Kill connections when less than or equal to: '\" + $OctopusParameters['ConnectionCount'] + \"' to integer.\"\n}\n\n#endregion\n\n#region Functions\n\nFunction Set-F5PoolState{\n param(\n $f5Pools,\n [switch]$forceOffline\n )\n \n Foreach ($f5Pool in $f5Pools){\n $poolInfo = $f5Pool.Split(\"|\")\n \n $poolName = $poolInfo[0]\n $ipAddress = $poolInfo[1]\n $port = $poolInfo[2]\n \n $member = ($ipAddress + \":\" + $port)\n \n $state = $OctopusParameters['LtmStatus']\n If ($forceOffline){\n $state = \"Offline\"\n }\n \n Write-Host \"Setting '$ipAddress' to '$state' in '$poolName' pool.\"\n Set-F5.LTMPoolMemberState -Pool $poolName -Member $member -state $state\n }\n}\n\nFunction Wait-ConnectionCount(){\n param(\n $f5Pools,\n [int]$maxWaitTime,\n [int]$connectionCount\n )\n \n #Start stop watch now.\n $elapsed = [System.Diagnostics.Stopwatch]::StartNew()\n \n Foreach ($f5Pool in $f5Pools){\n $poolInfo = $f5Pool.Split(\"|\")\n \n $poolName = $poolInfo[0]\n $ipAddress = $poolInfo[1]\n $port = $poolInfo[2]\n \n $MemberDef = New-Object -TypeName iControl.CommonIPPortDefinition\n $MemberDef.address = $ipAddress\n $MemberDef.port = $port\n $MemberDefAofA = New-Object -TypeName \"iControl.CommonIPPortDefinition[][]\" 1,1\n $MemberDefAofA[0][0] = $MemberDef\n $cur_connections = 100\n \n Write-Host (\"Pool name: \" + $poolName)\n Write-Host (\"IP Address: \" + $ipAddress)\n Write-Host (\"Port: \" + $port)\n\n While ($cur_connections -gt $connectionCount -and $elapsed.ElapsedMilliseconds -lt ($maxWaitTime * 1000)){\n $MemberStatisticsA = (Get-F5.iControl).LocalLBPoolMember.get_statistics( (, $poolName), $MemberDefAofA)\n $MemberStatisticEntry = $MemberStatisticsA[0].statistics[0]\n $Statistics = $MemberStatisticEntry.statistics\n \n Foreach ($Statistic in $Statistics){\n $type = $Statistic.type;\n $value = $Statistic.value;\n If ( $type -eq \"STATISTIC_SERVER_SIDE_CURRENT_CONNECTIONS\" ){\n #Just use the low value. Odds are there aren't over 2^32 current connections. If your site is this big, you'll have to convert this to a 64 bit number.\n $cur_connections = $value.low; \n Write-Host \"Current Connections: $cur_connections\"\n }\n }\n \n If ($cur_connections -gt $connectionCount -and $elapsed.ElapsedMilliseconds -lt ($maxWaitTime * 1000)){\n Start-Sleep -Seconds 5\n }\n }\n }\n}\n\n#endregion\n\n#region Process\n\n#Load the F5 powershell iControl snapin\nAdd-PSSnapin iControlSnapin\nInitialize-F5.iControl -HostName $OctopusParameters['HostName'] -Username $OctopusParameters['Username'] -Password $OctopusParameters['Password']\n\nSet-F5PoolState -f5Pools $f5Pools\n\nIf (($OctopusParameters['LtmStatus'] -ne \"Enabled\") -and ($OctopusParameters['WaitForConnections'] -eq \"True\"))\n{\n Write-Host \"Waiting for connections to drain before deploying. This could take a while...\"\n Wait-ConnectionCount -f5Pools $f5Pools -maxWaitTime $maxWaitTime -connectionCount $killConnectionWhenLE\n \n #We have now waited the desired amount, go ahead and force offline and move on with deployment.\n Set-F5PoolState -f5Pools $f5Pools -forceOffline\n}\n\n\n#endregion\n\n",
"Octopus.Action.Script.ScriptFileName": null,
"Octopus.Action.Package.FeedId": null,
"Octopus.Action.Package.PackageId": null
},
"Category": "F5",
"HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/f5-enable-disable-multiple-pools.json",
"Website": "/step-templates/b3d76f46-e074-47ff-b800-3020c3f47866",
"Logo": "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",
"$Meta": {
"Type": "ActionTemplate"
}
}
Page updated on Friday, June 2, 2017