# TnmMap.tcl --
#
#	This file contains utilities to manipulate Tnm maps and 
#	map items. It also includes a set of standard monitoring
#	procedures.
#
# Copyright (c) 1996-1997 University of Twente.
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# @(#) $Id: TnmMap.tcl 852 1997-09-19 23:07:05Z schoenw $

package require Tnm
package require TnmInet $tnm(version)
package provide TnmMap $tnm(version)

namespace eval TnmMap {
    namespace export GetIpAddress GetIpName GetSnmpSession
}

# TnmMap::GetIpAddress --
#
#	Return an IP address of a Tnm map item.
#	See the user documentation for details on what it does.
#
# Arguments:
#	node	The map item for which we want to get an IP address.
# Results:
#	The IP address of the given node or an error if it is not
#	possible to lookup an IP address.

proc TnmMap::GetIpAddress {node} {
    set ip [lindex [$node cget -address] 0]
    if {! [catch {Tnm::netdb ip class $ip}]} {
	return $ip
    }
    if [string length $ip] {
	if [catch {TnmInet::GetIpAddress $ip} ip] {
	    error $ip
	}
	return $ip
    }
    set name [lindex [$node cget -name] 0]
    if [string length $name] {
	if [catch {TnmInet::GetIpAddress $name} ip] {
            error $ip
        }
	if {[string length [$node cget -address]] == 0} {
	    $node configure -address $ip
	}
        return $ip
    }
    error "failed to lookup IP address for \"$node\""
}

# TnmMap::GetIpName --
#
#	Return an IP name of a Tnm map item.
#	See the user documentation for details on what it does.
#
# Arguments:
#	node	The map item for which we want to get an IP name.
# Results:
#	The IP name of the given node or an error if it is not
#	possible to lookup an IP name.

proc TnmMap::GetIpName {node} {
    set name [lindex [$node cget -name] 0]
    if [string length $name] {
	if [catch {Tnm::netdb ip class $name}] {
	    return $name
	}
	if {! [catch {TnmInet::GetIpName $name} name]} {
	    return $name
	}
    }
    set ip [lindex [$node cget -address] 0]
    if [string length $ip] {
	if [catch {TnmInet::GetIpName $ip} name] {
            error $name
        }
	if {[string length [$node cget -name]] == 0} {
	    $node configure -name $name
	}
        return $name
    }
    error "failed to lookup IP name for \"$node\""
}

# TnmMap::GetSnmpSession --
#
#	Return an SNMP session for a Tnm map item.
#	See the user documentation for details on what it does.
#
# Arguments:
#	node	The map item for which we want to get an SNMP session.
# Results:
#	The SNMP session for the given node or an error if it is not
#	possible to lookup an SNMP session.

proc TnmMap::GetSnmpSession {node} {
    set map [$node map]
    set alias [$node attribute Tnm:Snmp:Alias]
    if [string length $alias] {
	if [catch {snmp generator -alias $alias} s] {
	    error "invalid value \"$alias\" in Tnm:Snmp:Alias for \"$node\""
	}
	return $s
    }
    set config [$node attribute Tnm:Snmp:Config]
    if [string length $config] {
	if [catch {eval snmp generator $config} s] {
	    error "invalid value \"$config\" in Tnm:Snmp:Config for \"$node\""
	}
	return $s
    }

    if [catch {TnmMap::GetIpAddress $node} ip] {
	error $ip
    }
    if {[catch {snmp generator -address $ip} s] == 0} {
	return $s
    }

    error "failed to create an SNMP session to \"$node\""
}


proc TnmMap::GetMessages {item} {
    set cnt 0
    set txt [format "%-20s %-12s %s" "Date & Time" Tag Text]
    foreach msg [$item info messages] {
	append txt [format "\n%-20s %-12s %s" \
		[clock format [$msg time] -format {%b %d %T %Y}] \
		[$msg tag] [$msg text]]
	incr cnt
    }
    if {$cnt} {
	return $txt
    }
    return
}

proc TnmMap::GetEvents {item} {
    set cnt 0
    set txt [format "%-20s %-12s %s" "Date & Time" Tag Arguments]
    foreach msg [$item info events] {
	append txt [format "\n%-20s %-12s %s" \
		[clock format [$msg time] -format {%b %d %T %Y}] \
		[$msg tag] [$msg args]]
	incr cnt
    }
    if {$cnt} {
	return $txt
    }
    return
}

proc TnmMapNotifyViaEmail {item {interval 1800}} {
    set map [$item map]
    set to [$item attribute :Contact]
    if ![string length $to] {
        set to [$map attribute :Contact]
    }
    if ![string length $to] {
	return
    }

    set now [clock seconds]
    set last [$item attribute notify:mail:last]
    if {[string length $last] && $now - $last < $interval} {
	return
    }
    $item attribute notify:mail:last $now

    set name [$item cget -name]
    if ![string length $name] {
	set name [$item cget -address]
    }
    set subject "\[Tnm\]: $name ([$item health] %)"
    set body "\nMessages: (last [$item cget -expire] seconds)\n\n"
    foreach msg [$item info messages] {
	set time [clock format [$msg time] -format {%T}]
	append body [format "%s (%2d):\t %s\n" \
		$time [$msg health] [$msg text]]
    }
    TnmInet::SendMail $to $body $subject
}

proc TnmMapTraceRoute {node {maxlength 32} {retries 3}} {
    set dst [TnmMap::GetIpAddress $node]
    for {set i 0} {$i < $retries} {incr i} { 
	lappend icmparg $dst
    }
    set l ""
    for {set ttl 1} {[lsearch $l $dst] < 0 && $ttl < $maxlength} {incr ttl} {
        set trace [icmp -retries 0 trace $ttl $icmparg]
        set l ""
        set time ""
        foreach {ip rtt} $trace {
            if {[string length $rtt]} {
                if {[lsearch $l $ip] < 0} { lappend l $ip }
                append time [format " %5d ms" $rtt]
            } else {
                append time "   *** ms"
            }
        }
	set names ""
        foreach ip $l {
            if {[catch {netdb hosts name $ip} name]} {
                if {[catch {dns name $ip} name]} {
                    set name $ip
                }
            }
            if {[lsearch $names $name] < 0} { lappend names $name }
        }
	$node raise TnmMapTraceRoute:Value \
		[format "%2d %-47s %s" $ttl [string range $names 0 46] $time]
    }
    $node raise TnmMapTraceRoute:Done
}

