Data Visualization

Ive been collecting statistics from our AMS500 for about 8 months now. Its all stored in a mysql database. I have the base of everything done for the grapher. It works great at displaying whats happened in the last X hours. I want to start figuring out how I can leverage the data over time though now. Do statistical analysis. Do Mondays have a bigger hit than the Payroll Processing day? Which was the most active day?

I can write the code to do this, its no problem, the question is, “what data makes the most sense to build from?”

Ive packaged my entire app as a VM built in RHEL5. I’m considering rebuilding it in CENTOS5 and distributing it on the VMware marketplace for free. This type of app is really worthless if everything is running fine though. But I work in an industry where instead of fixing the problem, they want to point fingers at who is causing the problem. No downtime for maintenance… until it breaks. Ugh.

Other news. I had a  guy find me for an old VMware VCB script I wrote for 3.0 and 3.5 I wrote a while back. The script uses VBS and VCB to backup VM’s to local storage, logs and everything. I released it under the GPL. Gave him the new version and a HTA I created for backing up individual VM’s with a multi-select interface. He was pretty pleased. I was jazzed about that for days.

Its really amazing when members of the community reach out and  thank people for their work. I don’t know how many people are using that script, but it sure is nice to know one other person is happy.

Top Talkers graph, rambling about data visualization

I added a new data visualization type to my graphing application. Top Talkers.
Where previous visualizations were dependant on you selecting what lun / raid group / controller / Controlling Factors, then choosing what information to look at, this new visualization will always display the highest average control points?

I really need to learn some new lingo for this, its driving me nuts. What do you call the variables which make up the name of an object?

Anyways. So lets name the two ideas until I figure out what they are actually called. You have the Coordinates (Controller 1, Shelf 15, Drive 5) of the Object and the Descriptions (IOPS, Usage, Hit Time, ETC) of that specific Object.

An Object at a specific set of Coordinates has (a / many) Description(s).

Alrighty.

This object then can always be found at its coordinates.

Previous visualizations required you to specify the coordinates of a specific object to retrieve information about it. This visualization finds the highest average of a chosen Description for each individual Object, then graphs the first X percent.

The SQL query which lists the objects by the descending average of a chosen description allows the graph to have different objects displayed as the status of the device changes.

Drupal Graphing Module

This works rather well here. Now I am left alone, if someone needs to know whats going on with the San, they go to the graph. Once again, I made my job obsolete for the most part. Griffin is my name, efficiency is my game.

Real Time San Graphing

As an expansion of the San Graphing Excel document I created. I engineered a module for drupal which dynamically acquires a structure to graph.

The data must:

  • Be in a database
  • Be structured logically
  • Have control values (Can be Multiple)
    • They will be used as “Where” in the SQL Query.
    • Singular or multiple control values are accepted.

Example:

In a SAN Array, you may have multiple shelves of drives (control value: Shelf). You will have multiple drives (control value: HDU). And you have the time(s) that you wish to view (control value: Time Stamp).

After these control values which limit the results, you need to select the information you actually wish to graph. In the further San Example, we could graph IOPS, MB/s, etc.

Each graph is a piece of content in drupal. Its a node, which can be acted upon with other modules, like Panels. Panels can then group the nodes and display a dashboard at a path like so:

Its possible in the future to use AJAX to dynamically reload each individual graph, but I have yet to implement that, currently the page is refreshed every X seconds.

Because the content creation form is dynamically created, its never out of date. If new luns/raidgroups/shelves are added, it is automatically displayed at the next content creation.

For the AMS500 the San can produce performance logs. These logs were used in Excel, they have been modified a bit more and cleaned up using Sed and now python can import them directly into a Mysql database using the following script.


#!/usr/bin/python
'''
Created on Apr 20, 2010

@author: ghernandez
'''

import time
import datetime
import sys
import MySQLdb

#Variables
MySQLHost = "HOSTNAME"
MySQLUser = "USERNAME"
MySQLPasswd = "PASSWORD"
Database = "DATABASE"

Table_Lines = []
ImportDate= "Unknown!"
SearchArray= [
    ("Port Information", 2),
    ("RG Information", 2),
    ("LU Information", 2),
    ("Cache Information", 1),
    ("Partition Information", 2),
    ("Processor Information", 1),
    ("Drive Information", 3),
    ("Backend Information", 3)
]

