#!/bin/sh
# the next line restarts using scotty -*- tcl -*- \
exec scotty2.1.10 "$0" "$@"
#
# www_monitor.tcl -
#
# Simple WWW monitoring scripts for Tkined.
#
# Copyright (c) 1993-1996 Technical University of Braunschweig.
# Copyright (c) 2000 Covalent Technologies.
#

package require Tnm 2.1

ined size
LoadDefaults snmp monitor
SnmpInit WWW-Monitor

if {[info exists default(interval)]} {
    set interval $default(interval)
} else {
    set interval 60
}
if {![info exists default(graph)]} {
    set default(graph) false
}

##
## Create a chart. Honours the monitor.graph default definition.
##

proc CreateChart {id x y} {
    global default
    if {$default(graph) != "true"} {
	set id [CloneNode $id [ined create STRIPCHART] $x $y]
    } else {
	set id [CloneNode $id [ined create GRAPH]]
    }
    return $id
}

##
## Tell tkined to restart a command when a saved map gets loaded.
##

proc save { cmd } {
    set cmdList [ined restart]
    if {[lsearch $cmdList $cmd] >= 0} return
    lappend cmdList $cmd
    ined restart $cmdList
}

##
## Remove a command from the list of commands saved in the tkined map.
##

proc forget { cmd } {
    set cmdList ""
    foreach c [ined restart] {
	if {$c != $cmd} {
	    lappend cmdList $c
	}
    }
    ined restart $cmdList
}

##
## Restart a monitor job by calling cmd. Build the list of objects
## from tkined using ined retrieve commands.
##

proc restart { cmd args } {

    global interval

    if {! [catch {expr [lindex $args 0] + 0}]} {
	set itv  [lindex $args 0]
	set jid  [lindex $args 1]
	set args [lrange $args 2 end]
	set old_interval $interval
	set interval [expr int($itv)]
    }

    foreach id $args {
	catch {ined -noupdate clear $id}
	catch {ined -noupdate clear $id}
    }
    catch {eval $cmd $args}

    if {[info exists old_interval]} {
	set interval $old_interval
    }
}

##
## Monitor a SNMP variable in a stripchart. This version uses two
## procedures. The ShowVariable proc is called by the job scheduler
## to send SNMP requests at regular intervals. SNMP responses are
## processed by ShowVariableProc.
##

proc ShowVariableProc {id status vbl} {
    global cx
    if {$status != "noError"} {
	ined -noupdate values $id 0
	ined -noupdate attribute $id $cx(descr,$id) \
		"$cx(descr,$id) [lindex $vbl 0]"
	return
    }
    set now [mib scan sysUpTime [lindex [lindex $vbl 0] 2]]
    set syntax [lindex [lindex $vbl 1] 1]
    set value  [lindex [lindex $vbl 1] 2]
    set val 0
    switch -glob $syntax {
	Gauge -
	INTEGER -
	Unsigned32 -
	Integer32 {
	    set val $value
	}
	Counter* {
	    if {$now > $cx(time,$id)} {
		set val [expr $value - $cx(value,$id)]
		set val [expr $val / ( ($now-$cx(time,$id)) / 100.0 )]
	    } else {
		set val 0
	    }
	}
    }
    ined -noupdate values $id $val
    ined -noupdate attribute $id $cx(descr,$id) "$cx(descr,$id) $val"
    set cx(value,$id) $value
    set cx(time,$id) $now
    set txt "[ined -noupdate name $id] $cx(descr,$id)"
    MoJoCheckThreshold $id $txt $val
}

proc ShowVariable { ids } {
    global cx
    foreach id [MoJoCheckIds ShowVariable $ids] {
	$cx(snmp,$id) get "sysUpTime.0 $cx(name,$id)" \
		"ShowVariableProc $id %E \"%V\""
    }
}

##
## Start a new monitoring job for the device given by ip. The args
## list contains node ids with the snmp variable oid to display.
##

