KB_5K0070: STAT5000 Programming with the ISE Restful API via AutoIT.

stickyraffleSoftware and s/w Development

Nov 4, 2013 (4 years and 5 days ago)

98 views

KB_5K007
0:
STAT5000


Programming
with
the ISE Restful
API via AutoIT.



Description:


Thi
s KB article summarizes the STAT
5000
application and in the process will hopefully give you
pointers on how to modify the application for your
own purposes.


STAT5000 was designed and coded in one day as an
exercise to see how quickly a useful application
could be turned around using the Restful interface
into the ISE. The design goal of the program was
pretty simple. Basically, just jam as much
performance and

status related information into a
single screen AND do this for multiple ISEs so that
we could monitor host performance across multiple
ISEs from the ISE perspective. It was a very
, very,

long day (the author had only used AUTOIT for two
other programs pr
eviously).


Why use AutoIt?

In late 2009,

a

quick ‘google’
search of the internet for ‘best automated windows
scripting language’ indicated that one method that
stood out was a tool called autoit. This tool has been
around in the industry for 10+ years no
w and has
quite a following, is relatively simple to

use, and is
VERY feature rich. This, along with the desire to use
a language that was ‘free’ but also easy to pick up for
both programmers and non
-
programmers AND that
was capable of creating a final exe
cutable that had no
installation, dll, or registry dependencies all factored
into the decision to use it.


Interesting sidebar for those MAG Classic customers
out there. Almost 10 years ago AutoIt was used by
Xiotech Tech Marketing to create a program that

ran
as a service on customers servers. The application
just sat there and monitored the servers volume
freespace and when freespace got too low the
application would use the RMC
easyscripting/autoscripting language (which this
author also designed) to tel
l the MAGNITUDE
storage array to expand its VDisk. VERY early
precursor to our existing XIP functionality.


THE PROGRAM
:


STAT5000 was designed from the screen outwar
ds
(i.e. literal
napkin design). The final screen looked
like the following
. Red
-
Green rul
es apply (better to
be handy than handsome)
:




The screen is organized as follows:




1)

The u
pper left is the ISE selector/info/button
region. This is where you can:

a.

type in a new IP address (in the box below
the list of IP addresses) and then press Add
(or just hit enter).

b.

delete (remove) and ISE from the list by
selecting
the IP address

and pressing d
elete
(button or key).

c.

Change the sampling interval by clicking the
Sample= button (it will cycle through 5 or so
different selections.)

Rates aren’t neces
sarily
exact when you used the fastest sampling
due to v
ariances in TCP traffic

but it is pretty
fast.

d.

Start and Stop captures. Use the Start
Captures button…it will change to Stop
Captures once captures are started up.
Capture interval is sampling interva
l AND
all ISE performance info will be captured to
a file that contains the IP address of the ISE
with a csv extension. The format can then be
fed straight into excel. Column headers are
named such that a left to right sort will pull
all qdepths together f
or example (lazy
method of quick plotting

in excel
).

e.

Age captures…allows you to start a new
capture but first age out the existing
one…keeps 8 levels deep.

f.

And

in the legend area you can

see what all
the colors/symbols/etc are for on the rest of
the screen
.





2)

The o
ther 8 regions are each a separate ISE

as
shown above
. Each of these regions shows:

a.

The ise/mrc/battery/powersupply/datapac
status and fw versions/etc where appropriate

b.

The number of disks (i.e. each show as a disk
symbol) within each datapac P
LUS the status
and activity in each disk. The status is shown
by the color of the top of the disk (green is
ok, yellow is reduced capacity, and red is
failed). The performance is reflected in the
color of the disk ‘body’ itself with
colors
mixed via an alg
orithm that takes into
consideration IOPS, KBPS, QD, and
LATENCY. Lighter and brighter the busier
they are. Also, under each disk are up to
three horizontal lines, the length of which
indicate
s

the relative qdepth+latency for the
top line, the iops for the

middle line, and the
kbps for the bottom line. Finally, if the
mouse is dragged over any disk, a tooltip
will appear showing you much more detailed
info about that disk as shown in the
full
screenshot above

(mouse cursor symbol is
missing from capture)
. L
eft clicking while
this screen is up will save the information to
a text file and then pop that file up via
notepad (useful if you want to remember the
info and you have refresh set too fast…the
tooltip will continue to update dynamically
as you hold the m
ouse over a disk.)

c.

The region under the disk symbols is a recent
add
ition

(i.e. not in the first day of coding). It
can be an eyechart but it shows the
performance of each individual volume
(there can be up to 240 of them) or, if
selected by the user, this

region can just
show the performance of each host hba.
These are tiny symbols, but, like the physical
disk info you can drag the mouse over these
little dots and a tooltip will tell you more
info about them (vdisk name, actual
performance numbers,

host na
me, wwn,

etc.)

d.

Below this is the dashboard display (taken
somewhat literally when coded up.) On this
screen, total ISE is shown in pink, MRC1 is
brown, and MRC2 is an off green. Think of
the IOPS as a tachometer, the KBPS as a
speedometer, and the transfer

size
(essentially kbps/iops) is the fuel gauge in
the middle. The things that look like eyes are
lights that get larger and brighter the higher
the qdepth and latency gets (so if you see big
red eyes it is really busy.)

And of course the
actual readings a
re show
n

to the right of that.


Given that you know what you want to display, the
only other major task in this design was to determine
how to

retrieve the data and analyze
it. Retrieval is
discussed further down in this document,

but it is
worth noting a
few good starting points for accessing
restful information from the ISE. Before reading the
rest of this document, if you have an ISE available
right now do the following:


1)

From a web browser, enter either one of your
ISEs IP addresses into the web browser

address
bar followed by a /query (i.e. 10.10.20.30/query).

2)

You will get something that looks like the
following:



<?
xml version="1.0" encoding="ISO
-
8859
-
1"
?>


-

<
array
self
="
http://10.20.54.32/storage/arr
ays/1BC10089
">



<
status

value
="
0
"

string
="
Operational
" />




<
globalid
>
1BC10089
</
globalid
>




<
id
>
20000014C367348C
</
id
>




<
name
>
ISE
</
name
>




<
serialnumber
>
1BC10089
</
serialnumber
>




<
model
>
ISE1400
</
model
>




<
vendor
>
XIOTECH
</
vendor
>


...and so on…

3)

Cut and paste the http string into the browser and
press return.

You will be requested for the
Adminstrator login and password.
NOTE:
STAT5000 has the Administrator default
password hard coded into it.

4)

Take other http:// lines that

you see in the
resulting page and try looking at those


Now things will make more sense

when you look at
what this
code does (i.e. it is a big parsing, analysis,
and display engine for some of this data.)


Of note is that STAT5000

was actually written and

tested by the deve
loper while working from Florida
and his mountain home in Georgia

but
while
monitoring ISEs simultaneously in both Minnesota
and Colorado to ensure that this would work fine
over normal home internet connections. Using a 5
year old lapto
p to boot.
It was also necessary to slow
down the refresh rates from once a second to once
every two seconds as it got hard to pause or grab
information at the right moment when it was
changing so fast
, so atomic speed of retrieval wasn’t
an issue for this

type of application
.


Screen resolution needs to be 128
0 by 950 at least.
Some PCs

exhibit no flicker at all, some see an
occasional catchup flicker. Learning curv
e on the
developers part who normally programs in C and
C++ (who

also
happens to be writing

this KB article
).


Before delving into the code itself, a few more points
on the code design. AutoIt is a powerful language but
it is single threaded from a programmer viewpoint. In
a sense this is good because multi
-
threading can be
somewhat of a headache for novice and exp
ert
programmers alike...often times producing unwanted
results. Not something you want when programming
around your corporate storage. However, multi
-
threading is useful for ‘speeding’ up things (i.e. say
you want instantaneous GUI response so that you can

tooltip around existing data but also need to wait for
xml data to be transferred from the ISEs).


