This repository has been archived by the owner on Dec 4, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 118
/
Copy pathf5.aws_advanced_ha.v1.4.0rc3.tmpl
3835 lines (3367 loc) · 152 KB
/
f5.aws_advanced_ha.v1.4.0rc3.tmpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Copyright 2021 F5 Networks, Inc. See End User License Agreement (EULA) for
# license terms. Notwithstanding anything to the contrary in the EULA, Licensee
# may copy and modify this software product for its internal business purposes.
# Further, Licensee may upload, publish and distribute the modified version of
# the software product on devcentral.f5.com.
cli script f5.iapp.1.5.2.cli {
# Initialization proc for all templates.
# Parameters "start" and "stop" or "end".
proc iapp_template { action } {
switch $action {
start {
catch { tmsh::modify sys scriptd log-level debug }
set ::clock_clicks [clock clicks]
puts "\nStarting iApp $tmsh::app_template_name [clock format \
[clock seconds] -format {%m/%d/%Y %T}]\n"
tmsh::log info "Starting iApp template $tmsh::app_template_name"
}
stop -
end {
if { [info exists ::substa_debug] } {
puts $::substa_debug
}
puts "\nEnding iApp $tmsh::app_template_name [clock format \
[clock seconds] -format {%m/%d/%Y %T}]\nRun time [expr \
{ ([clock clicks] - $::clock_clicks) / 1000 }] msec\n"
tmsh::log info "Ending iApp template $tmsh::app_template_name"
}
}
set ::HTTP_CONTENT_TYPES { application/(css\|css-stylesheet\|doc\|excel\|javascript\|json\|lotus123\|mdb\|mpp\|msaccess\|msexcel\|ms-excel\|mspowerpoint\|ms-powerpoint\|msproject\|msword\|ms-word\|photoshop\|postscript\|powerpoint\|ps\|psd\|quarkexpress\|rtf\|txt\|visio\|vnd\\.excel\|vnd\\.msaccess\|vnd\\.ms-access\|vnd\\.msexcel\|vnd\\.ms-excel\|vnd\\.mspowerpoint\|vnd\\.ms-powerpoint\|vnd\\.ms-pps\|vnd\\.ms-project\|vnd\\.msword\|vnd\\.ms-word\|vnd\\.ms-works\|vnd\\.ms-works-db\|vnd\\.powerpoint\|vnd\\.visio\|vnd\\.wap\\.cmlscriptc\|vnd\\.wap\\.wmlc\|vnd\\.wap\\.xhtml\\+xml\|vnd\\.word\|vsd\|winword\|wks\|word\|x-excel\|xhtml\\+xml\|x-java-jnlp-file\|x-javascript\|x-json\|x-lotus123\|xls\|x-mdb\|xml\|x-mscardfile\|x-msclip\|x-msexcel\|x-ms-excel\|x-mspowerpoint\|x-msproject\|x-ms-project\|x-msword\|x-msworks-db\|x-msworks-wps\|x-photoshop\|x-postscript\|x-powerpoint\|x-ps\|x-quark-express\|x-rtf\|x-vermeer-rpc\|x-visio\|x-vsd\|x-wks\|x-word\|x-xls\|x-xml) image/(photoshop\|psd\|x-photoshop\|x-vsd) text/(css\|html\|javascript\|json\|plain\|postscript\|richtext\|rtf\|vnd\\.wap\\.wml\|vnd\\.wap\\.wmlscript\|wap\|wml\|x-component\|xml\|x-vcalendar\|x-vcard) }
}
proc iapp_is { args } {
set up_var [lindex $args 0]
upvar $up_var var
if { [info exists var] } {
foreach val [lrange $args 1 end] {
if { [subst $var] eq $val } {
return 1
}
}
}
return 0
}
proc iapp_substa { args } {
upvar substa_in argx \
substa_out rval
set argx $args
# check the explicit value first.
# multiple layers of variable substitution requires multiple subst.
# error occurs here if any of the substituted variables do not exist
# valid wildcard (*) array entries will fail here first.
uplevel {
append ::substa_debug "\n$substa_in"
if { [info exists [set substa_in]] } {
set substa_out [subst $$substa_in]
set substa_out [subst $substa_out]
} else {
# since explicit value did not exist, try a wildcard value.
# substitute "*" as the array key and repeat.
set substa_tmp [split $substa_in "()"]
set substa_in "[lindex $substa_tmp 0](*)"
append ::substa_debug "*"
if { [info exists [set substa_in]] } {
set substa_out [subst $$substa_in]
set substa_out [subst $substa_out]
} else {
error "substa \"$substa_in\" array value not found"
}
}
}
return $rval
}
proc iapp_conf { args } {
# Return value $object_name is set to the first word in $arg that
# contains an underscore, since the position of the object name in
# tmsh syntax is not consistent.
set args [join $args]
set object_name [lindex $args [lsearch -glob $args "*_*"]]
# Global array ::tmsh_history persists between calls to iapp_conf
# in order to suppress duplicate commands.
if { ![info exists ::tmsh_history($args)] } {
set ::tmsh_history($args) 1
iapp_debug $args
switch -exact -- [string range $args 0 5] {
create { tmsh::create [string range $args 7 end] }
modify { tmsh::modify [string range $args 7 end] }
delete { tmsh::delete [string range $args 7 end] }
default { error "iapp_conf illegal parameter" }
}
}
return $object_name
}
proc iapp_make_safe_password { password } {
return [string map { \' \\\' \" \\\" \{ \\\{ \} \\\} \; \\\; \| \\\| \# \\\# \ \\\ \\ \\\\ } $password]
}
proc iapp_pull { loc items_list } {
upvar $items_list items
if { [set item [lindex $items $loc]] != "" } {
set items [lreplace $items $loc $loc]
}
return $item
}
proc iapp_process_flags { flags_array args_list } {
upvar $flags_array flags
upvar $args_list args
if { [set dubdash [lsearch $args "--"]] != -1 } {
set args [lreplace $args $dubdash $dubdash];
} else {
set dubdash end
}
foreach flag [array names flags] {
while { [set ptr [lsearch [lrange $args 0 $dubdash] $flag]] != -1 } {
set args [lreplace $args $ptr $ptr];
# we want to run the code in the flags_array at the calling
# proc's level so that the variables that it sets up are
# available there.
set access_var [format "$%s(%s)" $flags_array $flag]
set command [subst -nocommands { set ptr $ptr ; subst $access_var }]
uplevel 1 $command
}
}
return $args
}
proc iapp_tmos_version { args } {
set cversion [tmsh::version]
if { $cversion eq "" } {
tmsh::log err "unable to determine TMOS version"
error "unable to determine TMOS version"
}
# if no op+version was specified, just return the version
if { $args eq "" } { return $cversion }
if { [llength $args] > 2 } {
error "Too many arguments"
}
set op [lindex $args 0]; # operator
set NOTFOUND -1
# constrain to valid operators - adding more is fine as long as
# they are supported by [expr] (and makes sense)
if { [lsearch -exact { < > <= >= == != } $op] == $NOTFOUND } {
tmsh::log err "illegal operator: $op"
error "illegal operator: $op"
}
set tversion [lindex $args 1]; # target version
# one or two decimal digits, optionally followed by 0-2 complete groups of
# dots followed by one or two decimal digits with nothing before or after
set regex {^\d{1,2}(\.\d{1,2}){0,2}$}
if { ! [regexp -- $regex $tversion] } {
tmsh::log err "cannot parse version from: $tversion"
error "cannot parse version from: $tversion"
}
# p=>prefix, c=>current, t=>target
foreach p { c t } {
# extract major/minor/point components
scan [set [set p]version] "%d.%d.%d" [set p]mjr [set p]mnr [set p]pnt
# ensure that these are each set to at least 0
foreach level { mjr mnr pnt } {
if { ! [info exists [set p]${level}] } { set [set p]${level} 0 }
}
# turn them into one big number that we can compare
# leave room in-between just to be safe
set [set p]num [expr {
[set [set p]mjr]*1000000 +
[set [set p]mnr]*10000 +
[set [set p]pnt]*100
}]
}
# a simple numeric comparison is all that is needed at this point
return [eval expr $cnum $op $tnum ]
}
proc iapp_safe_display { args } {
# strings sent to APL must be truncated to 65535 bytes, see BZ435592
if { [string length [set [set args]]] > 65535 } {
set last_newline [string last "\n" [set [set args]] 65500]
return "[string range [set [set args]] 0 $last_newline]Error: Too many items for display"
} else {
return [set [set args]]
}
}
proc iapp_get_items { args } {
# Set default values.
set error_msg "iapp_get_items $args:"
set do_binary 0
set nocomplain 0
set items ""
set join_char "\n"
set recursive "recursive"
set com_dir "/Common"
set loc_dir "[tmsh::pwd]"
# Set up flag-related work.
array set flags {
-exists { [set do_binary 1] }
-nocomplain { [set nocomplain 1] }
-list { [set join_char " "] }
-norecursive { [set recursive ""] }
-local { [set com_dir ""] }
-dir { [set loc_dir [iapp_pull $ptr args]] }
-filter { [set filter_field [iapp_pull $ptr args]] \
[set filter_op [iapp_pull $ptr args]] \
[set filter_value [iapp_pull $ptr args]] }
}
iapp_process_flags flags args
# Get system object names in all requested directories.
set save_dir [tmsh::pwd]
foreach dir [lsort -unique "$com_dir $loc_dir"] {
tmsh::cd $dir
set tmsh_rval [catch {
foreach obj [tmsh::get_config $args $recursive] {
if { [info exists filter_field] } {
if { $filter_field eq "NAME" } {
set val [tmsh::get_name $obj]
} else {
# If get_field_value throws error, assume "none" value
if { [catch {
set val [tmsh::get_field_value $obj $filter_field]
# strip quotes per BZ442531
set val [string map {\" ""} $val]
}]} { set val none }
}
# Non-Tcl operators =~ and !~ added for extra flexibility
if { $filter_op eq "=~" } {
set filter "\[regexp \"$filter_value\" \"$val\"\]"
} elseif { $filter_op eq "!~" } {
set filter "!\[regexp \"$filter_value\" \"$val\"\]"
} else {
set filter "\\\"$val\\\" $filter_op \\\"$filter_value\\\""
}
# If filter fails, skip to next object
if { ![eval expr $filter] } {
continue
}
}
# string map catches /Common added by ltm profile ntlm,
# which is unlike all other ltm profile return values.
lappend items $dir/[string map {/Common/ ""} [tmsh::get_name $obj]]
}
} err ]
}
tmsh::cd $save_dir
# array keys: $do_binary,$tmsh_rval,$nocomplain. Do not insert whitespace.
array set rval {
0,0,0 {[join $items $join_char]}
0,0,1 {[join $items $join_char]}
0,1,0 {[error "$error_msg $err"]}
0,1,1 {}
1,0,0 {[llength $items]}
1,0,1 {[llength $items]}
1,1,0 {0}
1,1,1 {0}
}
return [subst $rval($do_binary,$tmsh_rval,$nocomplain)]
}
proc iapp_get_provisioned { args } {
array set lnum {
none 0
minimum 1
nominal 2
dedicated 3
}
# Set defaults.
set required minimum
set do_binary 1
# Set up flag-related work.
array set flags {
-is { [set required [iapp_pull $ptr args]] }
-level { [set do_binary 0] }
}
iapp_process_flags flags args
if { [llength $args] > 1 } {
error "Too many arguments"
}
# If checking for AM provisioning on TMOS < 11.4,
# check for WAM provisioning instead.
if { $args eq "am" && [iapp_tmos_version < 11.4] } {
set args "wam"
}
# Get the provisioning level. If blank, assume none.
# Proc only checks 1 module at a time, so only 1 object is returned.
if { [catch {
set obj [tmsh::get_config sys provision $args]
set level [tmsh::get_field_value [lindex $obj 0] level]
}]} { set level none }
if { $do_binary } {
return [expr { $lnum($level) >= $lnum($required) }]
} else {
return $level
}
}
proc iapp_get_user { args } {
# Set defaults.
set do_role 0
set do_binary 0
# Set up flag-related work.
array set flags {
-is_admin { [set do_binary 1] }
}
iapp_process_flags flags args
if { [llength $args] > 1 } {
error "Too many arguments"
}
# Show user auth was introduced in v11.6
set user "unknown"
catch {
set user [tmsh::show auth user field-fmt]
} err
if { $do_binary } {
return [expr { $user == "unknown"
|| [string first "role " $user] == -1
|| [string first "role admin" $user] != -1
|| [string first "role resource-admin" $user] != -1 }]
} else {
return $user
}
}
proc iapp_destination { args } {
# Set defaults. Flag actions may overwrite defaults later.
set route_domain 0
set do_mask 0
set port 0
# Set up flag-based actions.
array set flags {
-route_domain { [set route_domain [iapp_pull $ptr args]] }
-mask { [set do_mask 1] }
-length { [set cidr_bits [iapp_pull $ptr args]] }
}
if { [llength [set non_switches [iapp_process_flags flags args]]] > 2 } {
error "Too many arguments"
}
if { [llength $non_switches] == 2 } { set port [lindex $non_switches 1] }
set addr [lindex $non_switches 0]
# Detect a CIDR mask and pull it off the addr string
if { [set loc [string first "/" $addr end-4]] != -1 } {
set cidr_bits [string range $addr [expr {$loc + 1}] end]
set addr [string range $addr 0 [expr {$loc - 1}]]
}
# Pull the route-domain off the addr string, but only use it as the
# route domain if it wasn't overridden by -route_domain flag.
if { [string first "%" $addr] != -1 } {
if { $route_domain == 0 } {
# route-domain is still default, so use value from addr string
set route_domain [lindex [split $addr "%"] 1]
}
set addr [lindex [split $addr "%"] 0]
}
if { $do_mask } {
# Define the delta between ipv4 and ipv6.
# length: ipv4 mask is 32 bits, ipv6 is 128 bits.
# group: ipv4 is grouped in octets, ipv6 as 16 bit words.
# format: ipv4 is decimal notation, ipv6 is hex.
# format1 also has the delimiter, format2 does not.
array set v {
0,length 32
0,group 8
0,format1 d.
0,format2 d
1,length 128
1,group 16
1,format1 .4x:
1,format2 .4x
}
set is_ipv6 [string match "*:*:*" $addr]
# Soften result of an illegal -length parameter.
if { ![info exists cidr_bits] || $cidr_bits > $v($is_ipv6,length) } {
set cidr_bits $v($is_ipv6,length)
} elseif { $cidr_bits < 0 } {
set cidr_bits 0
}
# Loop on the full length of the mask: 32 bits for ipv4, 128 for ipv6
for { set octet 0; set i 0 } { $i < $v($is_ipv6,length) } { incr i } {
# Take a break at intervals to save the grouping and add delimiter.
# Interval is 8 bits for ipv4 and 16 bits for ipv6.
if { $i && ![expr {$i % $v($is_ipv6,group)}] } {
# Add the grouping and delimiter to the mask, then reset.
append mask [format %$v($is_ipv6,format1) $octet]
set octet 0
}
# Shift the prior bits left by multiplying by 2.
# Then add the current bit, which is 1 if part of the mask, 0 if not.
# Current bit is part of the mask if $i < number of bits in the mask.
set octet [expr { 2 * $octet + ($i < $cidr_bits) }]
}
# Add the final grouping, then return the finished mask.
set ret_val [format $mask%$v($is_ipv6,format2) $octet]
} else {
# calculate a destination
# the route domain might be a name and we need a number.
if { ![string is integer $route_domain] } {
set route_domains [tmsh::get_config "/ net route-domain $route_domain"]
if { [llength $route_domains] != 1 } {
error "no such route domain: $route_domain"
}
# since we have already determined that the list is 1 long,
# this explicit reference to element 0 is safe
set route_domain [tmsh::get_field_value [lindex $route_domains 0] "id"]
}
set route_domain [expr { $route_domain == 0 ? "" : "%$route_domain" }]
# 0 and * represent wildcard port assignments in the GUI,
# but TMSH requires the string 'any' to specify a wildcard.
if { $port == 0 || $port == "*" } {
set port any
}
# Build the final destination. Use ":" for node names even if ipv6.
set is_ipv6_literal [string match "*:*:*" $addr]
set addr_delimiter [expr { $is_ipv6_literal ? "." : ":" }]
set ret_val ${addr}${route_domain}${addr_delimiter}${port}
}
return $ret_val
}
proc iapp_pool_members { args } {
# Set defaults.
array set fields {
address addr
port port
port-secure port_secure
connection-limit connection_limit
priority-group priority
ratio ratio
}
set route_domain ""
set port_override -1
set aaa_domain 0
set aaa_priority -1
set app_service ""
# Set up flag-related work.
array set flags {
-fields { [array set fields [iapp_pull $ptr args]] }
-route_domain { [set route_domain [iapp_pull $ptr args]] }
-port { [set port_override [iapp_pull $ptr args]] }
-aaa_domain { [set aaa_domain 1] }
-aaa_pool { [set aaa_priority 0] }
-noapp { [set app_service " app-service none"] }
}
iapp_process_flags flags args
# Identify the non-address/non-port fields. These go inside braces in tmsh.
set nonport_fields [lsearch -all -not -inline -regexp \
[array names fields] {address|port|port-secure}]
set members ""
foreach row [join $args] {
# Skip invalid table rows.
if { [llength [join $row]] %2 == 1 } {
continue
}
# Import APL table into an array for processing.
array unset columns
array set columns [join $row]
set addr $columns($fields(address))
# Identify the port number, either from table columns or by -port flag.
if { $port_override != -1 } {
set port $port_override
} elseif { [info exists columns($fields(port))] } {
set port $columns($fields(port))
} elseif { [info exists columns($fields(port-secure))] } {
set port $columns($fields(port-secure))
} else {
set port 80
}
# If specified, strip entered route domain and append the flag value.
if { $route_domain != "" } {
set addr [lindex [split $addr "%"] 0]
set addr "$addr%$route_domain"
}
# If -aaa_domain, use domain controller format, otherwise use pool format
if { $aaa_domain } {
append members " $columns($fields(host)) \{ ip $addr $app_service \}"
} else {
append members " [iapp_destination $addr $port] \{"
# Transfer non-port fields from the table to the tmsh string.
foreach name $nonport_fields {
if { [info exists columns($fields($name))] } {
append members " $name $columns($fields($name))"
}
}
# If -aaa_pool, add priority field with incrementing value.
# This is required by APM.
if { $aaa_priority >= 0 } {
append members " priority-group [incr aaa_priority]$app_service"
}
append members " \}"
}
}
return "[expr { $aaa_domain ? "" : "members " }][expr { $members eq "" \
? "none" : "replace-all-with \{ $members \}" }]"
}
proc iapp_debug { args } {
# Passwords should be obscured in all logs. Fields shown here are handled
# in this proc, but the global variable may be overwritten if alternate
# fields should be obscured.
if { ![info exists ::SENSITIVES] } {
set ::SENSITIVES {
account-password
admin-encrypted-password
PASSWORD
password
passwd
proxy-ca-passphrase
secret
}
}
# look for any of the sensitive words, and replace the word that follows it
set regex "(\\m([join $::SENSITIVES |])\\M)\\s+\[^\\s\]*"
regsub -all $regex [join $args] {\1 -OBSCURED-} args
regsub -all "(<Password.*>).*(</Password>)" $args {\1-OBSCURED-\2} args
set lev [tmsh::get_field_value [lindex [tmsh::get_config sys scriptd \
log-level] 0] log-level]
if { $lev eq {debug} } {
puts $args
}
}
# The apm_config proc provides a tmsh pre-processor for APM
# configuration, which in most cases will drastically reduce
# implementation code. To configure APM with this proc, pass
# it an array of object names and associated meta-tag substitutions.
# Each object must be categorized as a profile, a resource, or
# a policy-item. APM agents and customization-groups are derived
# from these 3 categories as needed.
#
# apm_config's return value is a list of the APM profiles defined
# in the argument and instantiated by the proc. This allows the
# procedure call to be embedded directly into a virtual server
# definition.
#
# These universal meta-tags may be placed anywhere in the array:
# <ITEM> The object name, eg. apm_access
# <PREFIX> The app name, including folder, eg. /Common/my_app.app/my_app
#
# Profile objects require the following meta-tags:
# <PROFILE_TYPE> The tmsh object type, eg. "apm profile access"
# <PROFILE_DEF> The body of the object, eg.:
# "access-policy <PREFIX>
# defaults-from /Common/access
# eps-group <PREFIX>_eps
# errormap-group <PREFIX>_errormap
# general-ui-group <PREFIX>_general_ui"
#
# apm_config will automatically create default customization-groups
# for the "-group" lines specified in access profile definitions.
# In the above example, there is no need to additionally specify a
# customization-group for errormap and general-ui.
#
# <PROFILE_TYPE> is a catch-all for other APM types, eg:
# apm_sso {
# <PROFILE_TYPE> {apm sso kerberos}
# <PROFILE_DEF> "account-name <USER>
# account-password <PASS>
# realm <REALM>" }
#
# In the example above, <PROFILE_TYPE> and <PROFILE_DEF> are
# apm_config meta-tags, while <USER>, <PASS>, and <REALM> must
# be substituted before calling apm_config, eg. if these tags are
# defined in $pre_proc_map, they may be substituted with:
# array set apm_map [string map [subst $pre_proc_map] [array get apm_map]]
#
# Resource objects require the following meta-tags:
# <RESOURCE_TYPE> The apm resource object type, eg. "webtop"
# <RESOURCE_DEF> The body of the object, eg.:
# "customization-group <ITEM>
# minimize-to-tray false
# webtop-type full"
#
# In the above example, a customization-group is specified. Any
# customization-group is assumed to be blank unless further defined by the
# <GROUP_DEF> meta-tag, eg. <GROUP_DEF> {type webtop}
#
# Policy-item objects are defined by the following meta-tags:
# <AGENT_TYPE> default "resource-assign"
# <AGENT_DEF> default "customization-group <ITEM>"
# <ITEM_AGENT> default "agents { <ITEM>_ag { type <AGENT_TYPE> }}"
# <ITEM_CAPTION> default "<ITEM>"
# <ITEM_COLOR> default "1"
# <ITEM_TYPE> default "action"
# <ITEM_RULES> defaults to a set of expressions/next-items where specified
# <RULE_CAPTION_0> default "fallback"
# <RULE_CAPTION_1> default "Successful"
# <RULE_CAPTION_2> default "successful"
#
# apm_config generates the APM agent and customization-group definitions
# as required for each policy-item, but specific objects may be defined
# by using the <AGENT_DEF> and <GROUP_DEF> meta-tags.
# To suppress the formation of an APM agent, specify <ITEM_AGENT> {}.
proc iapp_apm_config { args } {
set app_service ""
array set flags {
-noapp { [set app_service "app-service none\n "] }
}
iapp_process_flags flags args
upvar [lindex $args 0] map_array
# Pull $prefix from the array
set prefix $map_array(prefix)
unset map_array(prefix)
# Stencils for creating apm objects
set access_form \
"<TMSH_CREATE> apm policy access-policy <ITEM> {\n \
$app_service caption general\n \
start-item <ACCESS_START_ITEM>\n \
default-ending <ACCESS_ENDING>\n \
items replace-all-with {\n<ACCESS_ITEMS> }\n}"
set profile_form "<TMSH_CREATE> <PROFILE_TYPE> <ITEM> {\n \
$app_service <PROFILE_DEF>\n}"
set resource_form "<TMSH_CREATE> apm resource <RESOURCE_TYPE> <ITEM> {\n \
$app_service <RESOURCE_DEF>\n}"
set agent_form "<TMSH_CREATE> apm policy agent <AGENT_TYPE> <ITEM>_ag {\n \
$app_service <AGENT_DEF>\n}"
set group_form "<TMSH_CREATE> apm policy customization-group <ITEM> {\
$app_service <GROUP_DEF>}"
set agent_group_form "<TMSH_CREATE> apm policy customization-group <ITEM>_ag {\
$app_service <GROUP_DEF>}"
set policy_item_form "<TMSH_CREATE> apm policy policy-item <ITEM> {
$app_service <ITEM_AGENT>caption <ITEM_CAPTION>
color <ITEM_COLOR>
<ITEM_TYPE>
<ITEM_RULES>\n}"
# 1st round apm string map
set default_map_1 {
<ACCESS_ITEM> {}
<AGENT_DEF> "customization-group <ITEM>_ag"
<ITEM_AGENT> "agents replace-all-with {
<ITEM>_ag { type <AGENT_TYPE> }}\n "
<ITEM_CAPTION> <ITEM>
<ITEM_COLOR> {1}
<ITEM_TYPE> "item-type action"
<ITEM_RULES> "rules
{[expr {[string first <RULE_NEXT_2> $map_array($item)] != -1 ? "{
caption <RULE_CAPTION_2>
expression <RULE_EXPR_2>
next-item ${prefix}_<RULE_NEXT_2>
}":""}][expr {[string first <RULE_NEXT_1> $map_array($item)] != -1 ? "{
caption <RULE_CAPTION_1>
expression <RULE_EXPR_1>
next-item ${prefix}_<RULE_NEXT_1>
}":""}]{
caption <RULE_CAPTION_0>
next-item ${prefix}_<RULE_NEXT_0>
}}"
}
# 2nd round apm string map
set default_map_2 {
<ITEM> [expr { $item eq {default} ? "$prefix" : "${prefix}_$item" }]
<PREFIX> $prefix
<LOCAL_PATH> [string map {/ :} $prefix]
<GROUP_DEF> ""
<AGENT_TYPE> "resource-assign"
<RULE_CAPTION_2> "successful"
<RULE_CAPTION_1> "Successful"
<RULE_CAPTION_0> "fallback"
}
# Build APM access profile and access-policy from the access_form.
# Tags <ACCESS_ITEM> and <ACCESS_ENDING> are picked up from
# $map_array items. <ITEM> and <GROUP_DEF> are picked up from
# $default_map_2.
foreach item [lsort [array names map_array]] {
# Pick up the <ACCESS_ENDING> tag. There should be just 1.
set access_form [string map $map_array($item) $access_form]
# Filter out items that do not belong in the access-policy.
# Anything with an ITEM_xxx tag belongs
if { [string first <ITEM_ $map_array($item)] == -1 } {
continue
}
# Add to the items list for the access-policy, e.g. priority
append access_items " ${prefix}_$item {<ACCESS_ITEM>}\n"
set access_items [string map $map_array($item) $access_items]
set access_items [string map [subst $default_map_1] $access_items]
}
# Build APM resources, policy-items, agents, and customization-groups from
# the policy_item_form and resource_form.
foreach item [lsort [array names map_array]] {
# Each item starts as a profile, a resource, or a policy-item.
# Profiles are free-form, so other apm objects can use the profile form.
# In most cases, a policy-item spawns an agent.
# Any definition specifying a customization-group will spawn that group.
if { [string first "<PROFILE_DEF>" $map_array($item)] != -1 } {
# Collect profile names for attachment to the virtual server
if { [string first "apm profile " $map_array($item)] != -1 } {
lappend profiles [expr { $item eq {default}
? "$prefix" : "${prefix}_$item" }]
# When an access profile is found, built a policy of the same name
if { [string first "apm profile access" $map_array($item)] != -1 } {
set def [string map "<ACCESS_ITEMS> {$access_items}" $access_form]
append cmds "[string map [subst $default_map_2] $def]\n"
}
}
set def $profile_form
} elseif { [string first "<RESOURCE_DEF>" $map_array($item)] != -1 } {
set def $resource_form
} else {
set def $policy_item_form
if { [string first "<ITEM_AGENT> {}" $map_array($item)] == -1 } {
append def $agent_form
}
}
# Apply 1st pass of string maps
set def [string map $map_array($item) $def]
set def [string map [subst $default_map_1] $def]
# If a customization-group is specified, add its definition
if { [string first "customization-group" $def] != -1 } {
if { [string first "apm policy agent" $def] != -1 } {
append def $agent_group_form
} elseif { [string first "apm profile access" $def] == -1 } {
append def $group_form
}
}
# Apply 2nd pass of string maps
set def [string map $map_array($item) $def]
append cmds [string map [subst $default_map_2] $def]
}
# Divide and execute tmsh commands
set tag "<TMSH_CREATE>"
set tag_length [string length $tag]
set last [expr { [string first $tag $cmds] + $tag_length }]
while { [set pos [string first $tag $cmds $last]] != -1 } {
incr pos -1
iapp_conf create [string range $cmds $last $pos]
set last [expr { $pos + $tag_length + 1 }]
}
iapp_conf create [string range $cmds $last end]
return $profiles
}
proc iapp_upgrade_template { upgrade_var upgrade_trans } {
upvar $upgrade_var upgrade_var_arr
upvar $upgrade_trans upgrade_trans_arr
# create the new variables from the old
foreach { var } [array names upgrade_var_arr] {
# substitute old variable name for abbreviation "##"
regsub -all {##} $upgrade_var_arr($var) \$$var map_cmd
# run the mapping command from inside the array
if { [catch { subst $map_cmd } err] } {
if { [string first "no such variable" $err] == -1 } {
puts "ERROR $err"
}
}
}
# move variables over and apply translations
set var_mods ""
set var_adds ""
foreach var [array names vx] {
# if the APL variable name is in the translation array,
# then use the custom translation built for that variable.
if { [info exists upgrade_trans_arr($var)] } {
array set sub_arr [subst $upgrade_trans_arr($var)]
if { [info exists sub_arr($vx($var))] } {
set vx($var) $sub_arr($vx($var))
}
array unset sub_arr
# else, if the APL variable value is in the translation array,
# then use the generic translation of that value.
} elseif { [info exists upgrade_trans_arr($vx($var))] } {
set vx($var) [subst $upgrade_trans_arr($vx($var))]
}
# add to tmsh command string
if { [info exists ::$var] } {
append var_mods "\n $var \{ value \"$vx($var)\" \} "
} else {
append var_adds "\n $var \{ value \"$vx($var)\" \} "
}
}
# move tables over
set tbl_mods ""
set tbl_adds ""
foreach tbl [array names tx] {
# convert table from APL format to TMSH format
if { ![llength $tx($tbl)] } {
set tbl_def "column-names none"
} else {
set rows_def ""
foreach apl_row $tx($tbl) {
array set row_arr [join $apl_row]
append rows_def "\n \{ row \{ "
foreach apl_col [array names row_arr] {
append rows_def "$row_arr($apl_col) "
}
append rows_def "\}\}"
}
set tbl_def \
"\n column-names \{ [array names row_arr] \} rows \{ $rows_def \}"
array unset row_arr
}
# add to tmsh command string
if { [info exists ::$tbl] } {
append tbl_mods "\n $tbl \{ $tbl_def \} "
} else {
append tbl_adds "\n $tbl \{ $tbl_def \} "
}
}
# construct the "tmsh modify" command
set cmd "sys application service $tmsh::app_name "
if { [llength $var_mods] } {
append cmd "\nvariables modify { $var_mods }"
}
if { [llength $var_adds] } {
append cmd "\nvariables add { $var_adds }"
}
if { [llength $tbl_mods] } {
append cmd "\ntables modify { $tbl_mods }"
}
if { [llength $tbl_adds] } {
append cmd "\ntables add { $tbl_adds }"
}
# Execute with debug output. This conversion takes place within the
# existing ASO, so tmsh modify is used instead of tmsh create.
iapp_debug "TEMPLATE UPGRADE"
iapp_conf modify $cmd
return
}
proc iapp_downgrade_template { pivot_var upgrade_var downgrade_table } {
upvar $downgrade_table downgrade_tbl_arr
# The ASO variable "offload_history" is used to recover the legacy
# choice a user made about SSL offload. It should be present in all cases.
# This conditional only handles the case where a user has deliberately
# deleted it by manipulating the ASO directly from tmsh.
if { ![info exists ::offload_history] } {
set ::offload_history "No"
}
# BIG-IP erases table contents when the APL optional hides the table.
# Since the prior data is not available, this downgrade must back-convert
# existing table data. Unlike tables, variables remain intact from the
# legacy ASO.
set tbl_def ""
foreach tbl [array names downgrade_tbl_arr] {
# Check for existence of each table in the current context.
# If not, skip to next.
if { ![info exists [set tbl]] } {
continue
}
# Check for existence of each table in the legacy context.
# If not, add an empty table so "tmsh tables modify" does not fail.
if { ![info exists ::$downgrade_tbl_arr($tbl)] } {
iapp_conf modify sys app ser $tmsh::app_name tables add \{ $downgrade_tbl_arr($tbl) \}
}
append tbl_def "$downgrade_tbl_arr($tbl) \{ "
if { [llength [subst $$tbl]] } {
set rows_def ""
foreach apl_row [subst $$tbl] {
array set row_arr [join $apl_row]
append rows_def "\n \{ row \{ "
foreach apl_col [array names row_arr] {
append rows_def "$row_arr($apl_col) "
}
append rows_def "\}\}"
}
append tbl_def \
"column-names \{ [array names row_arr] \} rows \{ $rows_def \}"
array unset row_arr
} else {
append tbl_def "rows none"
}
append tbl_def " \} "
}
regsub -all "\n" $tbl_def {} tbl_def
set cmd "sys app ser $tmsh::app_name \
variables modify \{ \
$pivot_var \{ value $::offload_history \} \
$upgrade_var \{ value No \} \
\} \
tables modify \{ $tbl_def \}"
iapp_debug "TEMPLATE DOWNGRADE"
iapp_conf modify $cmd
return
}
proc iapp_get_ca_certs { args } {
# Procedure formats and returns ca-bundle 509 certificates from ca-bundle.bak
# (copy of tmos supplied ca-bundle.crt)
# Returns backup files when using -files flag
# Returns specified restore file certificates when using -restore -return flags
# Returns specified restore file table certificates when using -restore -tablename
# Returns selected certificates
#
# Set defaults. Flag actions may overwrite defaults later.
set rest_files 0
set do_restore 0
set restore_return 0
set restore_table_name 0
set do_certs 0
set user_get [iapp_get_user]
set username [string range $user_get [expr {[string last user $user_get] +5 }] end-3 ]
# Set up flag-based actions.
array set flags {
-files { [set rest_files 1] }
-return { [set do_restore 1] [set restore_return 1] }
-tablename { [set do_restore 1] [set restore_table_name 1] }
-certs { [set do_certs 1] }
}
iapp_process_flags flags args
set fn_ca_bundle "[lindex $args 0]"
set cert_choices "[lindex $args 1]"
set duplicate " "
if { $rest_files eq 0 || $do_restore } {
set fh_ca_bundle [open $fn_ca_bundle r]
set ca_bundle_data [read $fh_ca_bundle]
close $fh_ca_bundle
set ca_bundle_split [split [string map "{-----END CERTIFICATE-----} \001" $ca_bundle_data] "\001"]