proc start_www_monitor { ip args } {
    global cx interval

    set ids ""
    set re_args ""

    while {$args != ""} {
	set id [lindex $args 0]
	set var [lindex $args 1]

	if {$id != ""} {
	    set s [SnmpOpen $id $ip]
	    if {[catch {$s get "sysUpTime.0 $var"} vbl]} {
		set name [ined -noupdate name $id]
		set addr [ined -noupdate address $id]
		set msg "Unable to restart monitor job on $name"
		if {$addr != ""} {
		    append msg " \[$addr\]"
		}
		writeln "$msg.\nSNMP get failed: $vbl\n"
		$s destroy
		set args [lrange $args 2 end]
		ined delete $id
		continue
	    }
	    set cx(time,$id)  [mib scan sysUpTime [lindex [lindex $vbl 0] 2]]
	    set cx(value,$id) [lindex [lindex $vbl 1] 2]
	    set cx(snmp,$id) $s
	    set cx(name,$id) $var
            set cx(descr,$id) $var
	    lappend ids $id

	    ined -noupdate attribute $id $cx(descr,$id) $cx(descr,$id)
	}
	
	set args [lrange $args 2 end]
	append re_args " \$$id $var"
    }

    if {$ids != ""} {
	set jid [job create -command [list ShowVariable $ids] \
		 -interval [expr $interval * 1000]]
	save "restart start_www_monitor $interval $jid $ip $re_args"
    }
}

##
## Prepare a monitoring job for the objects in list and the SNMP variable
## given by varname. This proc is also called by other scripts so changes
## must be done very carefully...
##

proc MonitorVariable { id ip wwwlist } {
    set s [SnmpOpen $id $ip]
    set i 0
    foreach wwwinfo $wwwlist {
	set var [lindex $wwwinfo 0]
	set name [lindex $wwwinfo 1]
        incr i
        set err [catch {$s get $var } vbl]
        if {$err || [lindex [lindex $vbl 0] 1] == "noSuchInstance" } {
            ined acknowledge "Monitor variable-index $var on $ip: $err [lindex [lindex $vbl 0] 1]"
        } else {
             set chartid [CreateChart $id [expr 30+$i] [expr 30+$i]]
             ined -noupdate attribute $chartid "SNMP:Config" [$s configure]
             ined name $chartid $name
             eval start_www_monitor $ip { $chartid $var }
        }
    }
    $s destroy
}

##
## This is the command interface to the document access
## monitor.
##

proc "Access Summary" { list } {

    ForeachIpNode id ip host $list {
	set sh [SnmpOpen $id $ip]
	
	# test which www services might be of interest	
	set wwwlist ""
	$sh walk x "wwwServiceName wwwServiceType" {
            set oidstr [split [lindex [lindex $x 0] 0] .]
            set index [lindex $oidstr 12]
            set name [lindex [lindex $x 0] 2]
            set type [lindex [lindex $x 1] 2]
            puts "Index:  $index"
            puts "Name: $name"
            puts "Type: $type"
            if {$type == "wwwServer" } {
		lappend wwwlist [list "wwwSummaryInRequests.$index" $name]
            } else {
                writeln "Virtual host $name ignored (wwwServiceType= $type)"
            }
	}
        MonitorVariable $id $ip $wwwlist
	$sh destroy
    }
}

proc "Bytes Summary" { list } {

    ForeachIpNode id ip host $list {
        set sh [SnmpOpen $id $ip]

        # test which www services might be of interest
        set wwwlist ""
        $sh walk x "wwwServiceName wwwServiceType" {
            set oidstr [split [lindex [lindex $x 0] 0] .]
            set index [lindex $oidstr 12]
            set name [lindex [lindex $x 0] 2]
            set type [lindex [lindex $x 1] 2]
            puts "Index:  $index"
            puts "Name: $name"
            puts "Type: $type"
            if {$type == "wwwServer" } {
                lappend wwwlist [list "wwwSummaryOutLowBytes.$index" $name]
            } else {
                writeln "Virtual host $name ignored (wwwServiceType= $type)"
            }
        }
        MonitorVariable $id $ip $wwwlist
        $sh destroy
    }
}