The way you accomplish this functionality usin
g
AutoIt is actually very easy
. You simply
:


1)


write one autoit program that gathers the data (in

this case it

is called GET_XMLS
.exe) and

2)


w
rite a

main p
ro
gram (STAT
5000.exe) that
spawns off the GET_XMLS
.
exe whenever it
needs data

gathered
,

a.

thereby
using the OS for multi
-
tasking/threading

b.

and the filesystem as a ‘buffer’

mechanism
as
follows

i.

STAT
5000 puts

a list of
ISE IP
addresses in an INI file

ii.

t
hen GET_XMLS

1.


looks in that file for

a list of
IP addresses to scan

2.


grabs

all the requested restful
xml data from each of these
ISEs

3.


saves

that information in
to
text files

files,

iii.

then STAT
5000

1.


sees that the da
ta has
appeared,

2.


read
s the info in from the
files,

3.


And then
kicks off another
request a
fter the desired
sample dwell

has expired


Very simple and nothing steps on another process’s
toes.


A side benefit is that this

means that future programs
can be written to use
GET_XMLS.exe and
GET_XMLS
.exe can be upgraded if necessary
without recompiling all the programs that use it.

The
code for GET_XMLS.
exe is only 300 lines or so of
autoit source code, BTW and will be shown be
low.


Also, STAT
5000 is designed without any complex
schemas or data structures. You will see a pretty flat
structure when you look at the code and that was
done entirely to keep things readable and ‘safe’. No
pointers, strings are all essentially infinite

length,
no
complex conditional code that you have to reread a
number of times,
etc. Also, this program is safe
because it is ONLY monitoring an ISE…there is no
configuration code

active in this example at all (i.e.
no volume deletes).

During the entire de
velopment
and subsequent testing of STAT5000 not a single
‘impact’ was ever seen on an ISE due to running this
program.


Now for the code. Sources for STAT5000 are
currently about 1300 lines of code. The code that is
provided with the SDK might vary a bit
from this
documents contents but hopefully the gist will carry
forwards. The following section will go through the
code from top to bottom and after that section will be
one final section that summarizes Autoit.


When developing with AutoIt, you will want
to
create an empty directory so
mewhere, put the source
for STAT5000.au3 and GET_XMLS
.au3 in that
directory,
build GET_XMLS to create
GET_XMLS
.exe (or ju
st copy in the existing
GET_XMLS
.exe that is provided with the sdk) and
then you can
modify and either b
uild the
STAT
5000.au3 or run it interactively. It doesn’t
seem to run any slower interactively so is very usable
either way. The Editor that comes with AutoIt has
the
capability of doing full syntax and rules checking on
the code so is very handy as well.


The Code:


Each
of the two executables (GET_XMLS.exe and
STAT
5000.exe) are standalone executables (i.e. just
place them in a directory on a windows box and run
them).

If run without each other, STAT
5000 will
inform you that it
needs to be able to see
GET_XMLS
.exe in

the same directory and
GET_XMLS
.exe will just explain how to run it and
how to construct the ini file it uses in case you have
some standalone method you are
testing (i.e. without
using STAT5000). Again, STAT
5000 will normally
be all y
ou ru
n and it will call GET_XMLS
.exe when
needed.


Sources for each executable are totally contained in

the files GET_XMLS.au3 and STAT
5000.au3 which
are described in a bit more detail as follows.


GET_XMLS.AU3:


GET_XMLS
.au3 was originally constructed as part
of a test program called rest5000 (which
accessed
restful data and put it into a rudimentary report
format). With STAT5000 it was modularized
to work
with both rest5000, STAT
5000, and any other
applications that might be developed.


