Now that the esteemed Chase Abbott has blazed the trail with all the iApps foundational goodness surrounding concepts and components in the first two articles in this Getting Started with iApps series, we can move into the practical! In this article, we'll start small, copying an existing iApp template and modifying it slightly.

The IP Forwarding iApp

Before we rip open the hood and dismount the engine, let's first take a walk through to evaluate the existing options.

As indicated in the video above, we're going to make a couple modifications to this iApp.

  1. I don't want the end user of the iApp to have ability to forward all traffic as a default option, regardless of IP version.
  2. I want to make sure the port is all ports; no port specification supported.
  3. I want to disable the advanced features altogether, removing the ability to specify the fastL4 profile and iRules.

Now that we've established what we're going to do, let's get it done!

Existing Template

The existing template is shown by section below: 

package require iapp 1.0.0
iapp::template start

set app         $tmsh::app_name
set advanced    [iapp::is ::options__advanced yes]

# destination & mask array
# array keys: $::basic__forward_all
array set destination {
    IPv4 { destination any:any mask 0.0.0.0 }
    IPv6 { destination 0::0.any mask 0::0 }
    *    { destination [iapp::destination $::basic__addr $::basic__port] \
           mask $::basic__mask \
           [expr { [string match "*:*:*" $::basic__addr] ? "source ::/0" : "" }]}
}

# PROFILES
set use_default_profile [iapp::is ::basic__l4_profile_to_use "/#default#"]
# array keys: $advanced,$use_default_profile
array set profiles {
    1,0     { profiles replace-all-with \{ $::basic__l4_profile_to_use \}  }
    *       { profiles replace-all-with \{ \
                [iapp::conf create \
                    ltm profile fastl4 ${app}_fastL4 \
                    defaults-from fastL4 \
                    reset-on-timeout disabled \
                    loose-close enabled \
                    loose-initialization enabled] \}  }
}

# VLANS
# array keys: $::basic__vlan_listening
array set vlans {
    enabled  { vlans-enabled vlans replace-all-with \{ $::basic__vlan_selections \} }
    disabled { vlans-disabled vlans replace-all-with \{ $::basic__vlan_selections \} }
    default  { vlans-disabled vlans none }
}

# RULES
set have_irules [expr { ![iapp::is ::irules__irules ""] }]

# array keys $advanced,$have_irules
array set rules {
    1,1     { rules \{ $::irules__irules \} }
    *       { rules none }
}

# VIRTUALS
set virtual { [iapp::conf create ltm virtual ${app}_VER_Forwarding \
    ip-forward \
    [iapp::substa destination(VER)] \
    [iapp::substa profiles($advanced,$use_default_profile)] \
    [iapp::substa vlans($::basic__vlan_listening)] \
    [iapp::substa rules($advanced,$have_irules)]] }

switch $::basic__forward_all {
    Yes     { subst [string map "VER IPv4" $virtual]
              subst [string map "VER IPv6" $virtual] }
    No      { subst [string map "VER IP" $virtual] }
    default { subst [string map "VER $::basic__forward_all" $virtual] }
}

iapp::template stop
section intro {
    message hello
    message check_for_updates
}

section options {
    choice display_help display "xxlarge" default "hide"


    optional ( options.display_help == "max" ) {
        message display_help_max
    }

    choice advanced display "xxlarge" default "no"


    optional ( options.display_help == "max" ) {
        message conf_mode_max
    }
}

section basic {
    choice forward_all display "xxlarge" default "No"

    optional ( options.display_help == "max" ) {
        message forward_all_max
    }
    optional ( forward_all == "No" ) {
        string addr default "0.0.0.0" required validator "IpAddress"
        optional ( options.display_help == "max" ) {
            message addr_max
        }
        string mask default "0.0.0.0" required validator "IpAddress"
        optional ( options.display_help == "max" ) {
            message mask_max
        }
        string port default "0" required validator "PortNumber"
        optional ( options.display_help == "max" ) {
            message port_max
        }
    }

    choice vlan_listening default "default" display "xxlarge"

    optional ( options.display_help == "max" ) {
        message vlan_listening_max
    }

    optional ( vlan_listening != "default" ) {
        multichoice vlan_selections display "xlarge" tcl {
            package require iapp 1.0.0
            set ::choices [iapp::get_items net vlan]
            return [iapp::safe_display ::choices]
        }

        optional ( vlan_listening == "enabled" ) {
            optional ( options.display_help == "max" ) {
                message vlan_selections_enabled_max
            }
        }

        optional ( vlan_listening == "disabled" ) {

            optional ( options.display_help == "max" ) {
                message vlan_selections_disabled_max
            }
        }
    }

    optional ( options.advanced == "yes" ) {
        choice l4_profile_to_use default "/#default#" display "xxlarge" tcl {
            package require iapp 1.0.0
            set ::choices "/#default#\n[iapp::get_items ltm profile fastl4]"
            return [iapp::safe_display ::choices]
        }

        optional ( options.display_help == "max" ) {
            message l4_profile_to_use_max
        }
    }
}

