Creating an iApp Template from a Working Config

This is a description of how to build an iApp Template from a working config defined in bigip.conf. This is just one of many ways of going about writing an iApp Template, but it is one that has been used successfully for a number of iApps. This process may not be appropriate for all templates. It is fairly tedious to do it this way, but it can be very helpful for complicated configs and it can be useful is you don’t know how to create the config that you want using TMSH. It also has the benefit of starting with something that works and that you can compare against easily. This process is almost a requirement for configs involving the APM module.

Preparing the Configuration

1. To begin with, start with a clean BIG-IP. You don’t want extra objects on there to confuse the situation. It is a really good idea to save a copy of the file /config/bigip.conf when the system is clean and has no config on it yet. You can use this later to help determine what is basic to this file that is not a part of your config.
a. If you can’t get your system to a clean state, make a copy of your /config/bigip.conf before you start so that you have a complete list of those objects that you don’t want to include in your iApp template.
2. Build the solution that you eventually want the template to create manually through the GUI.
3. Completely test the solution to ensure that you are starting with the very best configuration possible.
4. Backup the configuration by making a copy of the file /config/bigip.conf. Copy this backup off the BIG-IP for safe keeping.

Getting the Template Started

1. Create a new template and open if for editing in whichever way works best for you. (If you need some help with this step, see iApp Lab 1 - Hello World.)
2. In the Presentation section, start with just a basic message telling the user the name of your template. You don’t have any options yet, so the only thing the user will be able to do is submit the template. That is fine for now.
3. In the Implementation section, we are going to start with the contents of the file /config/bigip.conf. Copy the contents of bigip.conf and paste them into the implementation section of your template. This is not going to work as first. This is just the starting point. We have a whole process to go through to turn this into working code.

Converting the Config to Working Template Code

There are a whole series of steps to go through to transform the config that you just pasted into the implementation section. This discussion cannot cover what to do in every situation, but it should provide good advice for how to do most things.

Cleanup the Config

The file bigip.conf contains the config that you have created, but just because something is in bigip.conf, doesn’t meant that you want to try to create it in your template. There are boiler-plate objects in there and also objects that get created automatically when create other objects. You don’t want to try to create either of these things in your template. It will result in errors. The following is a good start to getting rid of the extra cruft.
1. Remove any extraneous config objects. This is where the bigip.conf from when the system was clean (or from when you started) comes in handy. If you open that file, you will see that it isn’t blank. There are some objects defined in there even when the system is fresh and new. You don’t need to recreate those objects. Remove their reference from your Implementation section.
2. Remove all of the ‘/ltm node’ objects. These are created automatically by the system when the pools are created.
There may be other objects that still don’t belong, but we will have to find them later.

Making the Initial Conversion

Now we edit the contents of the Implementation section to turn it into actual Tcl code. Your implementation section contains a series of object definitions of the form
<TMSH Path> /<Partition Path>/<Object Name> {<Object Definition> }

For example,
ltm pool /Common/my_pool {
    members {
        /Common/10.133.21.103:http {
            address 10.133.21.103
        }
    }
    monitor /Common/my_http_monitor
    slow-ramp-time 300
}

This is going to have to be processed to turn it into usable code.
1. Remove the Partition Path. You don’t want to specify this in templates in most cases. The iApp system will automatically place the objects in the correct path. If you created your config in the Common partition, then the partition path is ‘/Common/’. You can remove all of them using a global search-and-replace.
2. Remove the outer curly braces from around the Object Definition, without removing any internal curly braces. Now you should have something that look more like this.
<TMSH Path> <Object Name> <Object Definition>

For example,
ltm pool my_pool
    members {
        10.133.21.103:http {
            address 10.133.21.103
        }
    }
    monitor my_http_monitor
    slow-ramp-time 300

3. Now, encase each object inside a call to tmsh::create. It should look like this
tmsh::create {
ltm pool my_pool
    members {
        10.133.21.103:http {
            address 10.133.21.103
        }
    }
    monitor my_http_monitor
    slow-ramp-time 300
}