Quick comment on commen
ts.
Autoit has comments
(anything that starts with a ;) and also has comment
block capability (#cs/#ce)
:


;GET_XMLS for 5K
-

TRB 1/2010

#include

<String.au3>

#include

<Constants.au3>

#include

<GuiEdit.au3>

#include

<Date.au3>

#include

<File.au3>

#include

<
Array.au3>

#include

<Misc.au3>


The include files above are all included with AutoIt.
Interestingly they are just other source files so good
tutorials also.


$version

=

"1.0"

$iniLocation

=

"torun.ini"


global

$ise_ips
[
64
],
$ip_cnt


AutoItSetOption
(
"WinDetectHiddenText"
,
1
)

$ip_cnt

=

0


I use a simple file blocking mechanism
(get_xmls.blk) to prevent recursion. Notice that the
onexit call ensure that the file is removed no matter
how the program is terminated…although there is a
slight hole if you
were to power cycle your PC at just
the right time (then you just need to manually delete
the file).


if

fileexists
(
"get_xmls.blk"
)

then

exit

Opt
(
"OnExitFunc"
,
"cleanup_on_exit"
)

filewrite
(
"get_xmls.blk"
,
$version
)


Now just read the ini file to figure out
what IP
addresses to process, get all the xmls from them, and
we are done so clean up


read_ini
()

get_all_xmls
()

filedelete
(
"get_xmls.blk"
)


And that is the end of the program…somewhat like
perl it just kind of ends when it doesn’t have
anything but
functions following it. Of course the
p
rogram is in the functions that follow.


Func

read_ini
()


If

(
FileExists
(
$iniLocation
))

Then



$ise_ips

=

_getISE_IPS
()


if
(
UBound
(
$ise_ips
)

>

1
)

Then


If

(
stringlen
(
$ise_ips
[
1
])>
0
)

Then


$ip_cnt
=
UBound
(
$ise_ips
)

-
1


Endif


Endif


Endif

EndFunc


Func

get_all_xmls
()


for

$i

=

1

to

UBound
(
$ise_ips
)

-

1



;for each

IP address


get_xmls
(
$ise_ips
[
$i
],
$i
)


Next


EndFunc


The above logic is pretty self explanatory, but the
bulk of the logic follows. The get_xmls routine is
where the commandline is processed. From this it
basically figures out what it has to grab in terms of
xml data from each ISE. NOTE: get_xmls CAN be
run standalone to just go out and grab data…if you
run i
t without any options it will tell you what you
need to do as shown in the MsgBox below…up to
how to create the INI file if you hadn’t used stat5000
to do that for you.


Aside from some convoluted logic that was placed in
the code to ‘skip’ queries if they

had previously been
done during this session, the logic in get_xmls
basically just calls GetAndSave to actually do the
getting of the XML and then the saving of it to a file.


Func

get_xmls
(
$ipstr
,
$iv
)


DIM

$path_str
[
1
]


Local

$i


Local

$todo
=
""


$can_skip_query
=
0


if
(
$CmdLine
[
0
]==
0
)

Then


MsgBox
(
0
,
"get_xmls
-

Command Line
Usage"
,
"get_xmls all
-

gets every xml the
program knows about"
&
@CRLF
&
"get_xmls
-
f
perf
-

gets perf xml using old
query"
&
@CRLF
&
"get_xmls perf drives
-

gets
perf and drive
xmls"
&
@CRLF
&
@CRLF
&
"Options:
perf cn ps vol bat host pool ise events dp
drives net"
&
@CRLF
&
@CRLF
&
"NOTE: torun.ini
file controls which MRCs are
queried"
&
@CRLF
&
@CRLF
&
"Example
torun.ini:"
&
@CRLF
&
"[IPS]"
&
@CRLF
&
"ISE_IPS=10
.64.50.118;10.64.50.115"
)


exit


els
eIf
(
$CmdLine
[
0
]==
1

AND

stringinstr
(
$CmdLine
[
1
],
"all"
)>
0
)

Then


$todo
=
",perf,cn,ps,vol,bat,host,pool,ise,ev
ents,dp,drives,net"


Else


For

$i

=

1

To

$CmdLine
[
0
]


if
(
$CmdLine
[
$i
]==
"
-
f"
)

Then


$can_skip_query
=
1


Else


$todo
&=
","
&
$CmdLine
[
$i
]


EndIf


Next


Endif


if
(
$can_skip_query

==

1
)

Then


$can_skip_query
=
0


if
(
fileexists
(
"query_"
&
$ipstr
&
".hdr"
))

Then


$query_resp
=
fileread
(
"query_"
&
$ipstr
&
".hdr"
)


if
(
StringInStr
(
$query_resp
,
" 200
OK"
)>
0
)

Then



$query_resp
=
fileread
(
"query_"
&
$ipstr
&
".xml"
)


if
(
stringlen
(
$query_resp
)>
30
)

Then

$can_skip_query
=
1


endif


EndIf


endif


if
(
NOT

(
$can_skip_query

==

1
))

Then


$query_resp

=

GetAndSave
(
$ipstr
,
""
,
"/query"
,
"query"
)

;
figure out where things are


EndIf


if
(
StringInStr
(
$query_resp
,
" 200 OK"
)>
0

OR

$can_skip_query
==
1
)

Then

;did we get
good response


$path_str
=
_StringBetween
(
$query_resp
,
$ipstr
,
'"><Status'
)


;figure
out path


if

NOT

@error

Then


;
grab xml info and analyze it


if
(
StringInStr
(
$todo
,
",perf"
))

Then


$data_resp

=

GetAndSave
(
$ipstr
,
$path_str
[
0
],
"/performanc
e"
,
"perf"
)


if
(
stringlen
(
$data_resp
)<
25
)

then

return


EndIf


if
(
StringInStr
(
$todo
,
",cn"
))

Then


$data_resp

=

GetAndSave
(
$ipstr
,
$path_str
[
0
],
"/controller
s"
,
"mrcs"
)


Endif


if
(
StringInStr
(
$todo
,
",ps"
))

Then


$data_resp

=

GetAndSave
(
$ipstr
,
$path_str
[
0
],
"/powersuppl
ies"
,
"ps"
)


Endif


if
(
StringInStr
(
$todo
,
",vol"
))

Then


$data_resp

=

GetAndSave
(
$ipstr
,
$path_str
[
0
],
"/volumes"
,
"
vols"
)


Endif


if
(
StringInStr
(
$todo
,
",bat"
))

Then


$data_resp

=

GetAndSave
(
$ipstr
,
$path_str
[
0
],
"/batteries"
,
"bats"
)


Endif


if
(
StringInStr
(
$todo
,
",host"
))

Then


$data_resp

=

GetAndSave
(
$ipstr
,
$path_str
[
0
],
"/hosts"
,
"ho
sts"
)


Endif


if
(
StringInStr
(
$todo
,
",pool"
))

Then


$data_resp

=

GetAndSave
(
$ipstr
,
$path_str
[
0
],
"/pools"
,
"po
ols"
)


Endif


if
(
StringInStr
(
$todo
,
",ise"
))

Then


$data_resp

=

GetAndSave
(
$ipstr
,
$path_str
[
0
],
"?all=yes"
,
"
ise"
)


if
(
stringlen
(
$data_resp
)<
25
)

then


sleep
(
2000
)


$data_resp

=

GetAndSave
(
$ipstr
,
$path_str
[
0
],
"?all=yes"
,
"
ise"
)


if
(
stringlen
(
$data_resp
)<
25
)

then


sleep
(
4000
)


$data_resp

=

GetAndSave
(
$ipstr
,
$path_str
[
0
],
"?all=yes"
,
"
ise"
)


EndIf


EndIf


Endif


if
(
StringInStr
(
$todo
,
",event"
))

Then


$data_resp

=

GetAndSave
(
$ipstr
,
$path_str
[
0
],
"/files/even
t"
,
"events"
)


Endif


if
(
StringInStr
(
$todo
,
",dp"
))

Then


$data_resp

=

GetAndSave
(
$ipstr
,
$path_str
[
0
],
"/datapacs"
,
"datapacs"
)


Endif


if
(
StringInStr
(
$todo
,
",drive"
))

Then


$data_resp

=

GetAndSave
(
$ipstr
,
$path_str
[
0
],
"/drives"
,
"d
rives"
)


Endif


if
(
StringInStr
(
$todo
,
",net"
))

Then


$data_resp

=

GetAndSave
(
$ipstr
,
$path_str
[
0
],
"/network"
,
"
network"
)


Endif


EndIf


EndIf

EndFunc

;

; Grab

the text found between the 'cnt'th
occurance of string 'from' and string 'to'
in string src

; …
useful for xml
(case insensitive
search):
$res=_TBetween($sub,"<SerialNumber>","</Ser
ialNumber>")

;

Func

_TBetween
(
$src
,
$from
,
$to
,
$cnt
=
1
)


$x
=
StringInStr
(
$src
,
$from
,
0
,
$cnt
)


if
(
$x
=
0
)

then

return

"
-
"


$x
=
$x
+
stringlen
(
$from
)


$y
=
StringInStr
(
$src
,
$to
,
0
,
1
,
$x
)


if
(
$y
=
0

OR

$y
<
$x
)

then

return

"
-
"


Return

StringMid
(
$src
,
$x
,
$y
-
$x
)

EndFunc


Func

GetAndSave
(
$ipstr
,
$pathstr
,
$substr
,
$fname
)


$ret

=

GetRestData
(
$ipstr
,
$pathstr
&
$substr
)


FileDelete
(
$fname
&
"_"
&
$ipstr
&
".hdr"
)


$y
=
StringInStr
(
$ret
,
"<
?xml"
)


if
(
$y
>
0
)

Then


FileWrite
(
$fname
&
"_"
&
$ipstr
&
".hdr"
,
stringle
ft
(
$ret
,
$y
-
1
))


Else


FileWrite
(
$fname
&
"_"
&
$ipstr
&
".hdr"
,
$ret
)


EndIf


if
(
$y
>
0
)

Then


FileDelete
(
$fname
&
"_"
&
$ipstr
&
".xml"
)


filewrite
(
$fname
&
"_"
&
$ipstr
&
".xml"
,
stringri
ght
(
$ret
,
stringlen
(
$ret
)
-
$y
+
1
))


EndIf


return

$ret

EndFunc


Func

_getISE_IPs
()



$iseipString

=

IniRead
(
$iniLocation
,

"IPS"
,

"ISE_IPS"
,

"NotFound"
)



$iseips

=

stringsplit
(
$iseipString
,

";"
)



if

$iseips

==

"NotFound"

Then



MsgBox
(
0
,

"Exception"
,

"Could not find
IPS Key 'ISE_IPS' in the configuration
file"
)



exit



EndIf



return

$iseips


EndFunc



Oddly enough the logic below could be replaced by a
single line in AutoIt but
then we wouldn’t get the
error checking and retry logic that is provided by this
logic. The logic below takes things down to the
lowest level you can go (i.e. essentially just
TCPConnect, TCPSend, and TCPRecv logic.
Nothing rocket science.


Func

GetRestData
(
$host
,
$get_what
,
$port
=
80
)


Local

$data

=

""


Opt
(
"TCPTimeout"
,
200
)

;
Set TCP Timeout to 200 ms


TCPStartup
()

;
Initialize TCP


$ip

=

TCPNameToIP
(
$host
)

;
convert host name to ip if
necessary


$socket

=

TCPConnect
(
$ip
,
$port
)

;
connect


if
(
$socket

==

-
1
)

Then


$data
=
"TCPConnect Err="
&
@error


else


; SEND HTTP GET


$sent

=

TCPSend
(
$socket
,
'GET
'
&
$get_what
&
' HTTP/1.1'
&
@CRLF
&
'Host:
'
&
$host
&
@CRLF
&
'Authorization: Basic
YWRtaW5pc3RyYXRvcjphZG1pbmlzdHJhdG9y'
&
@CRLF
&
'Accept: */*'
&
@CRLF
&
'Connection:
close'
&
@CRLF
&
@CRLF
)


if
(
$sent
==
0
)

Then

; should be #bytes sent to socket


$data
=
"TCPSend
Err="
&
@error
&
@CRLF


else


$begin
=
TimerInit
()


While

1


If

TimerDiff
(
$begin
)>
120000

Then


_FileWriteLog
(
"GET_XMLS.LOG"
,
"120 second
timeout waiting for TCP data from "
&
$host
)


if
(
$data
==
""
)

Then

$data
=
"TCPRecv
Err=120 second program
timeout"
&
@CRLF


ExitLoop


Endif


$data

&=

TCPRecv
(
$socket
,
1024
)

; Recieve data


If

@error

Then

; are we done yet?


if
(
$data
==
""
)

Then

; not expected ending so log


sleep
(
100
)


$data

&=

TCPRecv
(
$socket
,
1024
)


if

@error

Then


if
(
$data
==
""
)

Then


$data
=
"TCPRecv
Err="
&
@error
&
@CRLF


EndIf


ExitLoop


EndIf


Else


ExitLoop


EndIf


EndIf


WEnd


EndIf


EndIf



TCPCloseSocket
(
$socket
)


TCPShutdown
()


return

$data

EndFunc


Func

cleanup_on_exit
()


;Remove our busy lock since we are dying
for whatever reason


If
(
FileExists
(
"get_xmls.blk"
))

Then

FileDelete
(
"get_xmls.blk"
)

EndFunc


STAT5000.AU3:


STAT5000 uses (to an extreme)
windows
controls
for its GUI logic. This has a side effect of not being
able to resize the windows, but that is good because
doing so would likely hide useful data (as tightly
packed all of the information is in this display.)

It
also is best used in cases like this where you have
just a single screen so you don’t spend all your time
constructing and deconstruction controls.


;STAT5000 for 5K
-

TRB 2/2010

;based loosely on REST5000
-

TRB 1/2009

;based loosely on VIEW5000
-

TRB

12/2009

;which is based loosely on SHOW5000
-

11/2009

;which mimics capability of SHOWALL
-

later
half 2009

; Description:

; Full logic writtin in autoit to:

; 1) gather restful info (xml data) from
MRCs. Migrated this logic to get_xmls.au3.
Standard
now in get_xmls.au3.

; 2) analyze xml data and present the
results to the user in view5000 format, but
optimized to show stats and perf

; 3) also make the information available
in:

