F5 - Enable or Disable Multiple Pools

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.

Report an issue

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"
  }
}

History

Page updated on Friday, June 2, 2017