4. Replace every instance of ‘members {‘ with ‘members replace-all-with {‘
5. Replace every instance of ‘profiles {‘ with ‘profiles replace-all-with {‘
6. Replace every instance of ‘persist {‘ with ‘persist replace-all-with {‘
7. If you have GTM objects, replace every instance of ‘pools {‘ with ‘pools replace-all-with {‘

Adding Escape Characters

If your config contains iRules, you may need to escape some special characters to get this to run correctly.
1. inside the iRule definition,
  1. Replace all instances of $ with \$
  1. Replace all instances of [ with \[
  1. Replace all instances of ] with \]
  1. Replace all instances of ” with "

This will keep the Tcl interpreter from trying to do variable substitution or trying to run procs that don’t exist.

Testing the Conversion

In many cases, this will work now. In some cases, there are further edits that need to be made. You will need to test the template and see if it is working yet, and fix any problems that may arise.
1. Now is a good time to save your template. If you are editing outside of the BIG-IP, import your template back in.
2. Before you run your template, you want to remove your original config. The easiest way to do this is to replace the file /config/bigip.conf with the one you saved when your system was clean. Then run the command ‘tmsh load sys config’ from the command line.
3. Now you want to run your template and test to see that it works. If you have errors, correct them before continuing.
a. One common problem that you might run into is that some object types will have fields in them that are auto-generated by the system when the object is created. These fields should be removed from your Implementation section. You will be able to find these because you will get an error message complaining about unknown fields when you try to run the template. A good example of this is the ‘revision’ or ‘cache-path’ fields in APM policy objects.

 policy customization-group /Common/atest_citrix_apm_Connectivity_customization {
    app-service /Common/atest.app/atest
    cache-path /config/filestore/files_d/Common_d/customization_group_d/:Common:atest_citrix_apm_Connectivity_customization_1
    revision 1
    type secure-access-client
}

If you try to set the revision field with a TMSH create command, for example, you get the following error:
Command failed: tmsh::create apm policy customization-group /Common/atest_citrix_apm_Connectivity_customization { type secure-access-client revision 4 } "revision" read-only property

4. Make sure that your template is creating the config that you want by comparing the new /config/bigip.conf with the one that you saved containing your original config. They should be the same, except that
  1. The objects get created inside the template’s folder.
b. Each object now contains a new field, app-service, which specifies which iApp Application Service owns the object.


Making Your Template Dynamic

At this point in the process, your template runs correctly, but it always makes the same static config. In most cases, this is not the desired final product. In most cases, you want to give your user the ability to make choices about the config. This is done by adding content to the Presentation section to gather the information, and then using that input in the Implementation section to make choices or fill in content. In some cases, you will only want a few inputs in the Presentation section, while in others, your Presentation section might get very involved. This discussion cannot cover every possible case, but here are some things that you may want to do to your template.

Personalizing Object Names

Not happy with your original object names? Now is a good time to edit them to better fit the image you want to present. You can also make the names dynamicly generated, perhaps by beginning them all with the name of the iApp Application Service that owns them. This is how you do that. For each object instance,
1. Set the object name as a combination of the app name and the original object name, like this:
set object_name [format "%s_%s" $tmsh::app_name original_object_name]

2. Then replace the object name in the tmsh::create statement with the new one variable that you just set, like this:
set object_name [format "%s_%s" $tmsh::app_name my_pool]
tmsh::create {
ltm pool  $object_name
    members replace-all-with {
        10.133.21.103:http {
            address 10.133.21.103
        }
    }
    monitor my_http_monitor
    slow-ramp-time 300
}

Inserting User Input

Let’s say that you want the user to be able to select the load balancing method and the monitor on the pool that you are creating. Here is how you do that, using the example from above.
1. In the Presentation section, add two choice elements to collect the information that you want. For example,
section pool_choices {
    choice monitor_name display "xlarge" tcl {
        tmsh::run_proc f5.app_utils:get_ltm_monitors_filter http
    }

    choice lb_method_choice default "round-robin" display "xlarge" {
        "Dynamic Ratio (member)" => "dynamic-ratio-member",
        "Dynamic Ratio (node)" => "dynamic-ratio-node",
        "Fastest (application)" => "fastest-app-response",
        "Fastest (node)" => "fastest-node",
        "Least Connections (member)" => "least-connections-member",
        "Least Connections (node)" => "least-connections-node",
        "Least Sessions" => "least-sessions",
        "Observed (member)" => "observed-member",
        "Observed (node)" => "observed-node",
        "Predictive (member)" => "predictive-member",
        "Predictive (node)" => "predictive-node",
        "Round Robin" => "round-robin",
        "Ratio (member)" => "ratio-member",
        "Ratio (node)" => "ratio-node",
        "Ratio (session)" => "ratio-session",
        "Ratio Least Connections (member)" => "ratio-least-connections-memeber",
        "Ratio Least Connections (node)" => "ratio-least-connections-node",
        "Weighted Least Connections (member)" => "weighted-least-connections-member",
        "Weighted Least Connections (node)" => "weighted-least-connections-node"
    }
}

2. In the implementation section, insert the result, like this:
set object_name [format "%s_%s" $tmsh::app_name my_pool]
tmsh::create {
    ltm pool  $object_name
        members replace-all-with {
                10.133.21.103:http {
                address 10.133.21.103
            }
    }
    monitor $::pool_choices__monitor_name
    slow-ramp-time 300
    load-balancing-mode $::pool_choices__lb_method_choice
}

Letting the User Define the Pool Members

In almost every case, the pools’ members need to be selected dynamically by the user when they run the template. Here is an example of how to do that.
1. In the Presentation section, add a table to gather the pool member choices. For example,
section pool_choices {
    lb_method lb_method_choice

    table servers {
        string addr required validator "IpAddress"
        string port default "80" required validator "PortNumber" display "small"
        string connection_limit default "0" required validator "NonNegativeNumber" display "small"
        optional ( lb_method_choice == "ratio-member" ||
            lb_method_choice == "ratio-node" ||
            lb_method_choice == "ratio-session" ||
            lb_method_choice == "ratio-least-connections-memeber" ||
            lb_method_choice == "ratio-least-connections-node" ||
            lb_method_choice == "dynamic-ratio-member" ||
            lb_method_choice == "dynamic-ratio-node" ) {
            string ratio default "1" validator "NonNegativeNumber" display "small"
        }
    }

    choice monitor_name display "xlarge" tcl {
        tmsh::run_proc f5.app_utils:get_ltm_monitors_filter http
    }

2. in the Implementation section, process the results of that input, like this:
set servers $::pool_choices__servers
if { $servers == "" } {
    set members action "members none"
} else {
    set members \{
    foreach server $servers {
        append members [tmsh::get_field_value $server addr]
        append members ":"
        append members [tmsh::get_field_value $server port]
        append members " \{ connection-limit "
        append members [tmsh::get_field_value $server connection_limit]
        if { [tmsh::run_proc f5.app_utils:is_lb_method_ratio $lb_method] } {
            append members " ratio "
            append members [tmsh::get_field_value $server ratio]
        }
        append members " \}"
        append members " "
    }
    append members \}
    set members action "members replace-all_with $members"
}

set object_name [format "%s_%s" $tmsh::app_name my_pool]
tmsh::create {
    ltm pool  $object_name
    $members_action
    monitor $::pool_choices__monitor_name
    slow-ramp-time 300
    load-balancing-mode $::pool_choices__lb_method_choice
}

Other Common Things To Change

The number of ways that you can make your config dynamic are endless. It just depends on what you want to present as options to your user. You want to make your template flexible enough that it is useful for all of the common ways that you or your user want to use it to deploy iApp Application Services. We’ll close this discussion with a short, non-exhaustive list of common things that you might want to make dynamic.
1. Allow the user set the IP address and port values for virtual servers.
2. Allow the user choose SSL Keys and Certificates.
3. Allow the user define their own monitor send and receive strings.
4. Give the user choices for how to optimize HTTP traffic for their environment.
5. Give the user choices about what VLANs the virtual servers should answer on.
6. Etc…
—-

The BIG-IP API Reference documentation contains community-contributed content. F5 does not monitor or control community code contributions. We make no guarantees or warranties regarding the available code, and it may contain errors, defects, bugs, inaccuracies, or security vulnerabilities. Your access to and use of any code available in the BIG-IP API reference guides is solely at your own risk.