file = open(sys.argv[1], "r")
#file = open("/Storage-Logs/05.14/pfm00000-05.14_07.10.pfm", "r")
TopofFile = file.tell()

def AddEntry(databaseName, TableName, RowName, ListofLists_Entries):

    conn = MySQLdb.connect (host = "10.20.15.240",
                               user = "phpMyAdmin",
                               passwd = "phpMyAdmin",
                               db = "phpmyadmin")
    cursor = conn.cursor ()
    cursor.execute ("SELECT VERSION()")
    row = cursor.fetchone ()
    print "server version:", row[0]

def OpenFile_Count_Entries():
    global ImportDate
    linenum=1
    while 1:
        line = file.readline()
        '''Make sure the file is correct'''
        if linenum == 1 and "No." not in line:
            break
        ''' Get  Import Date '''
        if linenum == 2:
            ImportDate = line.split('+')[1].replace('/', "-")[:-2]
        '''If the line is empty and does not have a next line, quit.'''
        if line == '':
            break

        ''' Catch all header lines and mark line numbers'''
        if "---- " in line:
            Table_Lines.append(line[5:-7])
        linenum += 1

'''
    @note: Entries are number of fields to remove on the subsequent lines.
           The format of the perf files breaks a table down and the fields
           are in multiple sections. But X number of "Entries" will be repeated
           on the subsequent fields.
'''
def Add_Info(SearchString, Entries): #Returns a tuple or two varibles, entries, and
    file.seek(TopofFile)
    line = file.readline()
    '''Advance to SearchString'''
    while True:
        line = file.readline()
        if SearchString in line:
            line = file.readline() #Advance to next line.
            break
        if line == '': break

    Titles = [ '' ]
    InfoDict = {}

    while "----" not in line and not line == '':

        #print line
        if '\n' in line: line = line[:-1]
        if '\r' in line: line = line[:-1]
        array = line.split('+')
        '''The first two entries in this perf log will always be CTL and Port,
           we only need them once. We delete the first two elements in the array'''
        if "CTL" in line and not "----" in line:
            if Titles == [ '' ]:
                Titles = array
            else:
                for i in range(Entries):
                    del array[0]

                Titles += array
        else:                   #If its not a title, its a datum.

            if tuple(array[:Entries]) not in InfoDict.keys():
                InfoDict[tuple(array[:Entries])] = list(array[Entries:])
            else:
                currange = tuple(array[:Entries])
                for i in xrange(Entries):
                    del array[0]
                InfoDict[currange] += array

        line = file.readline()  

    '''
    #Check that the two numbers are the same.
    print "Number of titles          ", len(Titles)

    counter = 0
    iterator = 0
    for name in InfoDict.keys():
        iterator +=1
        counter += len(list(name) + InfoDict[name])
        a = counter / iterator

    print "Number of entries captured",a
    '''
    return Titles, InfoDict

def SortedDictionaryValues(adict):
    keys = adict.keys()
    keys.sort()
    return keys, map(adict.get, keys)

def main():
    conn = MySQLdb.connect(host = MySQLHost,
                           user = MySQLUser,
                           passwd = MySQLPasswd,
                           db = Database
                           )
    c = conn.cursor ()

    OpenFile_Count_Entries()

    for arg in SearchArray:
        #See if we have an entry for this date time already
        c.execute('SELECT TimeStamp FROM `' + Database + "`.`" + arg[0] + '` WHERE TimeStamp = "' + ImportDate + '" LIMIT 0, 1')
        #Skip inserts if the timestamp already exists
        if str(c.fetchone()) == "None":
            Titles, EntriesDict = Add_Info(arg[0], arg[1])

            InsertString = "INSERT INTO `" + Database + "`.`" + arg[0] + "` ( \n`Entries`, \n"

            #Build insert query string.
            for name in Titles:
                InsertString += "`" + name + "`,\n"

            #Remove last "," and add closing ")"
            InsertString = InsertString[:-1] + "\n`TimeStamp`\n)"

            #Build Value strings
            #First entry is null as it should be auto incrementing.
            ValueString = "VALUES "
            SortedKeys, SortedOutputs = SortedDictionaryValues (EntriesDict)
            counter = 0
            for arg in SortedKeys:
                Valuetuple = ("0", ) +  arg + tuple(SortedOutputs[counter]) + (ImportDate, )
                ValueString += str(Valuetuple) + ",\n"
                counter += 1

            #Append ";" and remove ","
            ValueString = ValueString[:-2] +";"     

            #print InsertString
            #print ValueString

            c.execute(InsertString + ValueString)

        else:
             print "Duplicate Data!"
             break
    c.close();
    conn.close()