proc set_bucket_control_parameters { id ip host } {
    global interval

    set sess [SnmpOpen $id $ip]

    set varbinds [list wwwDocCtrlLastNSize.1 \
                                        wwwDocCtrlBuckets.1 \
                                        wwwDocCtrlBucketTimeInterval.1 \
                                        wwwDocCtrlTopNSize.1 ]
    set error [catch { $sess get $varbinds } vbl ]
    if {$error || [lindex [lindex $vbl 0] 1] == "noSuchInstance"} {
        ined acknowledge "Can not retrieve server objects of host $ip: $vbl"
        catch { $sess destroy}
        return
    }

    set wwwDocCtrlLastNSize             [lindex [lindex $vbl 0] 2]
    set wwwDocCtrlBuckets               [lindex [lindex $vbl 1] 2]
    set wwwDocCtrlBucketTimeInterval    [lindex [lindex $vbl 2] 2]
    set wwwDocCtrlTopNSize              [lindex [lindex $vbl 3] 2]

    set result [ined request "WWW-MIB: Bucket Control Info" \
        [list [list "LastN table size '$wwwDocCtrlLastNSize' :" $wwwDocCtrlLastNSize ] \
                [list "Number of buckets '$wwwDocCtrlBuckets' :" $wwwDocCtrlBuckets ] \
                [list "Bucket creation interval '$wwwDocCtrlBucketTimeInterval' (ms):" $wwwDocCtrlBucketTimeInterval ] \
                [list "TopN table size '$wwwDocCtrlTopNSize' :" $wwwDocCtrlTopNSize ] ] \
        [list "set values" cancel] ]

    if {[lindex $result 0] == "cancel"} return

#Should be set as well: [ list wwwDocCtrlBucketTimeInterval.1 Integer32 [lindex $result 3] ] \
    set varbinds [list \
                [ list wwwDocCtrlLastNSize.1 Unsigned32 [lindex $result 1] ] \
                [ list wwwDocCtrlBuckets.1 Unsigned32 [lindex $result 2] ] \
                [ list wwwDocCtrlTopNSize.1 Unsigned32 [lindex $result 4] ] ]

    if {[catch {$sess set $varbinds } error]} {
        ined acknowledge "Set operation failed:" "" $error
    }

    catch { $sess destroy}
}

proc "Set BucketCtrl" { list } {

    foreach node $list {
        set id [lindex $node 1]
        set host [lindex $node 2]
        set ip [lindex $node 3]
        set_bucket_control_parameters $id $ip $host
    }
}


proc set_servers_parameters { id ip host } {
    global interval

    set sess [SnmpOpen $id $ip]

    set err [catch { $sess get [list ctMaxServers.0 \
                                ctMinSpareServers.0 \
                                ctMaxSpareServers.0 ]} vbl ]
    if {$err || [lindex [lindex $vbl 0] 1] == "noSuchInstance"} {
        writeln "Can not retrieve server objects of host $ip: $vbl"
        catch { $sess destroy}
        return
    }

    set maxServers              [lindex [lindex $vbl 0] 2]
    set minSpareServers         [lindex [lindex $vbl 1] 2]
    set maxSpareServers         [lindex [lindex $vbl 2] 2]

    set maxServersLabel "Max Servers '$maxServers' :"
    set minSpareServersLabel "Min Spare Servers '$minSpareServers' :"
    set maxSpareServersLabel "Max Spare Servers '$maxSpareServers' :"

    set result [ined request "Apache Server Process Info" \
        [list [list "Max Servers '$maxServers' :" $maxServers ] \
                [list "Min Spare Servers '$minSpareServers' :" $minSpareServers ] \
                [list "Max Spare Servers '$maxSpareServers' :" $maxSpareServers ] ] \
        [list "set values" cancel] ]

    if {[lindex $result 0] == "cancel"} return

    if {[catch {$sess set [list \
                [ list ctMaxServers.0 Integer32 [lindex $result 1] ] \
                [ list ctMinSpareServers.0 Integer32 [lindex $result 2] ] \
                [ list ctMaxSpareServers.0 Integer32 [lindex $result 3] ] ]} error ]} {
        ined acknowledge "Set operation failed:" "" $error
    }

    catch { $sess destroy}
}

proc "Set Servers" { list } {

    foreach node $list {
        set id [lindex $node 1]
        set host [lindex $node 2]
        set ip [lindex $node 3]
        set_servers_parameters $id $ip $host
    }
}

proc set_extendedstatus_parameters { id ip host } {
    global interval

    set sess [SnmpOpen $id $ip]

    set err [catch { $sess get [list ctExtendedStatus.0 ]} vbl ]
    if {$err || [lindex [lindex $vbl 0] 1] == "noSuchInstance"} {
        writeln "Can not retrieve server objects of host $ip: $vbl"
        catch { $sess destroy}
        return
    }

    if {[lindex [lindex $vbl 0] 2] == 1} {
        set extStatus "enable"
    } else {
        set extStatus "disable"
    }

    set result [ined request "Apache Server Process Info" \
        [list [list "Extended status '$extStatus' :" $extStatus radio "enable" "disable" ] ] \
        [list "set values" cancel] ]

    if {[lindex $result 0] == "cancel"} return

    if {[lindex $result 1] == "enable"} {
        set newExtStatus 1
    } else {
        set newExtStatus 2
    }

    if {[catch {$sess set [list \
                [ list ctExtendedStatus.0 Integer32 $newExtStatus ] ]} error ]} {
        ined acknowledge "Set operation failed:" "" $error
    }

    catch { $sess destroy}
}