optional ( options.advanced == "yes" ) {
    section irules {
    message irule_1_max
        optional ( options.display_help == "max" ) {
            message irule_2_max
            message irule_3_max
        }

        multichoice irules display "xlarge" tcl {
            package require iapp 1.0.0
            set ::choices [iapp::get_items -filter NAME !~ "^_sys_" ltm rule]
            return [iapp::safe_display ::choices]
        }
    }
}

text {
    intro "Welcome to the IP Forwarding template"
    intro.hello "Introduction" "This template supports configuring the BIG-IP to perform IP traffic forwarding."
    intro.check_for_updates "Check for Updates" "Check for new versions of this template on the AskF5 Knowledge Base website (http://support.f5.com/kb/en-us/solutions/public/13000/400/sol13422.html)."

    options "Template Options"
    options.display_help "Do you want to see inline help?" {
         "Yes, show inline help" => "max",
         "No, do not show inline help" => "hide"
    }
    options.display_help_max "" "Inline help is available to provide contextual descriptions to aid in the completion of this configuration.  Select to show or hide the inline help in this template. Important notes and warnings are always visible, no matter which selection you make here."
    options.advanced "Which configuration mode do you want to use?" {
        "Basic - Use F5's recommended settings" => "no",
        "Advanced - Expose advanced options"    => "yes"
    }
    options.conf_mode_max "" "This template supports twp configurations modes. Basic mode exposes the most commonly used settings, and automatically configures the rest of the options.  Advanced mode allows you to review and change all settings."

    basic "Virtual Server Questions"
    basic.forward_all "Should this forward all traffic not destined for virtual servers?" {
        "Forward all IPv4 traffic" => "IPv4",
        "Forward all IPv6 traffic" => "IPv6",
        "Forward all IPv4 and IPv6 traffic" => "Yes",
        "Forward specific traffic" => "No"
    }
    basic.forward_all_max "" "Choose whether to have the BIG-IP system forward all traffic that is not destined for a virtual server, or only forward traffic on a specific network, port and/or VLAN that you define. If you select Yes, the system forwards all traffic.  If you select No, you must enter the address, mask, and port that define the traffic you want to forward."
    basic.addr "What network address do you want to use for this virtual server?"
    basic.addr_max "" "Specifies the address of the destination network, for example, 10.0.0.0.  The default 0.0.0.0 is a wildcard for all addresses, however you can still limit what is forwarded by netmask, port, or VLAN."

    basic.mask "What is the netmask for that address?"
    basic.mask_max "" "Specifies the netmask, for example 255.255.255.0. The default 0.0.0.0 is a wildcard for any netmask."

    basic.port "What port do you want this virtual server to use?"
    basic.port_max "" "Specifies a specific port for forwarding traffic, for example 22. The default 0 is a wildcard for any port."
    basic.vlan_listening "What VLANs should this IP Forwarding virtual server listen on?" {
        "All VLANs" => "default",
        "Enabled on ..." => "enabled",
        "Disabled on ..." => "disabled"
    }
      basic.vlan_listening_max "" "You can choose to forward traffic from all VLANs or just from some of them. If you don't want to enable all VLANs, you can choose to include or exclude named VLANs and forward traffic from all of the rest."
    basic.vlan_selections "On which VLAN(s) should this IP Forwarding virtual server listen?"
    basic.vlan_selections_enabled_max "" "Select the VLANs from which traffic will be forwarded."

    basic.vlan_selections_disabled_max "" "Select the VLANs from which traffic NOT will be forwarded."
    basic.l4_profile_to_use "What Fast L4 profile do you want to use?" {"Use F5's recommended Fast L4 profile" => "/#default#"}

    basic.l4_profile_to_use_max "" "If you created a custom Fast L4 profile, you can select it from the list. This is not required for most purposes, and only necessary if you need to replace the default Fast L4 profile functionality."

    irules "iRules"
    irules.irules "Do you want to add any custom iRules to this configuration?"

    irules.irule_1_max "WARNING" "Improper use or misconfigurations of an iRule can result in unwanted application behavior and poor performance of your BIG-IP system. For this reason we recommended you verify the impact of an iRule prior to deployment in a production environment."

    irules.irule_2_max "" "The BIG-IP system supports a scripting language to allow an administrator to instruct the system to intercept, inspect, transform, direct and track inbound or outbound application traffic. An iRule contains the set of instructions the system uses to process data flowing through it, either in the header or payload of a packet."

    irules.irule_3_max "" "Correct event priority is critical when assigning multiple iRules. For more information about iRule event priority, see https://devcentral.f5.com/wiki/iRules.priority.ashx"
}

To get started with modifying this template, we want to create a copy.

  1. In the GUI, click iApps->Templates->f5.ip_forwarding, and then at the very bottom, click copy.
  2. At the iApps >> Clone Template screen, change the Template Name(s) field to the name you want your template to have. I called mine f5_stripped.ip_forwarding. You can edit from here, but at this step I just clicked Finish before re-entering to do the editing work.
  3. Back at the iApps template listing, you should see your new iApp, which other than title, should be the same as the original, with the exception of validation (which should now read None.)
  4. Click into your new template and now we'll get to work editing.

Presentation