; a) raw data (xml) retrieved files of
format [type]_[ip].xml (i.e
.
drives_10.10.20.30.xml)

; 4) provide polling logic to retrieve
data on a timed basis

; 5) GUI (initially) will simply be
view5000 format. Intent is something neat
to watch (and to see performance).

; 6) add logic to change polling rate
(via button
cycle)

; 7) add logic to capture all data into a
csv file for each sample interval

; 8) add logic to stop/start capture

; 9) add logic to age capture data and
then start capturing to an empty file

#include

<String.au3>

#include

<Constants.au3>

#include

<GuiConstants.au3>

#include

<GuiEdit.au3>

#include

<Date.au3>

#include

<Timers.au3>

#include

<GUIListView.au3>

#include

<WinAPI.au3>

#include

<ButtonConstants.au3>

#include

<EditConstants.au3>

#include

<GUIConstantsEx.au3>

#include

<
GUIComboBox.au3>

#include

<ListViewConstants.au3>

#include

<WindowsConstants.au3>

#include

<File.au3>

#include

<Array.au3>

#include

<Misc.au3>

#include

<Math.au3>

#include

<StaticConstants.au3>


You will note that the following

line is NOT in the
get_xmls
source
…when get_xmls.exe runs you will
see it on your lower right screen icon tray…you can
actually pause it (or kill it) in that tray if you want to.
No need for that in a GUI since the GUI is on the
screen so this line allows you to hide it from the
tray.


Opt
(
"TrayIconHide"
,

1
)

; don't put this app icon in the tray

Global

$dll

=

DllOpen
(
"user32.dll"
)

;

; define some 'constants'

;

$VERSION

=

"1.0"

$INILOCATION

=

"torun.ini"

Global

$MWIDTH
=
414
,
$MHIGH
=
288
,
$MCNT
=
8

;
height, width,

and number of 'ise' windows

Global

$DO_HOSTS
=
1
,
$DO_VOLS
=
0

Global

$INIT
=
1
,
$REFRESH
=
0

Global

$MAXIOPS
=
10000
,
$MAXKBPS
=
200000
,
$MAXQD
=
29
,
$M
AXIOSZ
=
1024

Global

$MAX_ISES
=
17

;
actually this is maximum ises + 1

Global

$MAX_VOLS
=
512

;
overkill since current is 240

Global

$MAX_HOSTS
=
256

;
overkill, but unsure of max

Global

$MAX_DISKS
=
40

; max
disks per ise

Global

$MAX_EVENTS
=
512

; max
events gathered

;

; Window forms and buttons

;

Global

$main_form
,
$listview

Global

$add_input
,
$sample_button
,
$delete_button
,
$a
dd_button
,
$agecaps_button
,
$stopstartcaps_bu
tton

Global

$hListView
,
$lastdd
=
-
1

Global

$hEdit
,

$hDC
,

$hBrush
,

$Item

=

-
1
,

$SubItem

=

0

Global

$Style

=

BitOR
(
$WS_CHILD
,

$WS_VISIBLE
,

$ES_AUTOHSCROLL
,

$ES_LEFT
)

Global

$iLastItem

=

-
1
,

$iLastsubitemNR

=

-
1

Global

$doing_get

=

0

; re
-
entrancy check for get_xmls call

Global

$cwnd

;
current window handle (used by subs)

Global

$passcnt
=
0

; tracks
frequency
of slow data gathers

Global

$capture_enabled
=
0

; used
to start and stop csv data capturing

Global

$gather_timer

;
pointer to timer handle

Global

$timestart

=

TimerInit
()

; get
timer ticking and baseline

Global

$begin

; tracks
timer differentials

Global

$L_NUM_BAD
,
$L_NUM_WARN

; color
(green/yellow/red) trackers

Dim

$host_or_vol
[
$MAX_ISES
]

; tracks
what we are looking at (host hbas or
volumes)

;

; Following is what we directly use and
re
trieve via restful calls

; NOTE: not everything in this Global list
is necessarily retrieved or used by this
APP

;

; for the ISE itself

Global

$ise_ips
[
$MAX_ISES
],
$ip_cnt

Global

$ise_iops
,
$ise_kbps
,
$ise_qd
,
$ise_readpct
,
$i
se_readlat
,
$ise_writelat
,
$ise_xfersize

Global

$ise_sts
,
$ise_detail
,
$ise_sn
,
$ise_temp
,
$ise
_dhcp
,
$ise_wol

; for the mrcs

Global

$cn1_iops
,
$cn1_kbps
,
$cn1_qd
,
$cn1_readpct
,
$c
n1_readlat
,
$cn1_writelat
,
$cn1_xfersize

Global

$cn2_iops
,
$cn2_kbps
,
$cn2_qd
,
$cn2_readpct
,
$c
n2_readlat
,
$cn2_writelat
,
$cn2_xfersize

Global

$cn1_ip
,
$cn1_sts
,
$cn1_detail
,
$cn1_sn
,
$cn1_f
wver
,
$cn1_temp
,
$cn1_fcspeed
,
$cn1_lipcnt
,
$cn
1_noscnt

Global

$cn2_ip
,
$cn2_sts
,
$cn2_detail
,
$cn2_sn
,
$cn2_f
wver
,
$cn2_temp
,
$cn2_fcspeed
,
$cn2_lipcnt
,
$cn
2_noscnt

