2006-11-02

Visualization of Progress in MUDding using Python and RRDTool

I like skill-based, level-less MUDs, so this will be about one of such MUDs, but provided here scripts probably can be adopted to any MUD system or even to any log file. Skill-based MUDs send special message to player when he archives progress in character development. Such message can look like this:
 "You learned a trick of casting snake rune" 
Let's try to write some code which can visualise progress of character development on time axis. As input data we will use log of all mud sessions. Saving mud session to log-file can be configured in most mud clients. First we have to extract progression marks and their essence. This can be easily done using following *nix commands:
grep 'You learned a trick of ' atp_log_0 | \
sort | \
sed -e 's/You learned a trick of casting \([a-zA-Z ]\+\).*/\1/'
As a result we will get something like this:
...
snake rune
stoneskin
stoneskin
stoneskin
summon
summon
...
Now the harder part, we have to count occurrences of the same strings and put them to some database. To see changes in time we have to do this regularly, for example every hour. In this case rrdtool would be very good as database engine. Following Python script will do everything what we need:
#!/usr/bin/python

import os
import rrdtool
from sys import argv
from sys import exit

if len(argv) != 2:
    print "USAGE: %s prepared_log" % argv[0]
    exit(1)

lines = open(argv[1], "r").readlines()

counter = {}

spell = ""
for line in lines:
    line = line[:-1]
    if spell == line:
        counter[line] += 1
    else:
        counter[line] = 1
    spell = line

for spell in counter.keys():
    print spell + ": " + str(counter[spell])
    rrdFileName = spell + ".rrd"
    if not os.path.exists(rrdFileName):
        # create rrd file
        rrdtool.create(rrdFileName, '-s', '3600',
            'DS:data:COUNTER:86400:0:50000',
            'RRA:AVERAGE:0.5:1:5000')
        print "created: " + rrdFileName
    rrdtool.update(rrdFileName, 'N:' + str(counter[spell]))
Now we have to play MUD for few hours and collect data. Finally we can attempt to perform visualisation. Manual generation of image[s] with charts from all .rrd files can be tiring, so we will use following Python script:
#!/usr/bin/python

import os

RRD_EXT = ".rrd"

colors = ("B81818","5CDE00","0089DE",
    "1A1F14","DEC400","486B20","1216A1",
    "432A96","913F64","9A3C17","F4A179","4B7B47",
    "7E6C67","51D9F4","559E7B","4B4D35","D8980E","A32245")

defs = ""
lines = ""
num = 0;
numc = 0;
files = os.listdir(".")
for file in files:
    base,ext = os.path.splitext(file)
    if ext == RRD_EXT:
        color = colors[numc]
        defs += 'DEF:d%d="%s":data:AVERAGE ' % (num, file)
        lines += 'AREA:d%d#%s:"%s":STACK ' % (num, color, base)
        num += 1
        if len(colors) == numc:
            numc = 0;
        else:
            numc += 1

command = 'rrdtool graph test.png --end now --start end-58h ' + \
'--height 400 --width 600 ' + defs + ' ' + lines

k = os.system(command)
if k == 0:
    os.system("eog test.png")
else:
    print "error code: ", k
"eog" is a standard Gnome image viewer. Of course you can put here your favourite one.

2006-10-31

Cisco/Catalyst Vlans on Ports - final solution

With great help from Cisco NMS EMEA TAC, I have finally found solution to map tagged and untagged vlans to physical ports on the Cisco Catalyst switches, without using SNMP Community String Indexing (CSI). With CSI one can select entity/instance of multi-instanced MIBs, but it is quite awkward and not well supported on most NMSs because you have to use separate community strings for every instance. On Catalysts, BRIDGE-MIB is a multi-instanced MIB, instances of which exist separately for every VLAN. Its dot1dBaseTable is the only way to map bridge port numbers to interfaces from ifTable. Some SNMP tables use bridge port numbering to point physical ports, so to bind data from that tables to something more tangible than bridge port number (like interface from IF-MIB or physical port from ENTITY-MIB) one have to scan dot1dBaseTable 3000 times , in case of having 3000 vlans!


I came up against BRIDGE-MIB and CIS in Vlan->Port mapping case because of the port numbering used in vmMembershipSummaryTable (CISCO-VLAN-MEMBERSHIP-MIB). I have been using that table to get access ports (ports with only one untagged vlan) for given vlan. CISCO-VLAN-MEMBERSHIP-MIB has another table - vmMembershipTable - which looked promising, but due to some kind of blackout of my mind, I thought that vmMembershipTable is write only and I have to use that damn vmMembershipSummaryTable. Now I know that vmMembershipTable can be read, is indexed in the the same manner as interfaces in ifTable and has one very useful column: vmVlan.


So for tagged vlans use vlanTrunkPortTable and for untagged - vmMembershipTable. Booth contain links to interfaces in ifTable, which can be easily linked to physical ports in ENTITY-MIB, then.