#    c.execute("SELECT * FROM `" + SearchArray[7][0] + "`")

#    x = c.fetchone()

'''---------------------------------------------
                    Start!
---------------------------------------------'''
# @todo: Add a check to see if an entry for this timestamp has already been added.
if __name__ == '__main__':
      main()

Im pretty happy at this point with it. The next step with this module would be to create an interface in python which would query cisco routers and create the same structured database. Then we could create graphs of performance statistics from across our network and get rid of solar winds.

AMS500 Stuff

Yah. So the excel app was such a hit I was asked to make it real time. So I wrote a python script that took the text files and imported them into a MySQL database. Then I created a drupal module that queries the database and uses “Open Flash Chart” to display.

Best part is, I made it dynamic. So you could take the module and point it at any database, write an interface.php for how to retrieve the arrays in the form you want, and pass it back to the rest of it.

Each graph is a new node. Nodes can be displayed with Views or Panels.

It all works out pretty nice. I got the alpha done yesterday. And it works, but its not as beautiful as I want. So back into the code I go.

AMS 500 Perf Worksheet

So, I am upto ver 1.4 now for the perf sheet. It does everything that I can think of. Im working on another one to display brocade stuff.

Main screen displays all features of the app. You can import perf files right from Storage Nav Modular, or import formatted ones where spaces are stripped for easy import.