Global

$cn1_conn
,
$cn2_conn

; for the power supplies

Global

$ps1_sts
,
$ps1_detail
,
$ps1_sn
,
$ps1_temp
,
$ps1
_bl1_sts
,
$ps1_bl2_sts

Global

$ps2_sts
,
$ps2_detail
,
$ps2_sn
,
$ps2_temp
,
$ps2
_bl1_sts
,
$ps2_bl2_sts

; for the batteries

Global

$bat1_sts
,
$bat1_detail
,
$bat1_sn
,
$bat1_temp
,
$bat1_charge_sts
,
$bat1_charge_detail
,
$bat1_
charge_left

Global

$bat2_sts
,
$bat2_detail
,
$bat2_sn
,
$bat2_temp
,
$bat2_charge_sts
,
$bat2_charge_detail
,
$bat2_
charge_left

; for the datapacs

Global

$dp1_sts
,
$dp1_detail
,
$dp1_sn
,
$dp1_temp
,
$dp1
_peaktemp
,
$dp1_poolnum
,
$dp1_health
,
$dp1_fwv
er
,
$dp1_model
,
$dp1_sparelevel
,
$dp1_capacity

Global

$dp2_sts
,
$dp2_detail
,
$dp2_sn
,
$dp2_temp
,
$dp2
_peaktemp
,
$dp2_poolnum
,
$dp2_health
,
$dp2_fwv
er
,
$dp2_model
,
$dp2_sparelevel
,
$dp2_capacity

Global

$dp_type
,
$dp_cg

; for the pools

Global

$pool1_sts
,
$pool1_detail
,
$pool1_avail
,
$pool
1_used
,
$pool1_r0
,
$pool1_r1
,
$pool1_r5

Global

$pool2_sts
,
$pool2_detail
,
$pool2_avail
,
$pool
2_used
,
$pool2_r0
,
$pool2_r1
,
$pool2_r5

Global

$r5_cnt
,
$r1_cnt
,
$r0_cnt
,
$numdrives
[
$MAX_ISE
S
]

Dim

$numevent
,
$event_date
[
$MAX_EVENT
S
],
$event_t
ime
[
$MAX_EVENTS
],
$event_desc
[
$MAX_EVENTS
],
$
event_type
[
$MAX_EVENTS
]

; for the volumes (luns) presented to
servers

Dim

$numvol
[
$MAX_ISES
],
$vol_sts
[
$MAX_ISES
][
$MAX
_VOLS
],
$vol_name
[
$MAX_ISES
][
$MAX_VOLS
],
$vol
_policy
[
$MAX_ISES
][
$MAX_VOLS
]

Dim

$vol_size
[
$MAX_ISES
][
$MAX_VOLS
],
$vol_locali
d
[
$MAX_ISES
][
$MAX_VOLS
],
$vol_level
[
$MAX_ISE
S
][
$MAX_VOLS
]

Dim

$vol_gid
[
$MAX_ISES
][
$MAX_VOLS
],
$vol_type
[
$M
AX_ISES
][
$MAX_VOLS
],
$vol_mirror
[
$MAX_ISES
][
$MAX_VOLS
],
$v_h
[
$MAX_ISES
][
$MAX_VOLS
]

Dim

$v_ss
[
$MAX_ISES
][
$MAX_VOLS
],
$wb_cnt

Dim

$vol_iops
[
$MAX_ISES
][
$MAX_VOLS
],
$vol_kbps
[
$
MAX_ISES
][
$MAX_VOLS
],
$vol_riops
[
$MAX_ISES
][
$MAX_VOLS
]

Dim

$vol_wiops
[
$MAX_ISES
][
$MAX_VOLS
],
$vol_rkbps
[
$MAX_ISES
][
$MAX_VOLS
],
$vol_wkbps
[
$MAX_ISES
][
$MAX_VOLS
]

Dim

$vol_readpct
[
$MAX_ISES
][
$MAX_VOLS
],
$vol_avg
xfrsize
[
$MAX_ISES
][
$MAX_VOLS
],
$vol_qd
[
$MAX_
ISES
][
$MAX_VOLS
]

Dim

$vol_readlat
[
$MAX_ISES
][
$MAX_VOLS
],
$vol_wri
telat
[
$MAX_ISES
][
$MAX_VOLS
]

Dim

$vol_qdmax
[
$MAX_ISES
][
$MAX_VOLS
],
$vol_rlatm
ax
[
$MAX_ISES
][
$MAX_VOLS
],
$vol_wlatmax
[
$MAX_
ISES
][
$MAX_VOLS
]

; for the disks themselves

Dim

$numdrive
,
$d_iops
[
$MAX_ISES
][
$MAX_DISKS
],
$d
_kbps
[
$MAX_ISES
][
$MAX_DISKS
],
$d_qd
[
$MAX_ISE
S
][
$MAX_DISKS
]

Dim

$d_readpct
[
$MAX_ISES
][
$MAX_DISKS
],
$d_readla
t
[
$MAX_ISES
][
$MAX_DISKS
],
$d_writelat
[
$MAX_I
SES
][
$MAX_DISKS
]

Dim

$d_gotit
[
$MAX_ISES
][
$MAX_DISKS
],
$d_sn
[
$MAX_
ISES
][
$MAX_DISKS
],
$d_sts
[
$MAX_ISES
][
$MAX_DI
SKS
],
$d_detail
[
$MAX_ISES
][
$MAX_DISKS
]

Dim

$d_capacity
[
$MAX_ISES
][
$MAX_DISKS
],
$d_fwver
[
$MAX_ISES
][
$MAX_DISKS
],
$d_prodid
[
$MAX_ISES
][
$MAX_DISKS
]

Dim

$d_temp
[
$MAX_ISES
][
$MAX_DISKS
],
$d_id
[
$MAX_I
SES
][
$MAX_DISKS
],
$d_rc
[
$MAX_ISES
][
$MAX_DISK
S
]

Dim

$d_5v
[
$MAX_ISES
][
$MAX_DISKS
],
$d_12v
[
$MAX_IS
ES
][
$MAX_DISKS
],
$d_dp
[
$MAX_ISES
][
$MAX_DISKS
],
$d_position
[
$MAX_ISES
][
$MAX_DISKS
]

; hosts

Global

$numhosts
[
$MAX_ISES
],
$host_id
[
$MAX_ISES
][
$M
AX_HOSTS
],
$host_wwn
[
$MAX_ISES
][
$MAX_HOSTS
],
$host_name
[
$MAX_ISES
][
$MAX_HOSTS
]

Dim

$host_type
[
$MAX_ISES
][
$MAX_HOSTS
],
$host_iop
s
[
$MAX_ISES
][
$MAX_HOSTS
],
$host_kbps
[
$MAX_IS
ES
][
$MAX_HOSTS
]

Dim

$host_riops
[
$MAX_ISES
][
$MAX_HOSTS
],
$host_wi
ops
[
$MAX_ISES
][
$MAX_HOSTS
],
$host_rkbps
[
$MAX
_ISES
][
$MAX_HOSTS
]

Dim

$host_wkbps
[
$MAX_ISES
][
$MAX_HOSTS
],
$host_re
adpct
[
$MAX_ISES
][
$MAX_HOSTS
],
$host_avgxfrsi
ze
[
$MAX_ISES
][
$MAX_HOSTS
]

Dim

$host_qd
[
$MAX_ISES
][
$MAX_HOSTS
],
$host_readl
at
[
$MAX_ISES
][
$MAX_HOSTS
],
$host_writelat
[
$M
AX_
ISES
][
$MAX_HOSTS
]

Dim

$host_qdmax
[
$MAX_ISES
][
$MAX_HOSTS
],
$host_rl
atmax
[
$MAX_ISES
][
$MAX_HOSTS
],
$host_wlatmax
[
$MAX_ISES
][
$MAX_HOSTS
]