proc "Set ExtStatus" { list } {

    foreach node $list {
        set id [lindex $node 1]
        set host [lindex $node 2]
        set ip [lindex $node 3]
        set_extendedstatus_parameters $id $ip $host
    }
}

proc show_vhosts { id ip host } {

    set snmpsess [SnmpOpen $id $ip]
    writeln "Virtual Hosts for $host \[$ip\]:"
    set txt  [format "%-5s %-20s %-30s %s\n" \
		"Index" "Virtual host" "Contact" "Description"]
    try {
        $snmpsess walk x [list wwwServiceName \
                                wwwServiceContact \
				wwwServiceDescription ] {
            set oidstr [split [lindex [lindex $x 0] 0] .]
            set index [lindex $oidstr 12]
            set vhost [lindex [lindex $x 0] 2]
            set contact [lindex [lindex $x 1] 2]
            set descr [lindex [lindex $x 2] 2]
            append txt  [format "%-5s %-20s %-30s %s\n" \
                        " $index" $vhost $contact $descr]
        }
    } msg {
        append txt "$msg"
    }
    writeln $txt
    catch { $snmpsess destroy}
}

proc "Show Vhosts" { list } {

    foreach node $list {
puts $node
        set id [lindex $node 1]
        set host [lindex $node 2]
        set ip [lindex $node 3]
        show_vhosts $id $ip $host
    }
}



##
## Display the jobs currently running.
##

proc "Monitor Job Info" { list } {
    MoJoInfo
}

##
## Modify the state or the interval of a running job.
##

proc "Modify Monitor Job" { list } {
    MoJoModify
}

##
## Set the parameters (community, timeout, retry) for snmp requests.
##

proc "Set SNMP Parameter" {list} {
    SnmpParameter $list
}

##
## Set the default parameters for monitoring jobs.
##

proc "Set Monitor Parameter" {list} {
    MoJoParameter
}

##
## Display some help about this tool.
##

proc "Help WWW-Monitor" {list} {
    ined browse "Help about WWW-Monitor" {
	"The WWW-Monitor contains a set of utilities to monitor www services" 
	"using SNMP requests. You can define thresholds by setting the" 
	"attributes Monitor:RisingThreshold or Monitor:FallingThreshold." 
	"The attribute Monitor:ThresholdAction controls what action will" 
	"be taken if the rising threshold or the falling threshold is" 
	"exceeded. The action types are syslog, flash and write." 
	"" 
	"Access Summary:" 
	"    Monitor the document access in a stripchart." 
	"    The values are displayed as the delta value per second." 
	"" 
	"Monitor Byte Access:" 
	"    Monitor the byte access in a stripchart." 
	"    The values are displayed as the delta value per second." 
	"" 
	"Monitor Job Info:" 
	"    This command display information about all monitoring jobs" 
	"    started by this monitor script." 
	"" 
	"Modify Monitor Job:" 
	"    Select one of the monitoring jobs and modify it. You can change" 
	"    the sampling interval and switch the state from active (which" 
	"    means running) to suspended." 
	"" 
	"Set SNMP Parameter:" 
	"    This dialog allows you to set SNMP parameters like retries, " 
	"    timeouts, community name and port number. " 
	"" 
	"Set Monitor Parameter:" 
	"    This dialog allows you to set the sampling interval and " 
	"    other parameter related to monitoring jobs." 
    }
}

##
## Delete the menus created by this interpreter.
##

proc "Delete WWW-Monitor" {list} {

    global menus

    if {[job info] != ""} {
	set res [ined confirm "Kill running monitoring jobs?" \
		 [list "kill & exit" cancel] ]
	if {$res == "cancel"} return
    }

    DeleteClones

    foreach id $menus { ined delete $id }
    exit
}

set menus [ ined create MENU "WWW-Monitor" \
	   "Access Summary" "Monitor Bytes Access" "" \
           "Set BucketCtrl" "" \
           "Set Servers" "Set ExtStatus" "" \
           "Show Vhosts" "" \
	   "Monitor Job Info" "Modify Monitor Job" "" \
	   "Set SNMP Parameter" "Set Monitor Parameter" "" \
	   "Help WWW-Monitor" "Delete WWW-Monitor" ]