Four types of graphs are able to be created.

  1. Port, Processor, and Cache Graphs
    • Creates several graphs that illustrate various Controller data.
  2. Raid Group Graphs
    • Creates a high overview of activity for the san.
    • Instead of drilling down into individual luns, which you may have hundreds, you get a high level view of the usage.
  3. Lun Graphs
    • Uses the lun information. Creates 11 unique graphs. (There is also a graph created displaying only a single controller [a / b graphs])
    • 33 Graphs total created. 11 unique, 11 for Controller 0, and 11 for Controller 1
  4. HDU Operating Rate
    • Creates a graph with the operating rate over time.
    • Good to see if you are maxing out your drives. To see if you need to use faster media.
  5. You can import a constitute file from the AMS500 to build out the different mappings.
    • Requires “Lun Names” to be filled in by hand.
    • Lun names can be the server host names that the luns are assigned to.
    • Allows all graphs to immediately show which server is doing what.
  6. Percentage Input
    1. Not all information is needed. Too much information makes the graphs unreadable.
    2. Allows you to see the top X% or the Bottom % (if inverse is turned on

Im hoping that there are other AMS users out there that can give me more ideas for this. The file is fully extendible. To add a completely new graph, its one line in most cases. There are images in the attached gallery.

Here it is: Download

This is released completely under the GPL. License is included in sheet. It is released thusly so that I may have bugs found and ideas given to improve it.

Here is a bash script that can be set to run via Cron to auto create perf files.  It requires Storage Navigator Modular CLI to be installed and setup.

#!/bin/bash
mydate=`date +%m.%d_%H.%M`
daymonth=`date +%m.%d`
export STONAVM_HOME=/home/ghernandez/SNM-CLI >> /Storage-Logs/auperform.log
$STONAVM_HOME/auperform -unit priv -auto 2 -pfmstatis -path "/Storage-Logs" >> /Storage-Logs/auperform.log
mv /Storage-Logs/pfm00000.txt /Storage-Logs/pfm00000-$mydate.txt >> /Storage-Logs/auperform.log
sleep 60</code>

if [ -e "/Storage-Logs/$daymonth" ]
then
mv -v "/Storage-Logs/pfm00000-$mydate.txt" "/Storage-Logs/$daymonth/" &gt;&gt; /Storage-Logs/auperform.log
sed 's/$/\r/' -i "/Storage-Logs/$daymonth/pfm00000-$mydate.txt"
sed -f /Storage-Logs/Formatting.sed "/Storage-Logs/$daymonth/pfm00000-$mydate.txt" &gt; "/Storage-Logs/$daymonth/pfm00000-$mydate.pfm"
else
mkdir /Storage-Logs/$daymonth
mv -v "/Storage-Logs/pfm00000-$mydate.txt" "/Storage-Logs/$daymonth/" &gt;&gt; /Storage-Logs/auperform.log
sed 's/$/\r/' -i "/Storage-Logs/$daymonth/pfm00000-$mydate.txt"
sed -f /Storage-Logs/Formatting.sed "/Storage-Logs/$daymonth/pfm00000-$mydate.txt" &gt; "/Storage-Logs/$daymonth/pfm00000-$mydate.pfm"
fi

Cisco Study notes.

CSMA/CD – Carrier Sense Multiple Access with Collision Detection.

Store and Forward:

Cut Through

Fragment Free, 64bytes

——————————–

Username Stuff

en – > conf t -> username <name> secret <password>

Enable SSH

Set Domain Name – > ip domain-name <name>

Create key -> crypto key generate rsa

Hitachi graphing performance stuff

Ive finished about 80% of it. I still have one major chunk of coding left to do. The code isnt pretty or beautiful, but its functional and it works. I have no doubt that chunks of it could have been pulled and turned into functions that take multiple inputs and then they all be tied together with while loops, but hey, whatever.

The only thing I have left to do is to create the HDU perf graphs. Excel only allows you to map 255 data series per chart. 15 hdu per shelf, 10 shelf = 150. I could technically put all of them for a standard ams500 on one chart. But 255 lines on one chart is going to be impossible to read.

One of the things I am interested in doing and throwing in is summing the values of each series, then sorting by size. IE, take a spreadsheet, Columns by Rows, Series name by Date. Depending on the data type (percentage, microseconds, hits, iops, etc) I could have it sum, then only display series lines that are greater than X. I mean, in operating rates for a HDU, do you really want to see all of the ones that are idle or under 10%? What about raid groups that have iops under 10 for a 2 minute session? Its all additional things to add as “features” not bugs. Thoughts though.

I originally had my app tie into my San info spreadsheet with names, luns, sizes, etc, so that luns could be named. This of course is a huge waste of processing power, so I just added a spreadsheet to the app.

You can fill in that sheet with lun numbers and names. This is a good thing because if you do this, then when you create graphs of lun iops or hits, it will name the series by server name. Its quite nice.

Overall, I am happy with what I have so far. I scripted an auperform to run every 10 for 2 minutes, then rename the file and dump it in a folder with todays date.

The app is automated for multi file import. So I can just go to a folder, ctrl-a, ok, and wait for 5 minutes. Then everythings in and I can graph graph graph.

Excel is so slow though. Im wondering if MRTG could do a better job or if I should use another app, or just code something real time for php. That way when files are dumped I could auto graph them and put them in a dashboard updated every 10 minutes. The nice thing about excel is that I can select a series in the graph and see points and what the name of the series is.

Ramble Ramble. Hm.

Hitachi Performance Application

Hitachi Data Systems sells a Performance Monitor and Tuning Application for ~ 12grand. They will show pretty graphs of whats going on. This is nice. Looking at the thousands of lines of perf info is a headache in a box.

Seriously. You can read a perf output and look at other ones and compare and contrast and poke and prod and massage your head all day long. Eh.

So I wrote a vba Excel app that will import those nasty pfmxxxx.txt files and create a spreadsheet. Colors all of the table headers and the area info. (Raidgroup Info, Lun Info, Cache Info) etc. Makes it look real nice and professional.

Then because Im a masochist, I decided well, Whats the possibility of automating a the perf log thing so that it will spew a log at me every X minutes, that way I can import a days worth of data across multiple spreadsheets.

So I did that with a bash script and the CLI for linux. Three lines. auperf whatever with output of directory x
rename that file to a datestamp (pfm-mm.dd.yyyy-hh.mm.ss.txt [ seconds just incase you know. You cant run a perf report twice in a minute prolly, but almost never in the same second ]

So now I have y amount of spreadsheets spanning a day. Each spreadsheet is ~4k lines. Yay.

Well, since each header has the same name

I can go from header RG to next RG numbers inbetween what what what

Now I have a pretty graph. All this is done in VBA now mind you.

One button to import pfm, one to create a graph.

Since I love variables, its all dynamic.

When its complete Ill post photos.

sentences getting smaller

Quite small now.

Goodnight.

Update

Got my VCP4. Next up RHCE. Woot woot.