; following are control tags (used to
display/hide/etc a ControlClick

Dim

$G_TOP
[
$MAX_ISES
],
$G_CN1
[
$MAX_ISES
],
$G_CN2
[
$MAX_ISES
],
$G_DP1
[
$MAX_ISES
],
$G_DP2
[
$MAX_IS
ES
]

Dim

$G_IO_L
[
$MAX_ISES
],
$G_IO_I
[
$MAX_ISES
],
$G_IO
_1
[
$MAX_ISES
],
$G_IO_2
[
$MAX_ISES
]

Dim

$G_KB_L
[
$MAX_ISES
],
$G_KB_I
[
$MAX_ISES
],
$G_KB
_1
[
$MAX_ISES
],
$G_KB_2
[
$MAX_ISES
]

Dim

$G_QD_L
[
$MAX_ISES
],
$G_QD_I
[
$MAX_ISES
],
$G_QD
_1
[
$MAX_ISES
],
$G_QD_2
[
$MAX_ISES
]

Dim

$G_MXLAT_L
[
$MAX_ISES
],
$G_MXLAT_I
[
$MAX_ISES
]
,
$G_MXLAT_1
[
$MAX_ISES
],
$G_MXLAT_2
[
$MAX_ISES
]

Dim

$G_RWPCT_L
[
$MAX_ISES
],
$G_RWPCT_I
[
$MAX_ISES
]
,
$G_RWPCT_1
[
$MAX_ISES
],
$G_RWPCT_2
[
$MAX_ISES
]

Dim

$aa
[
$MAX_ISES
]

;ise subwindow
control
tag

;yes, this actually works but minimal
checking:
InetGet("http://10.64.50.118/query","toddte
st.xml")


The above variables are a bit overkill for this
application (currently) but are included to give you
an idea of some of the other things that
you can
easily gather. The following few lines actually
comprise the entire ‘mainline’ of the code..basically
just a check to make sure you have a compiled
version of get_xmls.exe and then grab whatever ISE
IP addresses we might have, setup the windows GUI

stuff and jump into the main loop.


;*********START OF PROGRAM MAIN
CODE************

If

NOT

fileexists
(
"get_xmls.exe"
)

Then


MsgBox
(
0
,
"ERROR!"
,
"FILE: get_xmls.exe
missing. You need this for Cortex
Polling!"
)


exit

EndIf

; Gather up the MRC IP
addresses

read_ini
()


window_setup
(
"Emprise 5000 Stat Watcher,
Version "
&
$VERSION
)

draw_cheat_sheet
()

draw_ise_windows
(
$INIT
)

main_loop
()

gui_exit
()

;**********END OF PROGRAM MAIN
CODE**************


;
-----------------------------------------
-------------------------------------


; FUNCTIONS

;
-----------------------------------------
-------------------------------------


;

; program stays in the following loop until
user closes app

; all processing
is either a result of user
mouse movement, mouse clicks, keyboard
entry,

; or timer countdowns that kick off xml
data gathering and processing (which then
induce screen updates).

;

Func

main_loop
()


setup_listview
()


$gather_timer
=
_Timer_SetTimer
(
$main_form
,
20
00
,
"_get_xmls"
)


_get_xmls
(
0
,
0
,
0
,
0
)


GUICtrlSetState
(
$add_input
,

$GUI_FOCUS
)

;so that
when we type an IP and press
return we get 'Add'


While

1


$nMsg

=

GUIGetMsg
()


$pos
=
GUIGetCursorInfo
(
$main_form
)

;
NOTE: MouseGetPos is
relative to entire
screen, this is 'local'


show_tool_tip
(
$pos
)


If

_IsPressed
(
"2E"
,

$dll
)

Then


_GUICtrlListView_DeleteItemsSelected
(
$hList
View
)


GUICtrlSetData
(
$listview
,

"#|IP
ADDRESSES
("
&
_GUICtrlListView_GetItemCount
(
$listview
)
&
")"
)


save_ini
()


EndIf


Switch

$nMsg


Case

$GUI_EVENT_CLOSE


save_ini
()


gui_exit
()


Exit


case

$agecaps_button


do_age_captures
()


GUICtrlSetState
(
$add_input
,

$GUI_FOCUS
)


case

$stopstartcaps_button


do_stopstart_captures
()


if

$capture_enabled

Then


$nextbut
=
"Stop Capture"


GUICtrlSetColor
(
$stopstartcaps_button
,
0x801
080
)


Else


$nextbut
=
"Start Capture"


GUICtrlSetColor
(
$stopstartcaps_button
,
0x000
000
)


Endif


GUICtrlSetData
(
$stopstartcaps_button
,
$nextb
ut
)


GUICtrlSetState
(
$add_input
,

$GUI_FOCUS
)


case

$sample_button


$var
=
0


$curbut
=
GUICtrlRead
(
$sample_button
)


if

$curbut
<>
"Sample=off"

Then

_Timer_KillTimer
(
$main_form
,
$gather_timer
)


if

$curbut
=
"Sample=1hr"

Then


$nextbut
=
"Sample=off"


GUICtrlSetData
(
$sample_button
,
$nextbut
)


elseif

$curbut
=
"Sample=off"

Then


$nextbut
=
"Sample=2s"


$var
=
2000


elseif

$curbut
=
"Sample=2s"

Then


$nextbut
=
"Sample=10s"


$var
=
10000


elseif

$curbut
=
"Sample=10s"

Then


$nextbut
=
"Sample=1m"


$var
=
60000


elseif

$curbut
=
"Sample=1m"

Then


$nextbut
=
"Sample=10m"


$var
=
600000


elseif

$curbut
=
"Sample=10m"

Then


$nextbut
=
"Sample=1hr"


$var
=
3600000


endif


if
(
$var
>
0
)

Then



$gather_timer
=
_Timer_SetTimer
(
$main_form
,
$v
ar
,
"_get_xmls"
)


if

@error

OR

$gather_timer
=
0

Then

ContinueLoop


_get_xmls
(
0
,
0
,
0
,
0
)


GUICtrlSetData
(
$sample_button
,
$nextbut
)


GUICtrlSetState
(
$add_input
,

$GUI_FOCUS
)


EndIf


Case

$delete_button


_GUICtrlListView_DeleteItemsSelected
(
$hList
View
)


GUICtrlSetData
(
$listview
,

"#|IP ADDRESSES
("
&
_GUICtrlListView_GetItemCount
(
$listview
)
&
")"
)


save_ini
()


Case

$add_button


$iptest
=
GUICtrlRead
(
$add_input
)


If

$iptest

=

""

OR

StringInstr
(
$iptest
,
","
)<>
0

OR

NOT

StringRegExp
(
$iptest
,
"(
\
d
\
d?
\
d?).(
\
d
\
d?
\
d?)
.(
\
d
\
d?
\
d?).(
\
d{1,})$"
,
0
)

Then


MsgBox
(
16
,
"Enter a Valid
IP address"
,
"Expected input is something
like 10.64.100.101"
)


Else


$addnum

=

_GUICtrlListView_GetItemCount
(
$listview
)


_GUICtrlListView_AddItem
(
$hListView
,

$addnum
+
1
,

0
)


_GUICtrlListView_AddSubItem
(
$hListView
,

$addnum
,

GUICtrlRead
(
$add_input
),

1
,

1
)


_GUICtrlListView_EnsureVisible
(
$hListView
,

$addnum
)


GUICtrlSetData
(
$listview
,

"#|IP ADDRESS:
("
&
_GUICtrlListView_GetItemCount
(
$listview
)
&
")"
)


GUICtrlSetData
(
$add_input
,

""
)


GUICtrlSetState
(
$add_input
,

$GUI_FOCUS
)


save_ini
()


$passcnt
=
0


EndIf


EndSwitch


WEnd


_Timer_KillAllTimers
(
$main_form
)

EndFunc

;

; Get xml data via restful interface and
then process it.

; NOTE: the get process calls another
autoit program called get_xmls so that we
get multi
-
threading

; without the normal pain of
multi
-
threading

;

Func

_get_xmls
(
$hWnd
,
$Msg
,
$iIDTimer
,
$dwTime
)


if
(
$doing_get
==
1
)

THen

Return


$doing_get
=
1


if
(
$ip_cnt
>
0
)

Then


if
(
NOT

fileexists
(
"get_xmls.blk"
))

Then


sleep
(
100
)


if
(
NOT

fileexists
(
"get_xmls.blk"
))

Then


draw_ise_windows
(
$REFRESH
)


if
(
mod
(
$passcnt
,
300
)<>
0
)

Then


Run
(
@ComSpec

&

" /c get_xmls.exe
-
f perf"
,
@ScriptDir
,
@SW_HIDE
)


else


Run
(
@ComSpec

&

" /c get_xmls.exe
perf ise drive"
,
@ScriptDir
,
@SW_HIDE
)


endif


$passcnt
=
$passcnt
+
1


Endif


Endif


EndIf


$doing_get
=
0

EndFunc


Func

process_xmls
(
$ipstr
,
$wh
)


local

$inp
,
$logise
=
""
,
$logcn1
=
""
,
$logcn2
=
""
,
$logd
rives
=
""


$inp
=
FileRead
(
"perf_"
&
$ipstr
&
".xml"
)


if
(
stringlen
(
$inp
)>
100
)

Then


$sub
=
_TBetween
(
$inp
,
"Element
self"
,
"</Element"
)


if
(
stringlen
(
$sub
)<
20
)

Then

$sub
=
_TBetween
(
$inp
,
"Array self"
,
"</Array"
)


$ise_iops
=
_TBetween
(
$sub
,
"<TotalIOPS>"
,
"</T
otalIOPS>"
)


$ise_kbps
=
_TBetween
(
$sub
,
"<TotalKBPS>"
,
"</T
otalKBPS>
"
)


$ise_qd
=
_TBetween
(
$sub
,
"<QueueDepth>"
,
"</Qu
eueDepth>"
)


$ise_readpct
=
_TBetween
(
$sub
,
"<ReadPercent>"
,
"</ReadPercent>"
)


$ise_readlat
=
_TBetween
(
$sub
,
"<ReadLatency>"
,
"</ReadLatency>"
)


$ise_writelat
=
_TBetween
(
$sub
,
"
<WriteLatency
>"
,
"</WriteLatency>"
)


$ise_xfersize
=
_TBetween
(
$sub
,
"<AvgXfrSize>"
,
"</AvgXfrSize>"
)


$sub
=
_TBetween
(
$inp
,
"controllers/1"
,
"</cont
roller"
)


$cn1_iops
=
_TBetween
(
$sub
,
"<TotalIOPS>"
,
"</T
otalIOPS>"
)


$cn1_kbps
=
_TBetween
(
$sub
,
"<TotalKBPS>"
,
"</T
otalKBPS>"
)


$cn1_qd
=
_TBetween
(
$sub
,
"<QueueDepth>"
,
"</Qu
eueDepth>"
)


$cn1_readpct
=
_TBetween
(
$sub
,
"<ReadPercent>"
,
"</ReadPercent>"
)


$cn1_readlat
=
_TBetween
(
$sub
,
"<ReadLatency>"
,
"</ReadLatency>"
)


$cn1_writelat
=
_TBetween
(
$sub
,
"<WriteLatency
>"
,
"</WriteLatency>"
)


$cn1_xfersize
=
_TBetween
(
$sub
,
"<AvgXfrSize>"
,
"</AvgXfrSize>"
)



$sub
=
_TBetween
(
$inp
,
"controllers/2"
,
"</cont
roller"
)


$cn2_iops
=
_TBetween
(
$sub
,
"<Total
IOPS>"
,
"</T
otalIOPS>"
)


$cn2_kbps
=
_TBetween
(
$sub
,
"<TotalKBPS>"
,
"</T
otalKBPS>"
)


$cn2_qd
=
_TBetween
(
$sub
,
"<QueueDepth>"
,
"</Qu
eueDepth>"
)


$cn2_readpct
=
_TBetween
(
$sub
,
"<ReadPercent>"
,
"</ReadPercent>"
)


$cn2_readlat
=
_TBetween
(
$sub
,
"<ReadLatency>"
,
"</ReadLatency>"
)


$cn2_writelat
=
_TBetween
(
$sub
,
"<WriteLatency
>"
,
"</WriteLatency>"
)


$cn2_xfersize
=
_TBetween
(
$sub
,
"<AvgXfrSize>"
,
"</AvgXfrSize>"
)


$numdrive
=
0


for

$idx
=
1

to

40


$sub
=
_TBetween
(
$inp
,
"drives/"
&
$idx
,
"</drive
"
)


if

Stringlen
(
$sub
)>
20

then


$numdrive
=
$numdrive
+
1


$d_gotit
[
$wh
][
$idx
-
1
]=
1


$d_iops
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<TotalIOPS>"
,
"</TotalIOP
S>"
)


$d_kbps
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<TotalKBPS>"
,
"</TotalKBP
S>"
)


$d_qd
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<QueueDepth>"
,
"</QueueDe
pth>"
)


$d_readpct
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<ReadPercent>"
,
"</ReadPe
rcent>"
)


