iControl Apps - #20 - Server Control
The issue of server state continues to come up in the DevCentral iControl forums so I figured I'd write an application that illustrates how to get and set the state of a server object and in doing so show how the iControl API methods map to the three way toggle state of enabled, disabled, forced offline in the BIG-IP admin GUI.
Usage
The arguments for this application are the BIG-IP address, username and password along with optional arguments for the object identifier and the toggle state. I've created a few hash tables to help with converting from the state parameter's values of "enabled", "disabled", and "offline" to the appropriate monitor and session state values.
param ( $bigip = $null, $user = $null, $pass = $null, $object = $null, $state = $null ); Set-PSDebug -strict; $MONITOR_STATE_HASH = @{}; $MONITOR_STATE_HASH.Add("enabled", "STATE_ENABLED"); $MONITOR_STATE_HASH.Add("disabled", "STATE_ENABLED"); $MONITOR_STATE_HASH.Add("offline", "STATE_DISABLED"); $SESSION_STATE_HASH = @{}; $SESSION_STATE_HASH.Add("enabled", "STATE_ENABLED"); $SESSION_STATE_HASH.Add("disabled", "STATE_DISABLED"); $SESSION_STATE_HASH.Add("offline", "STATE_DISABLED"); #------------------------------------------------------------------------- # function Write-Usage #------------------------------------------------------------------------- function Write-Usage() { Write-Host "Usage: ServerControl.ps1 host uid pwd [object [state]]"; Write-Host " object: address (Node) | address:port (Pool Member)"; Write-Host " state: enabled | disabled | offline"; exit; }
Initialization
The main app logic checks for whether the required BIG-IP connections parameters are supplied. If they are, it performs the initialization to load the iControl PowerShell SnapIn into the current Runspace so that the iControl calls are available to the script.
At this point if the object parameter was not supplied, it will call the local Get-ObjectState function with null as an argument. This will return results for all objects on the system. If a object is supplied but a state is not, then the Get-ObjectState function will be called for that object. Finally, if both an object and state are supplied, the script will attempt to set the object's state to the specified value.
function Do-Initialize() { if ( (Get-PSSnapin | Where-Object { $_.Name -eq "iControlSnapIn"}) -eq $null ) { Add-PSSnapIn iControlSnapIn } $success = Initialize-F5.iControl -HostName $bigip -Username $user -Password $pass; return $success; } if ( ($bigip -eq $null) -or ($user -eq $null) -or ($pass -eq $null) ) { Write-Usage; } if ( Do-Initialize ) { if ( ! $object ) { # List all Node Addresses and Pool Members Get-ObjectState -objects $null; } elseif ( ! $state ) { # List toggle state of provided object Get-ObjectState -objects (,$object); } else { if ( ($state -eq "enabled") -or ($state -eq "disabled") -or ($state -eq "offline") ) { # Set the specified object's toggle state Set-ObjectState -object $object -state $state; } else { Write-Usage; } } } else { Write-Error "ERROR: iControl subsystem not initialized" }
Determining The Toggle State
When querying the Toggle state (enabled, disabled, or offline), one must look at the monitor and session enabled states. The three possible combinations of these two values will yield the appropriate toggle states.
Monitor State | Session Enabled State | Toggle State |
---|---|---|
STATE_ENABLED | STATE_ENABLED | Enabled (All traffic allowed) |
STATE_ENABLED | STATE_DISABLED | Disabled (Only persistent or active connections allowed) |
STATE_DISABLED | STATE_DISABLED | Forced Offline (Only active connections allowed) |
The local Get-ToggleState function will take as input the monitor and session enabled state of an object and return the toggle state.
function Get-ToggleState() { param( [string]$monitor_state = $null, [string]$session_enabled_state = $null ); $state = $null; if ( $monitor_state -and $session_enabled_state ) { if ( ($monitor_state -eq "STATE_ENABLED") -and ($session_enabled_state -eq "STATE_ENABLED") ) { $state = "enabled"; } elseif ( ($monitor_state -eq "STATE_ENABLED") -and ($session_enabled_state -eq "STATE_DISABLED") ) { $state = "disabled"; } elseif ( ($monitor_state -eq "STATE_DISABLED") -and ($session_enabled_state -eq "STATE_DISABLED") ) { $state = "offline"; } } $state; }
Querying The Server State
The application was designed to allow either an address (node address) or address:port (pool member) for the input object. The Get-ObjectState function will determine whether we are looking for a node address or pool member and call the appropriate method depending on the object type.
The Get-NodeAddressState function will call the LocalLB.NodeAddress.get_monitor_status() and LocalLB.NodeAddresss.get_session_enabled_state() functions for the specified node address and display the determined toggle status value.
The Get-PoolMemberState function is a bit more complex because it needs to do a reverse lookup for all pools that have the specified pool member in it's configuration. When a match is found, the pool member along with it's associated pool and status are displayed.
function Get-ObjectState() { param([string[]]$objects = $null); if ( ! $objects ) { $objects = Get-AllObjects; } foreach ($object in $objects) { $tokens = $object.Split((, ":")); if ( $tokens.Length -eq 1 ) { # Node Address Get-NodeAddressState -address $tokens[0]; } elseif ( $tokens.Length -eq 2 ) { # Pool Member Get-PoolMemberState -address $tokens[0] -port $tokens[1]; } else { Write-Host "Invalid object '$object'"; } } } function Get-NodeAddressState() { param([string]$address = $null); $state = $null; if ( $address ) { $MonitorStatusA = (Get-F5.iControl).LocalLBNodeAddress.get_monitor_status( (,$address)); $monitor_state = "STATE_ENABLED"; if ( $MonitorStatusA[0] -eq "MONITOR_STATUS_FORCED_DOWN" ) { $monitor_state = "STATE_DISABLED"; } $EnabledStateA = (Get-F5.iControl).LocalLBNodeAddress.get_session_enabled_state( (,$address)); $session_enabled_state = $EnabledStateA[0]; $state = Get-ToggleState -monitor_state $monitor_state -session_enabled_state $session_enabled_state; New-ObjectStatus -object $address -state $state; } } function Get-PoolMemberState() { param([string]$address = $null, [string]$port = $null); if ( $address -and $port ) { $state = $null; $pool_list = (Get-F5.iControl).LocalLBPool.get_list(); $memberSessionStateAofA = (Get-F5.iControl).LocalLBPoolMember.get_session_enabled_state($pool_list); $monitorStatusAofA = (Get-F5.iControl).LocalLBPoolMember.get_monitor_status($pool_list); for ($i=0; $i-lt$pool_list.Length; $i++) { for($j=0; $j-lt$memberSessionStateAofA[$i].Length; $j++) { $memberSessionState = $memberSessionStateAofA[$i][$j]; $monitorStatus = $monitorStatusAofA[$i][$j]; if ( ($monitorStatus.member.address -eq $address) -and ($monitorStatus.member.port -eq $port) ) { # found a match $session_enabled_state = $memberSessionState.session_state; $monitor_state = "STATE_ENABLED"; if ( $monitorStatus.monitor_status -eq "MONITOR_STATUS_FORCED_DOWN" ) { $monitor_state = "STATE_DISABLED"; } $state = Get-ToggleState -monitor_state $monitor_state -session_enabled_state $session_enabled_state; New-ObjectStatus -object "${address}:${port}" -parent "$($pool_list[$i])" -state $state; } } } } }
Setting The Server State
Simliar to the Get-ObjectState function, the Set-ObjectState function will determine whether the object is a node address or pool member and call the appropriate function.
The Set-NodeAddressState function will call the LocalLB.NodeAddress.set_monitor_state() and LocalLB.NodeAddress.set_session_enabled_state() functions with the appopriate monitor and session state as described in the above table.
The Set-PoolMemberState function again will do a reverse lookup to find all containing pools and set the values accordingly for all pools that contain that pool member.
function Set-ObjectState() { param( [string]$object = $null, [string]$state = $null ); if ( $object -and $state ) { $tokens = $object.Split((,":")); if ( $tokens.Length -eq 1 ) { Set-NodeAddressState -address $tokens[0] -state $state; } elseif ( $tokens.Length -eq 2 ) { Set-PoolMemberState -address $tokens[0] -port $tokens[1] -state $state; } } } function Set-NodeAddressState() { param( [string]$address = $null, [string]$state = $null ); if ( $address -and $state ) { $monitor_state = $MONITOR_STATE_HASH[$state]; $session_state = $SESSION_STATE_HASH[$state]; if ( $monitor_state -and $session_state ) { (Get-F5.iControl).LocalLBNodeAddress.set_monitor_state( (,$address), (,$monitor_state)); (Get-F5.iControl).LocalLBNodeAddress.set_session_enabled_state( (,$address), (,$session_state)); Get-NodeAddressState -address $address; } } } function Set-PoolMemberState() { param( [string]$address = $null, [string]$port = $null, [string]$state = $null ); if ( $address -and $port -and $state ) { $pool_list = (Get-F5.iControl).LocalLBPool.get_list(); $memberDefAofA = (Get-F5.iControl).LocalLBPool.get_member($pool_list); $monitor_state = $MONITOR_STATE_HASH[$state]; $session_state = $SESSION_STATE_HASH[$state]; Write-Host "state: $state; monitor_state $monitor_state; session_state: $session_state"; for ($i=0; $i-lt$pool_list.Length; $i++) { for($j=0; $j-lt$memberDefAofA[$i].Length; $j++) { $member = $memberDefAofA[$i][$j]; if ( ($member.address -eq $address) -and ($member.port -eq $port) ) { # found a match $memberMonitorState = New-Object -TypeName iControl.LocalLBPoolMemberMemberMonitorState; $memberMonitorState.member = $member; $memberMonitorState.monitor_state = $monitor_state; (Get-F5.iControl).LocalLBPoolMember.set_monitor_state( (,$pool_list[$i]), (,(,$memberMonitorState)) ); $memberSessionState = New-Object -TypeName iControl.LocalLBPoolMemberMemberSessionState; $memberSessionState.member = $member; $memberSessionState.session_state = $session_state; (Get-F5.iControl).LocalLBPoolMember.set_session_enabled_state( (,$pool_list[$i]), (,(,$memberSessionState)) ) Get-PoolMemberState -address $address -port $port; } } } } }
Utilitiy Functions
What's an app without a bit of utility? The New-ObjectStatus function is mainly to allow PowerShell to build a nicely formatted table of the results. An object is created with the properties of Object, Parent, and State and those property values are populated with the input to the function and the resulting object is passed out through the pipeline.
function New-ObjectStatus() { param( [string]$object = $null, [string]$parent = $null, [string]$state = $null ); $o = $null; if ( $object -and $state ) { $o = 1 | select "Object", "Parent", "State"; $o.Object = $object; $o.State = $state; if ($parent) { $o.Parent = $parent; } } $o; }
Example session
foo
PS D:\> .\ServerControl.ps1 bigip user pass Object Parent State ------ ------ ----- 1.1.1.1 enabled 1.1.1.2 disabled 1.1.1.3 enabled 1.2.3.4 enabled 10.10.10.148 enabled 10.10.10.148:22 catbert-ssh enabled 10.10.10.148:80 catbert-http enabled 10.10.10.149 enabled 10.10.10.149:22 xpbert-ssh enabled 10.10.10.149:80 xpbert-http enabled 10.10.10.149:80 pool_1 enabled 10.10.10.149:80 xpbert-http enabled 10.10.10.149:80 pool_1 enabled 10.10.10.149:81 xpbert-http enabled 10.10.10.201 enabled 10.10.10.201:80 dc-sea-web enabled 10.10.10.202 enabled 10.10.10.202:80 dc-sea-web enabled 10.10.10.203 enabled 10.10.10.203:80 dc-sea-media enabled 10.10.10.204 enabled 10.10.10.211 enabled 10.10.10.211:80 dc-llix-web enabled 10.10.10.212 enabled 10.10.10.212:80 dc-llix-web enabled 10.10.10.213 enabled 10.10.10.213:80 dc-llix-media enabled 20.20.20.101 enabled 20.20.20.101:80 pool_1 enabled 20.20.20.102 enabled 20.20.20.102:80 pool_2 enabled 30.30.30.149 enabled PS D:\> .\ServerControl.ps1 bigip user pass 10.10.10.149:80 Object Parent State ------ ------ ----- 10.10.10.149:80 xpbert-http enabled 10.10.10.149:80 pool_1 enabled PS D:\> .\ServerControl.ps1 bigip user pass 10.10.10.149:80 disabled Object Parent State ------ ------ ----- 10.10.10.149:80 xpbert-http disabled 10.10.10.149:80 pool_1 disabled PS D:\> .\ServerControl.ps1 bigip user pass 10.10.10.149:80 offline Object Parent State ------ ------ ----- 10.10.10.149:80 xpbert-http offline 10.10.10.149:80 pool_1 offline PS D:\> .\ServerControl.ps1 bigip user pass 10.10.10.149 enabled Object Parent State ------ ------ ----- 10.10.10.149 enabled
Conclusion
This appliation illustrates the logic in emulating the functionality of the three-way toggle in the BIG-IP admin GUI with the approrpriate methods in the iControl API and gives you a tool to easily enable/disable servers.
You can download the full code for this script in the iControl CodeShare under PsServerControl.