-
Notifications
You must be signed in to change notification settings - Fork 7
/
dpxmlsh_library.sh
executable file
·1833 lines (1640 loc) · 64.8 KB
/
dpxmlsh_library.sh
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
#!/bin/bash
#
# Author: Harley Stenzel <hstenzel@us.ibm.com>
# Description: A shell library for working with the DataPower XML Management
# Interface.
#
# These functions are designed to be sourced into either a running shell or
# into another shell script.
#
# Functions beginning with "_" are internal, and not for general use
# dpxmlsh_init does all the normal startup, common arg parsing, etc
# dpxmlsh_[amp|soma]_* functions take appropriate input (usually files
# or args) and return an xml result on stdout. The functions are
# named after the underlying xml management methods.
# dpxmlsh_* generally use their amp or soma counterparts directly or
# indirectly to deliver some easily usable function. For instance,
# get_status_* methods all call get status and return the result
# in a different but useful form. ls and lls are named after the
# common command and alias for listing files. The functions get_file
# and set_file do just what one would expect. Tab completion helps.
#
DPXMLSH_VERSION=0.64
# Changes
# 0.64 2016-03-04:
# * Disable the password selftest, runs afowl the repeated pw check
# * Disable the testhardware selftest, not applicable on all platforms
# 0.63 2016-01-04:
# * Add missing domain on set-file and get-file
# * Add createdir and removedir
# * selftest works on non-default domains
# 0.62 2014-12-08:
# * Fix dpxmlsh_help so init args are displayed.
# 0.61 2014-09-24:
# * Add action_testhardware
# 0.61 2014-09-23:
# * Better checks for the xmlstarlet command
# 0.59 2014-05-01:
# * Prompt in interactive mode now uses the whole ip address
# if the host is specified numerically.
# * Add netrc sanity checking and error message
# 0.58 2014-03-18:
# * Better handle missing command completion dependencies
# * Remove flags for command completion
# 0.57 2014-03-15:
# * Minor tweak to user setting when netrc auth is used
# 0.56 2014-02-15:
# * Bash completion for config objects
# 0.55 2014-01-10:
# * Bash completion for files and status providers is function complete
# * removed the colon-removing "normalization"
# 0.54 2013-12-11:
# * Begin using bash command line completion for files on DataPower
# 0.53 2013-12-11:
# * Use bash command line completion for status instead of warnings
# 0.52 2013-12-02:
# * Add to git
# * Few help cleanups
# 0.51 2013-10-30:
# * Begin work on a better exit code, see dpxmlsh_soma_get_status.
# Consider others in future versions. It's a pattern that could
# work for all but the get_file, which may return too much data.
# * rename dpxmlsh_soma_deletefile to dpxmlsh_soma_action_deletefile
# * add dpxmlsh_[soma_]action_changepassword
# * add dpxmlsh_[soma_]action_saveconfig
# * rename dpxmlsh_*action_exec_config -> dpxmlsh_action_execconfig
# * rename dpxmlsh_*deletefile -> dpxmlsh*_action_deletefile
# * Better boilerplate for standard handling of new soma actions
# 0.50 2013-10-28:
# * Add subshell mode. Can now simply exec the script library
# to get a subshell. Prompt includes [dphost:dpuser:dpdomain]
# prefixed to the existing prompt.
# * Add --interactive / --script. This suppresses downloading
# the list of status commands that are used for command completion.
# This is good for building scripts that call _init.
# 0.49 2013-08-23:
# * Fix bug with second init leading to xmlstarlet/xml not found
# * Add dpxmlsh_version to see metainfo about this script
# * Add dpxmlsh_unload to remove all functions and internal variables
# used in this shell library. Does not clean up things created
# by the user using the library.
# * Better cleanup on source
# 0.48 2013-08-01:
# * Pretty print xml results that are not set file
# * Detect if "xmlstarlet" is called "xml"
# 0.47 2013-07-18:
# * Fix bug where error was reported incorrectly on an empty status
# provider result
# * Pretty up the selftest output
# * Rename dpxmlsh_get_status_evaldump to dpxmlsh_get_status_evalprint
# 0.46 2013-07-02:
# * Add --verbose for putting orig xml on stderr
# * add soma_get_config, get_config_list, and get_config_import
# 0.45 2013-06-19:
# * Add label support to get_status_vtable and get_status_list
# * Add get_status_eval*, the full-featured cousin to _import
# * Make init fast and cheap for subsequent calls
# 0.44 2013-06-14:
# * clean up the selftest so examples are more obvious
# 0.43 2013-06-13:
# * Add get_status_import, which sucks an entire status provider into
# arrays in memory. Check self_test for examples, and the comments
# for the caveats.
# 0.42 2013-06-13:
# * Add get_status_vtable, which makes it very easy to work with
# multi-row tables. See the comments for examples, and also
# the example in selftest
# 0.41 2013-06-12:
# * Fix bug in table status provider where unpopulated columns are
# mishandled. Now put a "-" in any empty field as a placeholder.
# * Get status providers from init so it is only done once.
# 0.4 2013-06-11:
# * Improve the status provider table layout -- use columns for pretty
# formatting and turn spaces into underlines so the result is cut and
# awk friendly.
# * Improve status provider list output -- include status provider name
# as part of the name
# * Add "edit".
# * Rename file so that it includes the word "library"
# 0.3 2013-06-11:
# * Add general purpose status provider handling in 3 formats:
# XML, Table, and List
# * Remove dependency on xmllint -- xmlstarlet can fulfil its function
# 0.2 2013-04-14: start using xmlstarlet -- an easy command line xslt tool
# 0.1 2013-03-27: framework takes shape
# * working functions and unit test harness
# 0.0 2013-03-26: a few functions thrown in a file
#
# ToDo
# * Test domain support
# * figure out better way to get return codes
# * Fill out action and config set
# * Move to current xmlmgmt, not 2004
if [ "$_DPXMLSH_BASHRCFILE" != "" -a "$DPXMLSH_SCRIPT" != "true" ]
then
if [ -e "/etc/bash.bashrc" ]; then
. "/etc/bash.bashrc"
fi
if [ -e "$HOME/.bashrc" ]; then
. "$HOME/.bashrc"
fi
PS1="[\$_DPXMLSH_DPHOST_PROMPT:\$_DPXMLSH_DPUSER:\$_DPXMLSH_DPDOMAIN]$PS1"
fi
export _DPXMLSH_BASHRCFILE=""
if [ "$0" != "bash" -a "$DPXMLSH_SCRIPT" != "true" ]
then
case "$0" in
.*)
_DPXMLSH_BASHRCFILE="${PWD}/$0"
;;
/*)
_DPXMLSH_BASHRCFILE="$0"
;;
*)
_DPXMLSH_BASHRCFILE="$(which $0)"
;;
esac
echo >&2
echo "ATTENTION: Entering dpxmlsh subshell." >&2
echo "Use \"dpxmlsh_init\" to get started or \"dpxmlsh_help\" for more info. Tab-completion makes it easy!" >&2
echo "Enjoy!" >&2
echo >&2
bash --rcfile "$_DPXMLSH_BASHRCFILE" -i
echo "ATTENTION: Leaving dpxmlsh subshell" >&2
fi
function dpxmlsh_unload ()
{
eval $(set | grep -e ^_DPXMLSH_ -e '^_*dpxmlsh_.* \(\)$' | sed -e 's/=.*//g' -e 's/ .*$//g' -e 's/^/unset /g')
}
# the dpxmlsh globals and functions, ensure they're unset at load
dpxmlsh_unload
function dpxmlsh_help ()
{
cat <<-EOF
dpxmlsh -- working with a DataPower appliance via XML management from the
bash command line or shell script.
There are 3 steps to profit:
1) Run the script. This will place you in a subshell. Alternatively, you can
Source or import the script library. This is done once for the lifetime of the
interactive shell or shell script. Note the leading dot+space! That is *not* a typo!
i.e.: . /path/to/dpxmlsh.sh
2) Initialize the script library by calling the dpxmlsh_init function
i.e.: dpxmlsh_init -u admin -p supersercredpassword -h mydpappliance
3) Call the functions in the library for fun and profit.
i.e.: dpxmlsh_ls
dpxmlsh_edit local:/myfile.xsl
dpxmlsh_get_file temporary:/internal-state.txt
dpxmlsh_get_status_list Version
dpxmlsh_get_status_table TCPTable
dpxmlsh_set_firmware_and_accept_license myfirmware.scrypt3
Note that if you want to ensure that the script library is working properly, use the
dpxmlsh_selftest function that exercises a significant portion of the library in a
non-destructive fashion. Additionally, _selftest prints examples of how many of the
functions should be called.
dpxmlsh_init takes the following args:
$(set | sed -n '/^dpxmlsh_switch/,/^}/p' | grep "^ *-.*)" | sed 's/^ */ /g')
Note that the user defaults to "admin", the domain to "default", the port to 5550,
and the password inherited from ~/.netrc (see man netrc), so the only thing that
must be specified is the host.
The high-level functions, which are usable but user/script alone, without
need for additional xml processing:
$(set | grep "^dpxmlsh_[^ ]* ()" \
| grep -v -e '_init' -e '_amp_' -e '_soma_' \
| sort | sed -e 's/^/ /g' -e 's/ ()//g' )
The low-level, xml-result-emitting functions are:
$(set | grep "^dpxmlsh_[^ ]* ()" \
| grep -e '_amp_' -e '_soma_' \
| sort | sed -e 's/^/ /g' -e 's/ ()//g' )
EOF
}
# Let the exit code of a series of pipes reflect the first failing command.
# Add the following command *immediately* after the series of pipes.
# _dpxmlsh_pipestatus $FUNCNAME ${PIPESTATUS[@]}
_dpxmlsh_pipestatus ()
{
local rc
local caller="$1"
shift
for rc in $*
do
if [ "$rc" != 0 ]
then
echo "ERROR: $caller pipestatus $*, returning $rc" >&2
return $rc
fi
done
return 0
}
# globals used by dpxmlsh
function _dpxmlsh_globals_init ()
{
export _DPXMLSH_CURLSOMACMD=""
export _DPXMLSH_CURLAMPCMD=""
export _DPXMLSH_STATUSPROVIDERS=""
export _DPXMLSH_DEBUGDIR="$HOME/dpxmlsh-debug"
export _DPXMLSH_DPDOMAIN="default"
export _DPXMLSH_DEBUG="false"
export _DPXMLSH_VERBOSE=false
export _DPXMLSH_DPHOST=""
export _DPXMLSH_DPHOST_PROMPT=""
export _DPXMLSH_DPUSER=admin
export _DPXMLSH_DPPASS=""
export _DPXMLSH_DPXMLPORT=5550
}
_dpxmlsh_globals_init
# Called at the onset, and whenever the user, hostname, password, auth
# method, domain, etc changes.
function dpxmlsh_init ()
{
if [ -z "$_DPXMLSH_CURLSOMACMD" ]
then
# check prereqs
test "$BASH" \
|| { echo "ERROR: $FUNCNAME only the bash shell is supported" >&2 ; return 1; }
curl -V | grep -q -i ssl \
|| { echo "ERROR: $FUNCNAME curl missing or does not support ssl" >&2 ; return 1; }
export _DPXMLSH_XMLSTARLET=""
local WOOHOO_XMLSTARLET=$( echo "<top><node>woohoo</node></top>" | xmlstarlet sel --template --match '//node' --value-of '.' 2>/dev/null )
local WOOHOO_XML=$( echo "<top><node>woohoo</node></top>" | xml sel --template --match '//node' --value-of '.' 2>/dev/null )
if [ "$WOOHOO_XMLSTARLET" = "woohoo" ]
then
_DPXMLSH_XMLSTARLET="xmlstarlet"
elif [ "$WOOHOO_XML" = "woohoo" ]
then
_DPXMLSH_XMLSTARLET="xml"
else
echo "ERROR: $FUNCNAME xmlstarlet/xml missing" >&2
return 1
fi
local opensshtest="$(echo 2bornot2b | openssl enc -base64 -a -A | openssl enc -base64 -A -d)"
test "2bornot2b" = "$opensshtest" \
|| { echo "ERROR: $FUNCNAME openssl missing or not functional" >&2 ; return 1; }
{ echo "a b c"; echo "def g hij" ; } | column -t > /dev/null \
|| { echo "ERROR: $FUNCNAME 'column' command missing or not functional" >&2 ; return 1; }
fi
_dpxmlsh_globals_init
dpxmlsh_switch "$@"
}
# Like init, except that it leaves current settings in place
function dpxmlsh_switch ()
{
while [[ $# -gt 0 ]]
do
case $1 in
--help)
dpxmlsh_help
return 0
;;
-h|--host|--dp-host)
shift
_DPXMLSH_DPHOST="$1"
# it's an ip address if it's all digits and dots or if it contains a ":" (ipv6).
if [ "$(echo $_DPXMLSH_DPHOST | tr -d '0-9.')" = "" ] || echo "$_DPXMLSH_DPHOST" | grep -q ":"
then
_DPXMLSH_DPHOST_PROMPT="$_DPXMLSH_DPHOST"
else
_DPXMLSH_DPHOST_PROMPT="$(echo $1 | cut -d. -f1)"
fi
;;
-u|--user|--dp-user)
shift
_DPXMLSH_DPUSER="$1"
;;
-p|--pass|--passwd|--password|--dp-pass|--dp-passwd|--dp-password)
shift
_DPXMLSH_DPPASS="$1"
;;
-d|--domain|--dp-domain)
shift
_DPXMLSH_DPDOMAIN="$1"
;;
-P|--port|--dp-port|--dp-xml-mgmt-port)
shift
_DPXMLSH_DPXMLPORT="$1"
;;
--debug)
set -x
_DPXMLSH_DEBUG="true"
mkdir -p "$_DPXMLSH_DEBUGDIR" || { echo ERROR: could not make "$1" >&2 ; return 1 ; }
;;
--no-debug)
set +x
_DPXMLSH_DEBUG="false"
;;
--verbose)
_DPXMLSH_VERBOSE=true
;;
--no-verbose)
_DPXMLSH_VERBOSE=false
;;
--debug-dir)
shift
_DPXMLSH_DEBUGDIR="$1"
mkdir -p "$_DPXMLSH_DEBUGDIR" || { echo ERROR: could not make "$1" >&2 ; return 1 ; }
;;
*)
echo Warning: invalid arg $1 >&2
;;
esac
shift
done
if [ -z "$_DPXMLSH_DPHOST" ] ; then
echo "ERROR: Must specify -h <dphostname>" >&2
_DPXMLSH_DPDOMAIN=""
_DPXMLSH_DPUSER=""
return 1
fi
local _DPXMLSH_CURLAUTH=""
if [ -z "$_DPXMLSH_DPPASS" ]
then
if [ -r $HOME/.netrc ] && \
cat "$HOME/.netrc" \
| grep "machine $_DPXMLSH_DPHOST " \
| grep "login .*" \
| grep "password .*" >/dev/null 2>&1
then
_DPXMLSH_CURLAUTH="--netrc"
_DPXMLSH_DPUSER="<netrc>"
else
dpxmlsh_help
echo "ERROR: Must specify -p <dppassword> or create ~/.netrc with line \"machine $_DPXMLSH_DPHOST login <myDPUser> password <myDPPassword>\"" >&2
_DPXMLSH_DPDOMAIN=""
_DPXMLSH_DPUSER=""
return 1
fi
else
_DPXMLSH_CURLAUTH="--user $_DPXMLSH_DPUSER:$_DPXMLSH_DPPASS"
fi
local _DPXMLSH_CURLSOMAURL="https://$_DPXMLSH_DPHOST:$_DPXMLSH_DPXMLPORT/service/mgmt/current"
local _DPXMLSH_CURLAMPURL="https://$_DPXMLSH_DPHOST:$_DPXMLSH_DPXMLPORT/service/mgmt/amp/1.0"
# this is the curl command we will use everywhere.
_DPXMLSH_CURLSOMACMD="curl $_DPXMLSH_CURLAUTH --silent --insecure --data @- $_DPXMLSH_CURLSOMAURL"
_DPXMLSH_CURLAMPCMD="curl $_DPXMLSH_CURLAUTH --silent --insecure --data @- $_DPXMLSH_CURLAMPURL"
}
# return true if we have a dp:result OK
function _dpxmlsh_soma_isresultok ()
{
$_DPXMLSH_XMLSTARLET sel --noblanks --text -N dp="http://www.datapower.com/schemas/management" \
--template --match '//dp:result' --value-of '.' --nl \
| tr -d " " \
| grep -q "^OK$"
_dpxmlsh_pipestatus $FUNCNAME ${PIPESTATUS[@]}
}
# the contents the document are passed as $1
function _dpxmlsh_soma_isok ()
{
local DOCUMENT="$1"
# return 1 if there is no dp:response in the document
echo "$DOCUMENT" | grep -q "<dp:response " || return 1
# return 1 if we have <env:Fault>
echo "$DOCUMENT" | grep -q "<env:Fault>" && return 1
# return 1 if we have a dp:result and it is not <dp:result>OK</dp:result>
if echo "$DOCUMENT" | grep -q "<dp:result>"
then
echo "$DOCUMENT" \
| $_DPXMLSH_XMLSTARLET sel --noblanks --text -N dp="http://www.datapower.com/schemas/management" \
--template --match '//dp:result' --value-of '.' --nl \
| tr -d " " \
| grep -q "^OK$" \
|| return 1
fi
# otherwise, return 0 because there wasn't a fault and f there was a result, it wasn't OK
return 0
}
# return true if we have a amp:tatus ok
function _dpxmlsh_amp_isresultok ()
{
$_DPXMLSH_XMLSTARLET sel --noblanks --text -N amp="http://www.datapower.com/schemas/appliance/management/1.0" \
--template --match '//amp:Status' --value-of '.' --nl \
| grep -q "^ok$"
_dpxmlsh_pipestatus $FUNCNAME ${PIPESTATUS[@]}
}
# Used as a debug/monitor point to save off copies of the contents
# of the pipe at various points in the cycle. Names files with
# a date stamp and a description of the monitor point.
# Takes args:
# 1) The calling function name,
# 2) the directionality (request, response, internal),
# 3) The suffix (usually xml, sometimes .base64.txt)
#
function _dpxmlsh_tee ()
{
local callingfunction="$1"
local direction="$2"
local suffix="$3"
# save a copy of strategic transforms in debug mode
if [ "$_DPXMLSH_DEBUG" = "true" ]
then
tee "$_DPXMLSH_DEBUGDIR"/$( date +%Y%m%d_%H%M%S_%N ).$callingfunction-$direction.$suffix
elif [ "$_DPXMLSH_VERBOSE" = "true" ]
then
case $direction in
request|response)
printf "\nvvvvvvvv verbose $direction vvvvvvvv\n" >&2
tee /dev/fd/2
printf "\n^^^^^^^^ verbose $direction ^^^^^^^^\n" >&2
;;
*)
cat
;;
esac
else
cat
fi
}
# Generic function to convert from the user version of a command to the xml version
# Generally called thusly:
# function dpxmlsh_action_<actionname> () { _dpxmlsh_shell2xml 2 2 "$FUNCNAME" "$@"; }
# where args are minimum-args, maximum-args, and the rest is boilerplate. FUNCNAME
# is a BASH builtin that evaluates to the name of the current function, and
# transparently pass all the rest of the args along.
function _dpxmlsh_shell2xml ()
{
local MINARGS="$1"
local MAXARGS="$2"
local SOMACMD="$(echo $3 | sed 's/^dpxmlsh_/dpxmlsh_soma_/g')"
shift
shift
shift
# check args
[ $# -lt "$MINARGS" ] && { echo ERROR: $SOMACMD invalid number of args >&2 ; return 1; }
[ $# -gt "$MAXARGS" ] && { echo ERROR: $SOMACMD invalid number of args >&2 ; return 1; }
local RESULT=$($SOMACMD "$@")
if _dpxmlsh_soma_isok "$RESULT"
then
return 0
fi
echo "$RESULT" | $_DPXMLSH_XMLSTARLET fo >&2
echo "ERROR" >&2
return 1
}
# transfer a single file from local $1 to datapower and call it $2
# $1 may be - for stdin or a real file
# $2 is represented in DataPower notation, i.e. store:///myfile.xml
function dpxmlsh_soma_set_file ()
{
local FROM="$1" # either a file or - for input on stdin
local DPFILENAME="$2" # what the file is called in DP, such as temporary:myfile.xml
{
cat <<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<dp:request xmlns:dp="http://www.datapower.com/schemas/management" domain="$_DPXMLSH_DPDOMAIN">
<dp:set-file name="$DPFILENAME">
EOF
# cat is happy to take "-" as well, and cleverly we are happy to take
# stdin to this function and let this instance of cat consume it
cat $FROM \
| openssl enc -base64 -a -A \
| _dpxmlsh_tee $FUNCNAME internal base64.txt
cat <<-EOF
</dp:set-file>
</dp:request>
</soapenv:Body>
</soapenv:Envelope>
EOF
} | _dpxmlsh_tee $FUNCNAME request xml \
| $_DPXMLSH_CURLSOMACMD \
| _dpxmlsh_tee $FUNCNAME response xml \
| $_DPXMLSH_XMLSTARLET fo
_dpxmlsh_pipestatus $FUNCNAME ${PIPESTATUS[@]}
}
# transfer a single file from local $1 to datapower and call it $2
# $1 may be - for stdin or a real file
# $2 is represented in DataPower notation, i.e. store:///myfile.xml
function dpxmlsh_set_file ()
{
local FROM="$1" # either a file or - for input on stdin
local DPFILENAME="$2" # what the file will be called in DP, such as temporary:myfile.xml
# check args
[ $# = 2 ] || { echo dpxmlsh_soma_set_file ERROR, invalid number of args to dpxmlsh_soma_set_file >&2 ; return 1; }
[ "$FROM" = "-" -o -r "$FROM" ] || { echo dpxmlsh_soma_set_file ERROR: $FROM not readable or stdin >&2 ; return 1; }
local RESULT=$(dpxmlsh_soma_set_file "$FROM" "$DPFILENAME")
if _dpxmlsh_soma_isok "$RESULT"
then
return 0
fi
echo "$RESULT" | $_DPXMLSH_XMLSTARLET fo >&2
echo "ERROR" >&2
return 1
}
# Get the file specified as an arg and give the soma xml result back on stdout
# Can't pretty-print it because get we'll run out of memory on big files
function dpxmlsh_soma_get_file ()
{
local DPFILENAME="$1" # what the file is called in DP, such as temporary:myfile.xml
{ cat <<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<dp:request xmlns:dp="http://www.datapower.com/schemas/management" domain="$_DPXMLSH_DPDOMAIN">
<dp:get-file name="$DPFILENAME"/>
</dp:request>
</soapenv:Body>
</soapenv:Envelope>
EOF
} | _dpxmlsh_tee $FUNCNAME request xml \
| $_DPXMLSH_CURLSOMACMD \
| _dpxmlsh_tee $FUNCNAME response xml
_dpxmlsh_pipestatus $FUNCNAME ${PIPESTATUS[@]}
}
# Get the file specified as an arg and give it back on stdout
function _dpxmlsh_get_file ()
{
local DPFILENAME="$1" # what the file is called in DP, such as temporary:myfile.xml
dpxmlsh_soma_get_file "$DPFILENAME" \
| tr -d '\n' \
| sed -e 's;^.*<dp:file name=[^>]*>;;g' -e 's;</dp:file>.*;;g' \
| _dpxmlsh_tee $FUNCNAME internal base64.txt \
| openssl enc -base64 -A -d
_dpxmlsh_pipestatus $FUNCNAME ${PIPESTATUS[@]}
# this is "better" than tr and sed, but it doesn't work because it runs out of memory
# | $_DPXMLSH_XMLSTARLET sel --noblanks --text -N dp="http://www.datapower.com/schemas/management" \
# --template --match '//dp:file' --value-of '.' --nl \
# gives: -:2: error: xmlSAX2Characters: huge text node: out of memory
}
# transfer a single file on DataPower called $1 to the local machine where
# it is called $2
# $1 is represented in DataPower notation, i.e. store:///myfile.xml
# $2 may be - for stdin or a real file
function dpxmlsh_get_file ()
{
local DPFILENAME="$1" # what the file is called in DP, such as temporary:myfile.xml
local TO="$2" # either a file or - for stdout
if [ "$TO" = "-" -o -z "$TO" ]
then
_dpxmlsh_get_file "$DPFILENAME"
else
_dpxmlsh_get_file "$DPFILENAME" > "$TO"
fi
}
function dpxmlsh_action_changepassword () { _dpxmlsh_shell2xml 2 2 "$FUNCNAME" "$@"; }
function dpxmlsh_soma_action_changepassword ()
{
local OLDPASSWORD="$1"
local NEWPASSWORD="$2"
{ cat <<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<dp:request xmlns:dp="http://www.datapower.com/schemas/management" domain="$_DPXMLSH_DPDOMAIN">
<dp:do-action>
<ChangePassword>
<OldPassword>$OLDPASSWORD</OldPassword>
<Password>$NEWPASSWORD</Password>
</ChangePassword>
</dp:do-action>
</dp:request>
</env:Body>
</env:Envelope>
EOF
} | _dpxmlsh_tee $FUNCNAME request xml \
| $_DPXMLSH_CURLSOMACMD \
| _dpxmlsh_tee $FUNCNAME response xml \
| $_DPXMLSH_XMLSTARLET fo
_dpxmlsh_pipestatus $FUNCNAME ${PIPESTATUS[@]}
}
function dpxmlsh_action_saveconfig () { _dpxmlsh_shell2xml 0 0 "$FUNCNAME" "$@"; }
function dpxmlsh_soma_action_saveconfig ()
{
{ cat <<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<dp:request xmlns:dp="http://www.datapower.com/schemas/management" domain="$_DPXMLSH_DPDOMAIN">
<dp:do-action>
<SaveConfig/>
</dp:do-action>
</dp:request>
</env:Body>
</env:Envelope>
EOF
} | _dpxmlsh_tee $FUNCNAME request xml \
| $_DPXMLSH_CURLSOMACMD \
| _dpxmlsh_tee $FUNCNAME response xml \
| $_DPXMLSH_XMLSTARLET fo
_dpxmlsh_pipestatus $FUNCNAME ${PIPESTATUS[@]}
}
function dpxmlsh_deletefile () { echo "WARNING: $FUNCNAME deprecated, use dpxmlsh_action_deletefile instead" >&2 ; dpxmlsh_action_deletefile "$@"; }
function dpxmlsh_soma_deletefile () { echo "WARNING: $FUNCNAME deprecated, use dpxmlsh_soma_action_deletefile instead" >&2 ; dpxmlsh_soma_action_deletefile "$@"; }
# should have action in the name
function dpxmlsh_action_deletefile () { _dpxmlsh_shell2xml 1 1 "$FUNCNAME" "$@"; }
function dpxmlsh_soma_action_deletefile ()
{
local DPFILENAME="$1" # what the file is called in DP, such as temporary:myfile.xml
{ cat <<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<dp:request xmlns:dp="http://www.datapower.com/schemas/management" domain="$_DPXMLSH_DPDOMAIN">
<dp:do-action>
<DeleteFile>
<File>$DPFILENAME</File>
</DeleteFile>
</dp:do-action>
</dp:request>
</env:Body>
</env:Envelope>
EOF
} | _dpxmlsh_tee $FUNCNAME request xml \
| $_DPXMLSH_CURLSOMACMD \
| _dpxmlsh_tee $FUNCNAME response xml \
| $_DPXMLSH_XMLSTARLET fo
_dpxmlsh_pipestatus $FUNCNAME ${PIPESTATUS[@]}
}
function dpxmlsh_action_removedir () { _dpxmlsh_shell2xml 1 1 "$FUNCNAME" "$@"; }
function dpxmlsh_soma_action_removedir ()
{
local DPDIRNAME="$1"
{ cat <<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<dp:request xmlns:dp="http://www.datapower.com/schemas/management" domain="$_DPXMLSH_DPDOMAIN">
<dp:do-action>
<RemoveDir>
<Dir>$DPDIRNAME</Dir>
</RemoveDir>
</dp:do-action>
</dp:request>
</env:Body>
</env:Envelope>
EOF
} | _dpxmlsh_tee $FUNCNAME request xml \
| $_DPXMLSH_CURLSOMACMD \
| _dpxmlsh_tee $FUNCNAME response xml \
| $_DPXMLSH_XMLSTARLET fo
_dpxmlsh_pipestatus $FUNCNAME ${PIPESTATUS[@]}
}
function dpxmlsh_action_createdir () { _dpxmlsh_shell2xml 1 1 "$FUNCNAME" "$@"; }
function dpxmlsh_soma_action_createdir ()
{
local DPDIRNAME="$1"
{ cat <<-EOF
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<dp:request xmlns:dp="http://www.datapower.com/schemas/management" domain="$_DPXMLSH_DPDOMAIN">
<dp:do-action>
<CreateDir>
<Dir>$DPDIRNAME</Dir>
</CreateDir>
</dp:do-action>
</dp:request>
</env:Body>
</env:Envelope>
EOF
} | _dpxmlsh_tee $FUNCNAME request xml \
| $_DPXMLSH_CURLSOMACMD \
| _dpxmlsh_tee $FUNCNAME response xml \
| $_DPXMLSH_XMLSTARLET fo
_dpxmlsh_pipestatus $FUNCNAME ${PIPESTATUS[@]}
}
# get a list of all files on the appliance
function dpxmlsh_soma_get_filestore ()
{
{ cat <<- EOF
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<dp:request xmlns:dp="http://www.datapower.com/schemas/management" domain="$_DPXMLSH_DPDOMAIN">
<dp:get-filestore/>
</dp:request>
</env:Body>
</env:Envelope>
EOF
} | _dpxmlsh_tee $FUNCNAME request xml \
| $_DPXMLSH_CURLSOMACMD \
| _dpxmlsh_tee $FUNCNAME response xml \
| $_DPXMLSH_XMLSTARLET fo
_dpxmlsh_pipestatus $FUNCNAME ${PIPESTATUS[@]}
}
function dpxmlsh_ls ()
{
dpxmlsh_soma_get_filestore \
| $_DPXMLSH_XMLSTARLET sel --template --match '//file' --value-of '../@name' --output '/' --value-of '@name' --nl \
| grep -v '^$'
_dpxmlsh_pipestatus $FUNCNAME ${PIPESTATUS[@]}
}
function dpxmlsh_lls ()
{
local file
local size
local date
local time
dpxmlsh_soma_get_filestore \
| $_DPXMLSH_XMLSTARLET sel --template --match '//file' \
--value-of 'size' \
-o ' ' --value-of 'modified' \
-o ' ' --value-of '../@name' --output '/' --value-of '@name' \
--nl \
| grep -v '^$' \
| while read size date time file
do
printf "%9d %10s %8s %s\n" "$size" "$date" "$time" "$file"
done
_dpxmlsh_pipestatus $FUNCNAME ${PIPESTATUS[@]}
}
function dpxmlsh_soma_get_config ()
{
local DPOBJECTCLASS=""
local DPOBJECTNAME=""
local ARG1="$1"
local ARG2="$2"
# figure out a normalized object name to dp name mapping
# this is only needed when both class name and object
# name are specified.
if [ ! -z "$ARG1" -a ! -z "$ARG2" ] ; then
local NEWARG2
NEWARG2=$(dpxmlsh_get_status_table ObjectStatus \
| grep "^LinkAggregation" | awk '{print $4 " " $4}' \
| sed 's/[- ]// ' | grep "^$ARG2 " | cut -d" " -f2)
# If we don't get an answer (0 words) or we get more than
# one answer (2 or more words) then stick with what was
# provided exactly. Otherwise, the status provider
# successfully mapped an object name!
if [ $(echo "$NEWARG2" | wc -w) = 1 ] ; then
ARG2="$NEWARG2"
fi
fi
if [ ! -z "$1" ]; then
DPOBJECTCLASS="class=\"$ARG1\""
fi
if [ ! -z "$2" ]; then
DPOBJECTNAME="name=\"$ARG2\""
fi
{ cat <<-EOF
<?xml version="1.0"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<dp:request xmlns:dp="http://www.datapower.com/schemas/management" domain="$_DPXMLSH_DPDOMAIN">
<dp:get-config $DPOBJECTCLASS $DPOBJECTNAME/>
</dp:request>
</soapenv:Body>
</soapenv:Envelope>
EOF
} \
| _dpxmlsh_tee $FUNCNAME request xml \
| $_DPXMLSH_CURLSOMACMD \
| _dpxmlsh_tee $FUNCNAME response xml \
| $_DPXMLSH_XMLSTARLET fo
_dpxmlsh_pipestatus $FUNCNAME ${PIPESTATUS[@]}
}
# Config in the form of a list of name-value pairs, separated by '=",
# with the value quoted. As such, the result should be in form where it
# could be sourced into a script and used directly
# Note that each variable's name is transformed. All spaces, dashes, dots
# and underlines in the orig name are removed for compliance with shell variable
# names or readability. Underscores are used as separators. Unnamed multi-
# instance variables are assigned an ordinal 0..n. Variables are named in
# the form <PrefixName>_config_<<ObjectType_[objectname|instancenumber]>...>="value"
#
# Takes 3 args:
# 1: ObjectType (or "")
# 2: ObjectName (or "")
# 3: Prefix name (or "")
function dpxmlsh_get_config_list ()
{
cat >/tmp/dpxmlsh_getconfig.$$.xsl <<-"EOF"
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exslt="http://exslt.org/common"
xmlns:math="http://exslt.org/math"
xmlns:date="http://exslt.org/dates-and-times"
xmlns:func="http://exslt.org/functions"
xmlns:set="http://exslt.org/sets"
xmlns:str="http://exslt.org/strings"
xmlns:dyn="http://exslt.org/dynamic"
xmlns:saxon="http://icl.com/saxon"
xmlns:xalanredirect="org.apache.xalan.xslt.extensions.Redirect"
xmlns:xt="http://www.jclark.com/xt"
xmlns:libxslt="http://xmlsoft.org/XSLT/namespace"
xmlns:test="http://xmlsoft.org/XSLT/"
xmlns:dp="http://www.datapower.com/schemas/management"
extension-element-prefixes="exslt math date func set str dyn saxon xalanredirect xt libxslt test"
exclude-result-prefixes="math str">
<xsl:output omit-xml-declaration="yes" indent="no" method="text"/>
<xsl:param name="inputFile">-</xsl:param>
<xsl:template match="/">
<xsl:call-template name="t1"/>
</xsl:template>
<xsl:template name="t1">
<xsl:for-each select="//*">
<xsl:for-each select="ancestor-or-self::*">
<xsl:if test="string-length(../@name)=0">
<xsl:variable name="sample2" select="."/>
<xsl:if test="( count($sample2/../preceding-sibling::*[name()=name($sample2/..)]) + count($sample2/../following-sibling::*[name()=name($sample2/..)]) ) > 0" >
<xsl:value-of select="count($sample2/../preceding-sibling::*[name()=name($sample2/..)])"/>
<xsl:value-of select="'_'"/>
</xsl:if>
</xsl:if>
<xsl:value-of select="translate(name(),'_.- ','')"/>
<xsl:if test="string-length(@name)!=0">
<xsl:value-of select="'_'"/>
<xsl:value-of select="translate(@name,'_.- ','')"/>
</xsl:if>
<xsl:if test="not(position()=last())">
<xsl:value-of select="'_'"/>
</xsl:if>
<xsl:if test="count(./*)=0">
<xsl:value-of select="'=__QUOTE__'"/>
<xsl:value-of select="."/>
<xsl:value-of select="'__QUOTE__'"/>
</xsl:if>
</xsl:for-each>
<xsl:value-of select="' '"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
EOF
dpxmlsh_soma_get_config "$1" "$2" \
| $_DPXMLSH_XMLSTARLET tr /tmp/dpxmlsh_getconfig.$$.xsl - \
| grep "dp:config" \
| grep "=" \
| sed -e 's/^.*_dp:config_//g' -e 's/__QUOTE__/"/g' -e "s/^/$3_config_/g"
_dpxmlsh_pipestatus $FUNCNAME ${PIPESTATUS[@]}
local rc=$?
rm -f /tmp/dpxmlsh_getconfig.$$.xsl
return $rc
# This approach didn't work because I could not index multiple
# anonymous inner objects without direct access to xslt.
# dpxmlsh_soma_get_config $1 \
# | $_DPXMLSH_XMLSTARLET sel -N dp="http://www.datapower.com/schemas/management" \
# -T -t \
# -m '//*' -m 'ancestor-or-self::*' \
# -v 'name()' \
# -i 'string-length(@name)!=0' \
# -o _ -v '@name' \
# -b \
# -i 'not(position()=last())' \
# -o _ \
# -b \
# -i 'count(./*)=0' \
# -o '[' -v 'count(../preceding-sibling::*)' -o "]=__QUOTE__" -v . -o "__QUOTE__" \
# -b \
# -b \
# -n \
# | grep "dp:config" | grep = | sed -e 's/^.*_dp:config_//g' -e 's/__QUOTE__/"/g'
}
# Import config into shell variables.
# Takes 3 args:
# 1: ObjectType (or "")
# 2: ObjectName (or "")
# 3: Prefix name (or "")
function dpxmlsh_get_config_import ()
{
# First clear out old stuff
eval $(set | grep "^$3_config_" | sed -e 's/^/unset /g' -e 's/=.*$//g')
# Then pull in the new
eval $(dpxmlsh_get_config_list "$1" "$2" "$3" | sed 's/^/export /g')
}
# Print imported config. Note that this can also be used to filter
# the config after it is already imported, provided the import was less
# specific than the print.
# Takes 3 args:
# 1: ObjectType (or "")
# 2: ObjectName (or "")
# 3: Prefix name (or "")
function dpxmlsh_get_config_importprint ()
{
local pattern="$3_config"
if [ ! -z "$1" ] ; then
pattern="${pattern}_$1"
else
pattern="${pattern}_[a-zA-Z0-9]*"
fi