$d_readlat
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<ReadLatency>"
,
"</ReadLa
tency>"
)


$d_writelat
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<WriteLatency>"
,
"</Write
Latency>"
)


$logdrives
=
$logdrives
&
","
&
$d_iops
[
$wh
][
$idx
-
1
]&
","
&
$d_kbps
[
$wh
][
$idx
-
1
]&
","
&
$d_qd
[
$wh
][
$idx
-
1
]&
","
&
$d_readpct
[
$wh
][
$idx
-
1
]&
","
&
$d_readlat
[
$wh
][
$idx
-
1
]&
","
&
$d_writelat
[
$wh
][
$idx
-
1
]


else


$d_gotit
[
$wh
][
$idx
-
1
]=
0


endif


Next


$sub2
=
_TBetween
(
$inp
,
"<volumes"
,
"</volumes"
)


$idx
=
0


$sub
=
_Tbetween
(
$sub2
,
"
<volume"
,
"</volume"
,
$
idx
+
1
)



While

$sub
<>
"
-
"



$idx
=
$idx
+
1


$vol_iops
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<TotalIOPS>"
,
"</TotalIOP
S>"
)


$vol_kbps
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<TotalKBPS>"
,
"</TotalKBP
S>"
)


$vol_riops
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<ReadIOPS>"
,
"</ReadIOPS>
"
)


$vol_rkbps
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<ReadKBPS>"
,
"</ReadKBPS>
"
)


$vol_wiops
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<WriteIOPS>"
,
"</WriteIOP
S>"
)


$vol_wkbps
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<WriteKBPS>"
,
"</WriteKBP
S>"
)


$vol_qd
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<QueueDepth>"
,
"</QueueDe
pth>"
)


$vol_avgxfrsize
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<avgxfrsize>"
,
"</avgxfrs
ize>"
)


$vol_readpct
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<ReadPercent>"
,
"</ReadPe
rcent>"
)


$vol_readlat
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<ReadLatency>"
,
"</ReadLa
tency>"
)


$vol_writelat
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<WriteLatency>"
,
"</Write
Latency>"
)


$vol_rlatmax
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<readlatencymax>"
,
"</rea
dlatencymax>"
)


$vol_wlatmax
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<writelatencymax>"
,
"</wr
itelatencymax>"
)


$vol_qdmax
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<QueueDepthmax>"
,
"<
/Queu
eDepthmax>"
)


$vol_name
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<name>"
,
"</name"
)


