iControlsh - An iControl Based Console BIG-IP Shell
I’ve been toying with the idea of building an iControl shell for a long time. For those who don’t know what I’m talking about, a shell is essentially a fully enclosed application that runs within a console on your computer. Windows has the Command Prompt and Unix has it’s various shells (sh, bash, ksh, etc) and since most of my working day involves interacting with a shell of some sort, I figured it might be useful to create an interactive BIG-IP shell.
For this example, I picked PowerShell as my language of choice for a couple of reasons: it already is a shell, I have tons of reference samples already, and it’s very extensible.
The Methodology
I wanted to design this application in such a way that it could be actively developed on without having to mess with the core engine. That way I could add more supported components and methods down the road in a very easy manner.
PowerShell has the concept of user defined “functions” and that ended up being the basis of my modular design. At the core of the code is a input processor that receives commands from the user and distributes them to the various registered components. It looks something like this:
1: while(1)
2: {
3: $prompt = "$(Get-CurrentPath)> ";
4: $i = (Read-Host $prompt).Trim();
5:
6: Debug-Message "< $($MyInvocation.MyCommand.Name) '$i' >";
7: if ( $i.Length -gt 0 )
8: {
9: Debug-Message "CommandLine: '$i'...";
10: switch -Wildcard ($i.ToLower())
11: {
12: "" { break; }
13: "c*" { Initialize-Connection $i; }
14: "h*" { Show-Help -Full -Topic (Get-Arguments $i); }
15: "i" { Show-Information; }
16: "q" { Exit-Console; }
17: "r" { Invoke-Expression $LoadModStr; }
18: "d" { $script:DEBUG = -not $script:DEBUG; }
19: "!*" {
20: $cmd = $i.Replace("!", "").Trim();
21: Debug-Message "Executing command $cmd";
22: Invoke-Expression $cmd;
23: }
24: ".." { Move-Up; }
25: default {
26: if ( $script:Connected )
27: {
28: if ( Is-SubDirectory $i ) { Move-Down $i; }
29: elseif ( Is-ValidCommand $i ) { Process-Command $i; }
30: else { Show-Help; }
31: }
32: else
33: {
34: Show-Help;
35: }
36: }
37: }
38: }
39: }
You’ll see that there are some other functions that I’ve written in there that do various things.
Get-CurrentPath
I wanted to include some sort of navigation into this shell to give the user some context as to where they were in the current command structure. This is very similar to how the various systems prompts will work with your current directory you are in. I opted for the format of icsh:/module/interface for my navigation format.
1: theboss.dev.net> : ?
2: Child Objects
3: ----------------
4: gtm
5: ltm
6: networking
7: system
8:
9: Commands In this object
10: -----------------------
11: gtm/wideip/list
12: ltm/pool/list
13: ltm/pool/members
14: ltm/pool/stats
15: ltm/poolmember/disable
16: ltm/poolmember/enable
17: ltm/poolmember/list
18: ltm/virtualserver/list
19: networking/vlan/list
20: system/systeminfo/list
21: theboss.dev.net> : gtm
22: theboss.dev.net/gtm> : wideip
23: theboss.dev.net/gtm/wideip> : ..
24: theboss.dev.net/gtm> : ..
25: theboss.dev.net> : ltm
26: theboss.dev.net/ltm> : pool
27: theboss.dev.net/ltm/pool> : ..
28: theboss.dev.net/ltm> : poolmember
29: theboss.dev.net/ltm/poolmember> : ..
30: theboss.dev.net/ltm> : ..
31: theboss.dev.net> : system
32: theboss.dev.net/system> : systeminfo
Initialize-Connection
This function will ensure that the iControl PowerShell subsystem is configured and will initialize the connection to the BIG-IP.
Show-Help
This function presents the global commands as well as any child objects (ie. “ltm” has “pool”, “poolmember”, “virtualserver”, etc") and the available sub commands.
Show-Information
This is a diagnostic function to print out shell version and various internal variable related to the state of the shell.
Exit-Console
This is a wrapper function to ensure that the users connection information is persisted to the registry so that it can be remembered the next time the shell is started.
Move-Up
Navigate up an object in the hierarchy.
Move-Down
Navigate down to a child of the current object.
Process-Command
This is where the fun happens. But, before I do, it will be a good idea to talk about how objects and methods are injected into the shell. There is a child directory where the main script resides called Modules. In there I’ve created a folder hierarchy to match the object hierarchy in the iControl API. The file system looks like this
1: c:\icontrolsh\iControlsh.ps1
2: c:\icontrolsh\modules\gtm
3: c:\icontrolsh\modules\gtm\wideip.ps1
4: c:\icontrolsh\modules\ltm
5: c:\icontrolsh\modules\ltm\pool.ps1
6: c:\icontrolsh\modules\ltm\poolmember.ps1
7: c:\icontrolsh\modules\ltm\virtualserver.ps1
8: c:\icontrolsh\modules\networking
9: c:\icontrolsh\modules\networking\vlan.ps1
10: c:\icontrolsh\modules\system
11: c:\icontrolsh\modules\system\systeminfo.ps1
The script does some file system navigation to determine all the unique “modules” in the modules directory and then for each module, it “dot sources” the child .ps1 files. “dot sourcing” is essentially running the script and it allows for any variable or function declarations to be included in the current runspace. The content for the ltm\virtualserver.ps1 script is as follows:
1: # Global Script variables
2:
3: if ( -not $script:ICONTROLSH )
4: {
5: Write-Host "This script is not intended to run standalone. Exiting...";
6: }
7:
8: #----------------------------------------------------------------------------
9: function icsh/ltm/virtualserver/list()
10: #----------------------------------------------------------------------------
11: {
12: <#
13: .SYNOPSIS
14: This function allows you to query the list of the virtual servers on a BIG-IP LTM.
15:
16: .DESCRIPTION
17: This function allows you to query the list of the virtual servers on a BIG-IP LTM.
18:
19: .EXAMPLE
20: list
21:
22: .NOTES
23: NAME: icsh/ltm/virtualserver/list
24: AUTHOR: Joe Pruitt, F5
25:
26: .LINK
27: http://devcentral.f5.com/s
28: #>
29: param($Opts);
30:
31: Debug-Message "< $($MyInvocation.MyCommand.Name) '$Opts' >";
32: $list = (Get-F5.iControl).LocalLBVirtualServer.get_list();
33: Write-Host "Virtual Server List";
34: Write-Host "-------------------";
35: foreach($pool in $list)
36: {
37: Write-Host "$pool";
38: }
39: }
PowerShell has a built-in help system that I’m able to make use of by including metadata within the <# and #> markers. You’ll notice that I included a check for a script variable that’s set in the iControlsh.ps1 script so that this script isn’t accidentally run on it’s own.
After all the scripts are “dot sourced”, the builtin Get-Command cmdlet can be used to list all of the defined iControl shell functions.
1: theboss.dev.net> : !! Get-Command icsh*
2: CommandType Name
3: ----------- ----
4: Function icsh/gtm/wideip/list
5: Function icsh/ltm/pool/list
6: Function icsh/ltm/pool/members
7: Function icsh/ltm/pool/stats
8: Function icsh/ltm/poolmember/disable
9: Function icsh/ltm/poolmember/enable
10: Function icsh/ltm/poolmember/list
11: Function icsh/ltm/virtualserver/list
12: Function icsh/networking/vlan/list
13: Function icsh/system/systeminfo/list
This get’s us back to the Process-Command function. With a little string manipulation it’s fairly easy to parse the function names for valid child elements as well as invoking the method calls with the PowerShell Invoke-Expression cmdlet.
1: #----------------------------------------------------------------------------
2: function Process-Command()
3: #----------------------------------------------------------------------------
4: {
5: param($cmd);
6: Debug-Message "< $($MyInvocation.MyCommand.Name) >";
7: $tokens = $cmd.Split(" ");
8: $command = $tokens[0];
9: $args = "";
10: for($i=1; $i-lt$tokens.Length; $i++)
11: {
12: if ( $i -gt 1 ) { $args += " "; }
13: $args += $tokens[$i];
14: }
15: $key = $(Get-CurrentPath) + "/" + $command;
16: $code = Gen-FunctionName $command;
17:
18: Debug-Message "Processing command: '$code' '$args'";
19: if ( $args.Length -gt 0 ) { $code += " -Opts '$args'"; }
20: Debug-Message "Invoking code <$code>";
21: Invoke-Expression $code;
22: }
The Full Source
The source for this application can be found in the iControl CodeShare under “PsBigipInteractiveShell”.
Video Walkthrough
There’s a lot of code to review in an article like this and I’m going to leave it to you all to view the source in the wiki. In the meantime, you can watch this short video I’ve recorded illustration a sample session within the shell.
Related Articles on DevCentral
- F5 DevCentral > Hot Topics > iControl
- What Does BIG-IP Stand For?
- F5 DevCentral > Community > Group Details - Microsoft PowerShell ...
- Audio White Paper - Managing BIG-IP Devices With HP and Microsoft ...
- DevCentral Wiki: iControl Wiki Home
- F5 DevCentral > Community > Group Details - iControl Assembly
- Self-Contained BIG-IP Provisioning with iRules and pyControl ...
- Java iControl Objects - LTM Pool Member > DevCentral > F5 ...
- Audio White Paper - F5 iControl
- Introducing PoshBing – The PowerShell library for Microsoft's Bing ...
- L4L7_53191
Nimbostratus
Very, very nicely done Joe - impressive stuff!