In the presentation section, we can weed out the basic/advanced section since we aren't allowing profile or iRule manipulation (lines 13-19.) Next we'll eliminate the forward_all option since we're not giving them the choice of of IPv4/IPv6 defaults or all networks (lines 23-28; 41.) Moving on, we'll trim the port section (lines 37-40.) Finally, we'll pull the advanced section altogether, which deals with the profiles and iRules (lines 69-96.)

In the text area of the presentation, we pull out all the unnecessary text now that those GUI objects will not be present (lines 110-14; 117-123; 130-131; 137; 142-153.)

This is best shown in the following images.

Presentation Diff 1

Presentation Diff 2

Implementation

The changes in the implementation section follow suit. We remove the arrays for destination (lines 7-15) and profiles (lines 17-29,) as well as the army for rules (lines 38-46,) and the switch for the type of forwarding virtual this will be since we're forcing end users to specify the networks (lines 56-61.) The only thing we need to slightly modify is the virtuals section (lines 48-54.) Here, we are removing the need for the variable and just creating the virtual server with the iapp::conf command. We are not passing a port from the iApp, so we use :any here (line 15) and then use the array as is for vlans to configure those. Screenshot to show these updates is below.

Implementation Diff

Easy enough! Updated iApp code is below.

package require iapp 1.0.0
iapp::template start

set app         $tmsh::app_name

# VLANS
# array keys: $::basic__vlan_listening
array set vlans {
    enabled  { vlans-enabled vlans replace-all-with \{ $::basic__vlan_selections \} }
    disabled { vlans-disabled vlans replace-all-with \{ $::basic__vlan_selections \} }
    default  { vlans-disabled vlans none }
}
iapp::conf create ltm virtual ${app}_IPv4_Forwarding \
    ip-forward \
    destination $::basic__addr:any mask $::basic__mask \
    [iapp::substa vlans($::basic__vlan_listening)]

iapp::template stop
section intro {
    message hello
    message check_for_updates
}

section options {
    choice display_help display "xxlarge" default "hide"
    optional ( options.display_help == "max" ) {
        message display_help_max
    }
}

section basic {

        string addr default "0.0.0.0" required validator "IpAddress"
        optional ( options.display_help == "max" ) {
            message addr_max
        }
        string mask default "0.0.0.0" required validator "IpAddress"
        optional ( options.display_help == "max" ) {
            message mask_max
        }


    choice vlan_listening default "default" display "xxlarge"

    optional ( options.display_help == "max" ) {
        message vlan_listening_max
    }

    optional ( vlan_listening != "default" ) {
        multichoice vlan_selections display "xlarge" tcl {
            package require iapp 1.0.0
            set ::choices [iapp::get_items net vlan]
            return [iapp::safe_display ::choices]
        }

        optional ( vlan_listening == "enabled" ) {
            optional ( options.display_help == "max" ) {
                message vlan_selections_enabled_max
            }
        }

        optional ( vlan_listening == "disabled" ) {

            optional ( options.display_help == "max" ) {
                message vlan_selections_disabled_max
            }
        }
    }
}

text {
    intro "Welcome to the IP Forwarding template"
    intro.hello "Introduction" "This template supports configuring the BIG-IP to perform IP traffic forwarding."
    intro.check_for_updates "Check for Updates" "Check for new versions of this template on the AskF5 Knowledge Base website (http://support.f5.com/kb/en-us/solutions/public/13000/400/sol13422.html)."

    options "Template Options"
    options.display_help "Do you want to see inline help?" {
         "Yes, show inline help" => "max",
         "No, do not show inline help" => "hide"
    }
    options.display_help_max "" "Inline help is available to provide contextual descriptions to aid in the completion of this configuration.  Select to show or hide the inline help in this template. Important notes and warnings are always visible, no matter which selection you make here."

    basic "Virtual Server Questions"
    basic.addr "What network address do you want to use for this virtual server?"
    basic.addr_max "" "Specifies the address of the destination network, for example, 10.0.0.0.  The default 0.0.0.0 is a wildcard for all addresses, however you can still limit what is forwarded by netmask, port, or VLAN."

    basic.mask "What is the netmask for that address?"
    basic.mask_max "" "Specifies the netmask, for example 255.255.255.0. The default 0.0.0.0 is a wildcard for any netmask."

    basic.vlan_listening "What VLANs should this IP Forwarding virtual server listen on?" {
        "All VLANs" => "default",
        "Enabled on ..." => "enabled",
        "Disabled on ..." => "disabled"
    }
    basic.vlan_listening_max "" "You can choose to forward traffic from all VLANs or just from some of them. If you don't want to enable all VLANs, you can choose to include or exclude named VLANs and forward traffic from all of the rest."
    basic.vlan_selections "On which VLAN(s) should this IP Forwarding virtual server listen?"
    basic.vlan_selections_enabled_max "" "Select the VLANs from which traffic will be forwarded."

    basic.vlan_selections_disabled_max "" "Select the VLANs from which traffic NOT will be forwarded."
}

With the finished stripped version of the ip forwarding iApp, we can now deploy the template in a new application service.

Join us for the next article in this series, where we'll tackle writing an iApp from scratch!