$sub
=
_Tbetween
(
$sub2
,
"<volume"
,
"</volume"
,
$
idx
+
1
)



WEnd


$numvol
[
$wh
]=
$idx



$sub2
=
_TBetween
(
$inp
,
"<hosts"
,
"</hosts"
)


$idx
=
0


$sub3
=
_TBetween
(
$sub2
,
"<host"
,
"</host"
,
$idx
+
1
)


While

$sub3
<>
"
-
"


$h_name
=
_TBetween
(
$sub3
,
"<name>"
,
"</name"
)


$tcnt
=
1


$sub
=
_TBetween
(
$sub3
,
"<endpoint
"
,
"</endpoint"
,
$tcnt
)


While

$sub
<>
"
-
"


$tcnt
=
$tcnt
+
1


$idx
=
$idx
+
1


$host_name
[
$wh
][
$idx
-
1
]=
$h_name


$host_wwn
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub3
,
"<wwn>"
,
"</wwn"
)


if
(
$host_wwn
[
$wh
][
$idx
-
1
]=
"
-
"
)

Then


$host_wwn
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub3
,
"<
globalid>"
,
"</globalid
"
)


EndIf


$host_iops
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<TotalIOPS>"
,
"</TotalIOP
S>"
)


$host_kbps
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<TotalKBPS>"
,
"</TotalKBP
S>"
)


$host_riops
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<ReadIOPS>"
,
"</ReadIOPS>
"
)


$host_rkbps
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<ReadKBPS>"
,
"</ReadKBPS>
"
)


$host_wiops
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<WriteIOPS>"
,
"</WriteIOP
S>"
)


$host_wkbps
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<
WriteKBPS>"
,
"</WriteKBP
S>"
)


$host_qd
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<QueueDepth>"
,
"</QueueDe
pth>"
)


$host_avgxfrsize
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<avgxfrsize>"
,
"</avgxfrs
ize>"
)


$host_readpct
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"
<ReadPercent>"
,
"</ReadPe
rcent>"
)


$host_readlat
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<ReadLatency>"
,
"</ReadLa
tency>"
)


$host_writelat
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<WriteLatency>"
,
"</Write
Latency>"
)


$host_rlatmax
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<readlatencymax>"
,
"</rea
dlatencymax>"
)


$host_wlatmax
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<writelatencymax>"
,
"</wr
itelatencymax>"
)


$host_qdmax
[
$wh
][
$idx
-
1
]=
_TBetween
(
$sub
,
"<QueueDepthmax>"
,
"</Queu
eDepthmax>"
)


$sub
=
_TBetween
(
$sub3
,
"<endpoint
"
,
"</endpoint"
,
$tcnt
)


WEnd


$sub3
=
_TBetween
(
$sub2
,
"<host"
,
"</host"
,
$idx
+
1
)


Wend


$numhosts
[
$wh
]=
$idx


Else


return

0


EndIf



$inp
=
FileRead
(
"ise_"
&
$ipstr
&
".xml"
)


if
(
stringlen
(
$inp
)>
100
)

Then


$ise_sts
=
_TBetween
(
$inp
,
'
string="'
,
'"><Details value'
)


$ise_detail
=
_TBetween
(
$inp
,
"<detail>"
,
"</de
tail>"
)


$ise_sn
=
_TBetween
(
$inp
,
"<SerialNumber>"
,
"</
SerialNumber>"
)


$cn1_ip
=
_TBetween
(
$inp
,
"<IpAddress1>"
,
"</Ip
Address1>"
)


$cn2_ip
=
_TBetween
(
$inp
,
"<IpAddress2>"
,
"</Ip
Address2>"
)


$ise_temp
=
_TBetween
(
$inp
,
'<Temperature
value="'
,
'"'
)



$sub
=
_TBetween
(
$inp
,
"/controllers/1"
,
"</con
troller>"
)


$cn1_sts
=
_TBetween
(
$sub
,
'string="'
,
'"'
)


$sub
=
_TBetween
(
$inp
,
"/controllers/2"
,
"</con
troller>"
)


$cn2_sts
=
_TBetween
(
$sub
,
'string="'
,
'"'
)


$sub
=
_TBetween
(
$inp
,
"/datapacs/1"
,
"</datapa
c>"
)


if
(
$sub
==
"
-
"
)

Then

$sub
=
_TBetween
(
$inp
,
"/media/1"
,
"</media>"
)


$dp1_sts
=
_TBetween
(
$sub
,
'string="'
,
'"'
)


$sub
=
_TBetween
(
$inp
,
"/datapacs/2"
,
"</datapa
c>"
)


if
(
$sub
==
"
-
"
)

Then

$sub
=
_TBetween
(
$inp
,
"/media/2"
,
"</media>"
)


$dp2_sts
=
_TBetween
(
$sub
,
'string="'
,
'"'
)


$sub
=
_TBetween
(
$inp
,
"/powersupplies/1"
,
"</p
owersupply>"
)


$ps1_sts
=
_TBetween
(
$sub
,
'string="'
,
'"'
)


$sub
=
_TBetween
(
$inp
,
"/powersupplies/2"
,
"</p
owersupply>"
)


$ps2_sts
=
_TBetween
(
$sub
,
'string="'
,
'"'
)


$sub
=
_T
Between
(
$inp
,
"/batteries/1"
,
"</batte
ry>"
)


$bat1_sts
=
_TBetween
(
$sub
,
'string="'
,
'"'
)


$sub
=
_TBetween
(
$inp
,
"/batteries/2"
,
"</batte
ry>"
)


$bat2_sts
=
_TBetween
(
$sub
,
'string="'
,
'"'
)


$logise
=
","
&
$ise_sn
&
","
&
$ise_sts
&
","
&
int
((
$
ise_temp
*
9
/
5
)+
32
)&
","
&
$ise_iops
&
","
&
$ise_kb
ps
&
","
&
$ise_qd
&
","
&

_


$ise_readlat
&
","
&
$ise_writelat
&
","
&
$ise_rea
dpct
&
","
&
$ise_xfersize


$logcn1
=
","
&
$cn1_sts
&
","
&
$cn1_iops
&
","
&
$cn1
_kbps
&
","
&
$cn1_qd
&
","
&

_


$cn1_readlat
&
","
&
$cn1_writelat
&
","
&
$cn1_rea
dpct
&
","
&
$cn1_xfersize


$logcn2
=
","
&
$cn2_sts
&
","
&
$cn2_iops
&
","
&
$cn2
_kbps
&
","
&
$cn2_qd
&
","
&

_


$cn2_readlat
&
","
&
$cn2_writelat
&
","
&
$cn2_rea
dpct
&
","
&
$cn2_xfersize


Else


return

0


endif



$inp
=
FileRead
(
"query_"
&
$ipstr
&
".xml"
)


if
(
stringlen
(
$inp
)>
100
)

Then


$ise_wwn
=
_TBetween
(
$inp
,
'
string="'
,
'"><Details value'
)


$ise_date
=
_TBetween
(
$inp
,
'date="'
,
'" '
)


$ise_time
=
_TBetween
(
$inp
,
'time="'
,
'">'
)


$sub
=
_TBetween
(
$inp
,
"controllers/1"
,
"/contr
oller"
)


if
(
stringlen
(
$sub
)>
100
)

Then


$cn1_fwver
=
_TBetween
(
$inp
,
"<FwVersion>"
,
"</
FwVersion>"
)


EndIf


$sub
=
_TBetween
(
$inp
,
"controllers/2"
,
"/contr
oller"
)


if
(
stringlen
(
$sub
)>
100
)

Then


$cn2_fwver
=
_TBetween
(
$inp
,
"<FwVersion>"
,
"</
FwVersion>"
)


EndIf


Else


return

0


endif



$inp
=
FileRead
(
"drives_"
&
$ipstr
&
"