tmsh2iapp's most recent version 20171013.2 (commit 2127b835a937ea88a914745bb820e6c7c8d0f2a2) doesn't handle deployment scenarios within partition with a non-default (non-zero) route domain well.
This is a combination of BIG-IP behaviour and iApp behaviour. If using iWorkflow 2.3.0 with multi-tenancy enabled you WILL run into this issue if trying to use tmsh2iapp to generate iApp templates for import to iWorkflow.
e.g. if deploying within partition TENANT that has non-default route domain ID 123, with a pool member address of 203.0.113.4:80 it will try to create a node named "203.0.113.4", if no node yet exists in the partition the node will be created successfully and BIG-IP will automatically append %123 to it.
If an existing node for 203.0.113.4 has already been created BIG-IP will think of the node as 203.0.113.4%123 and deployment of the iApp will error as it a node named 203.0.113.4%123 with address 203.0.113.4%123 already exists YET the iApp is telling BIG-IP to create a node named 203.0.113.4 with address 203.0.113.4%123 ... the append of %123 by BIG-IP is implicit given the context of being within a partition with non-default route domain.
NOTE: The iApp it creates will work in the above situation with non-zero route domains provided the user understands that the need to postfix %N route domain identifiers to pool member addresses.
However; assuming the user will remember to do that, or that a third party system leveraging the API will know to postfix a route domain identifier that it may know nothing about, is dumb.
A better approach to this is to have the generated iApp determine the partition, then determine the default route domain for the partition, and if it is a non-zero route domain postfix %N to the node address.
This can be done with the following the iApp implementation code, the following as an example shell for implementation section within iApp when pm__pool_name is used.
puts "Starting iApp $tmsh::app_name.app generated with tmsh2iapp version 20171013.2"
set partition "/[lindex [split [tmsh::pwd] /] 1]"
set partition_name "[lindex [split [tmsh::pwd] /] 1]"
set obj [tmsh::get_config auth partition $partition_name default-route-domain]
set routedomainid [tmsh::get_field_value [lindex $obj 0] default-route-domain]
puts "The iApp is being instantiated in @partition $partition with default RD%$routedomainid"
if { $partition == "/" } { puts "Warning: unexpected behaviour when @partition variable is to \"/\"" }
set cfg { %BREVITY% }
set cfg [string map "@service_folder $tmsh::app_name.app @partition $partition %BREVITY% __app_service__ $tmsh::app_name.app/$tmsh::app_name " $cfg]
set fileId [open /var/tmp/cslab_salt_stack_master_v1.0.13.cfg "w"]
puts -nonewline $fileId $cfg
close $fileId
tmsh::load sys config merge file /var/tmp/cslab_salt_stack_master_v1.0.13.cfg
if {([info exists {::pm__%POOL_NAME%}]) && ([string length ${::pm__%POOL_NAME%}] > 0)} {
if {$routedomainid != 0} {
set cmd "tmsh::modify ltm pool %POOL_NAME% { members replace-all-with { [string map ": %${routedomainid}:" ${::pm__%POOL_NAME%}] } }"
} else {
set cmd "tmsh::modify ltm pool %POOL_NAME% { members replace-all-with { ${::pm__%POOL_NAME%} } }"
}
puts "$cmd"
eval $cmd
if {([info exists {::pm__%POOL_NAME%_properties}]) && ([string length ${::pm__%POOL_NAME%_properties}] > 0)} {
set cmd "tmsh::modify ltm pool %POOL_NAME% { members modify { all { ${::pm__%POOL_NAME%_properties} } } }"
puts "$cmd"
eval $cmd
}
}
puts "Finished iApp $tmsh::app_name.app generated with tmsh2iapp version 20171013.2"
A helper function that may also be useful, which I've taken from the application services integration iApp, may also be useful to insert for other situations in which tmsh2iapp has to iterate through IP addresses to determine if it should append %N route domain identifiers or not.
# Check to see if an ip has a routedomain included.
# Return: 0=false; 1=true
proc has_routedomain { ip } {
return [string match *%* $ip]
}