#
#   File:    topology-shadow-d.prc
#   Author:  J.M. Heisz, Tom Yuyitung
#   Version:  1.37 02/03/15 16:32:08
#
#   Copyright (c) 1997 Halcyon Inc.
#
#   This file contains procedures which override the following 
#   ClassConsoleShadow methods for the topology module:
#
#   Overridden shadow query methods:
#
#   - getTopologyChildren
#   - getTopologyAdornments
#   - getTopologyInfo
#   - getHierarchyChildren
#   - getIndexedStatus
#   - getStatusDetail
#   - getStatusSummary
#   - topologyWalk
#   - doTopologySearch
#   - doTopologySearchByAlarm
#
#   Overridden support methods:
#
#   - getConsoleDescription
#   - getFullConsoleDescription
#   - getFamilyName
#   - getLocalFamilyAttributes
#   - determineIfHierBranch
#
#   The ClassModule method moduleAction is also overridden 
#
#   These methods are always run in the context of the topology module
#   root node.
#
#   Note that the following ClassConsoleShadow methods are not 
#   overridden:
#
#   - getTopologyDetail
#   - getHierarchyDetail
#

method setAttr { attr } {
    if { "$attr" == "DeTaIlNeEdEd" } {
        return ""
    } else {
        return $attr
    }
}

########################################################
#
# Methods: getTopoProperty
#          getConsoleDescription
#          getFullConsoleDescription
#          getFamilyName
#          getLocalFamilyAttributes
#
# Purpose: Get a topology property 
#
# Input:   none
#
# Output:  property value
#
########################################################
method getTopoProperty { name } {
    if { [ toe_csend value [ locate topoProperties.${name} ] getValue 0 ] } {
        return ""
    }
    return $value
}

method getConsoleDescription {} {
    return [ getTopoProperty topoDesc ]
}

method getFullConsoleDescription {} {
    return [ getTopoProperty topoFullDesc ]
}

method getFamilyName {} {
    return [ getTopoProperty topoFamily ]
}

method getLocalFamilyAttributes {} {
    return ""
}

########################################################
# Method:  determineIfHierBranch
# 
# Purpose: Indicate that topology module root is always
#          a hierarchical branch
#
# Returns: true 
########################################################
method determineIfHierBranch {} {
    return true
}

########################################################
#
# Method:  getTopologyChildren
#
# Purpose: Obtains the topology child information from the
#          database for the topologychild shadow query.
#
# Input:   none
#
# Output:  List of topology entries.  
#          Each list entry is a sub-list of:
#
#  <id> <x> <y> <desc> <family> <topotype> <topocfg> <issoftgroup> 
#  <attr> <url> <targurl>
#
########################################################
method getTopologyChildren {} {

#    ddl print debug "*** getTopologyChildren: [ getModuleParam instance]\n"
    return [ topoDbTopologyChildren [ getModuleParam instance ] ]
}

########################################################
#
# Method:  getTopologyInfo
#
# Purpose: Obtains complete topological information for
#          the topologyinfo shadow query.
#
#          Used for data edit retrieval and cut/paste.
#
# Input:   Two modes:  if blank argument (get operation), returns
#          details for this node.  If non-blank (set operation), 
#          returns details for each object identified in the 
#          incoming info.
#
# Output:  A list of topology data lists, one for each object
#          in question.  Sublist is:
#          <id> <x> <y> <desc> <fulldesc> <hostname> <ipaddr>
#          <netmask> <arch> <family> <topotype> <topocfg>
#          <ispoll> <polltype> <issoftgroup> <readinfo> <writeinfo> 
#          <targetHost> <targetIP> <url>
#
########################################################
method getTopologyInfo { entries } {
#    ddl print debug "*** getTopologyInfo: [ getModuleParam instance]:$entries\n"
    if { $entries == "" } {
        return [ getObjectTopologyInfo "" ]

    } else {
        set instance [ getModuleParam instance ]
        return [ eval topoDbEntityInfo $instance $entries ]
    }
}

method getObjectTopologyInfo { desc } {
    if { $desc == "" } {
        set desc [ getConsoleDescription ]
    }
    set fulldesc [ getFullConsoleDescription ]
    set host [ sysinfo hostname ]
    set ip [ sysinfo ip ]
 
    return [ list [ list [ toe_name ] "" "" $desc $fulldesc $host $ip "" \
           "" [ getFamilyName ] "" "" "true"  "aview" "false" "" "" \
           $host $ip [ getNodeURL "" "" ] ] ]
}


########################################################
#
# Method:  getTopologyAdornments
#
# Purpose: Obtains the topological adornments from the
#          database for the topologyadorn shadow query. 
#
# Input:   none
#
# Output:  List of topology adornments.  Each entry is
#          is a sub-list of:
#            {<id> <x> <y> <type> <config>}
#
########################################################
method getTopologyAdornments {} {
    return [ topoDbAdornmentInfoList [ getModuleParam instance ] ]
}

########################################################
#
# Method:  getHierarchyChildren
#
# Purpose: Obtain a list of the hierarchical child objects from
#          the database for the hierarchychild shadow query.  
#
# Input:   none
#
# Output:  A list of the hierarchical children.  Each
#          entry in the list corresponds to a child,
#          and is a list of the hierarchy detail members:
#
#          <id> <desc> <family> <branch> <issoftgroup> <attr> <url> <targurl>
#
########################################################
method getHierarchyChildren { } {

#    ddl print debug "*** getHierarchyChildren: [ getModuleParam instance]\n"
    return [ topoDbHierarchyChildren [ getModuleParam instance ] ]
}

########################################################
#
# Method:  getIndexedStatus
#
# Purpose: Obtain an indexed status vector. The name cross-references
#          the hierarchy/topology object identifier.
#          for the indexedstatus shadow query.
#
# Usage:  snmp://host:port/<object>?indexedstatus
#
# Input:   none
#
# Output:  List of row ids and status
#          {<id> <status>} ...
#
########################################################
method getIndexedStatus { } {
#    ddl print debug "*** getIndexedStatus: [ getModuleParam instance ]\n"
    set object [ locate entityViewTable.entityViewEntry.entityStatus ]
    set staturl [ toe_send $object getNodeURL "" "" ]
    set statinfo [ toe_send $object getIndexedTableData ]
    return [ topoDbFilterIndexedStatus $staturl $statinfo ]
}

########################################################
#
# Method:  topologyWalk
#
# Purpose: Get a list of urls of entities which are part 
#          of the topology agent's server context (as
#          defined by the event trap destination). This
#          method is used by the topologywalk shadow query.  
#
#
#  Input:  none
#
#  Output: <url-1> <flag-1> ...
#
#          where
#             <url-n> is the url of the entity
#             <flag-n> indicates whether the corresponding 
#                      url is a topology url
#                      1 indicates it is a topology url  
#                      0 indicates it is not a topology url  
# 
#
##########################################################
method topologyWalk {} {
   #
   # get the event trap destination of the topology agent 
   #
   set topoEventDest [ getEventTrapDest ]
   if { $topoEventDest == "" } {
	ddl print warning "topologyWalk: event destination not set"
   }

   set instance [ getModuleParam instance ]
   set entities [ topoDbEntityTargetInfo $instance ]
   
   set retList ""
   foreach entity $entities { 

        lextract $entity 0 id 1 eventDest 3 type 6 url

	#
        # we only care about agent entity
        #
        if { ![ isAgentPollType $type ] } { continue }

	#
	# do not count the entity if it is not a part of the 
	# topology agent's server context
	#
	if { $eventDest != $topoEventDest } { continue }

	#
	# set topology url flag
        #
	lextract [ decomposeSnmpURL $url ] 0 net_loc 2 module

	if [ string match topology* $module ] {
            set flag 1
	} else {
            set flag 0
	}

        #
        # if the url of the form:
        #     snmp://host:port/sym/.../base/mibman/modules
        #
        # convert it to:
        #     snmp://host:port/
        #
        # Both of these urls represent the overall status of 
        # the agent. 
        #
        if { [ regexp "base\/mibman\/modules" $module ] } {
            set url "snmp://$net_loc/" 
        }
	lappend retList $url $flag 
   }

   return $retList
}

########################################################
#
# Method:  getStatusSummary
#
# Purpose: Gets the raw status counts for the statussummary
#          shadow query.  
#
# Input:   none
#
# Output:  Returns list of counts, according to:
#          {ACK DWN ERR WRN INF IRR DIS OFF PRF}
#
# Notes:   Filters redundant status counts associated
#          with topology modules.
#
########################################################
method getStatusSummary { } {

    set alarms "ACK DWN ERR WRN INF IRR DIS OFF PRF"
    foreach alarm $alarms { set $alarm 0 }
    set alarmlen [ llength $alarms ]
    set increment [ expr $alarmlen + 1 ]

    set ids [ topoDbEntityList [ getModuleParam instance ] ]

    foreach id $ids {
        set counts [ getEntityStatusCounts $id ]
	set countlen [ llength $counts ]

        if { $countlen == $alarmlen } {
	    #
	    # non-topology entity, simply add counts to total alarm counts
            #
	    foreach alarm $alarms count $counts {
		incr $alarm $count
	    }

	} else {
	    #
	    # topology entity, only add counts qualified with unique urls
	    #
	    set urls ""
	    for { set j 0 } { $j < $countlen } { incr j $increment } {
	        set url [ lindex $counts $j ]
	        if { [ lsearch $urls $url ] == "-1" } { 
	            lappend urls $url
	            set idx [ expr $j + 1 ]
	            foreach alarm $alarms {
		        incr $alarm [ lindex $counts $idx ]
		        incr idx
	            }
                }
            }
	}
    }
    return [ list $ACK $DWN $ERR $WRN $INF $IRR $DIS $OFF $PRF ]
}

##############################################################################
#
# Method:  getStatusDetail
#
# Purpose: Gets the status counts for each topology entity
#          for the statusdetail shadow query. 
#
# Input:   none
#
# Output:  url-1 {<status counts>} url-2 {<status counts>} ...
#
# Notes:   Removes redundant entries and combines all non-topology
#          counts for the current topology
#
##############################################################################
method getStatusDetail {} {
 
#    ddl print debug "*** getStatusDetail: [ getModuleParam instance ]\n"
 
    set alarms "ACK DWN ERR WRN INF IRR DIS OFF PRF"
    set alarmlen [ llength $alarms ]
    foreach alarm $alarms { set $alarm 0 }
 
    set topo_counts ""
    sliceforeach entity counts entityStatusCounts {
        #
        # for non-topology view entities, add data to the alarm variables
        #
        if { [ llength $counts ] == $alarmlen } {
            foreach alarm $alarms count $counts {
                incr $alarm $count
            }
 
        } else {
            #
            # for topology view entities, append data to topo_counts
            #
            set topo_counts [ concat $topo_counts $counts ]
        }
    }
 
    set myurl [ getModuleURL ]
    set non_topo_counts [ list $ACK $DWN $ERR $WRN $INF $IRR $DIS $OFF $PRF ]
 
    #
    # filter out any counts generated by the current topology view
    # i.e. circular references
    #
    while { "true" } {
        set i [ lsearch $topo_counts $myurl ]
        if { $i == -1 } {
            break
        }
        set topo_counts [ lreplace $topo_counts $i [ expr $i + $alarmlen ] ]
    }
 
    return [ concat $myurl $non_topo_counts $topo_counts ]
}

proc getModuleURL {} {
    set url [ ilookup -d "" internal moduleURL ]
    if { $url == "" } {
        set url [ toe_send [ getModuleRoot ] getNodeURL "" "" ]
        define internal moduleURL $url
    }
    return $url
}

########################################################
#
# Method:  doTopologySearch
#
# Purpose: Obtain hierarchical path information for nodes which
#          match the specified pattern.
#
# Usage:   snmp://host:port/<object>?topologysearch
#          snmp://host:port/<object>?topologysearchhost
#
# Input: pattern:   The pattern to match the name on.
#        isHost:    Whether the name is a host or a topology object name
#
# Output:  Two lists - first is hierarchy detail info for matches
#          { <id> <desc> <family> <branch> <issoft> <attr> <url> <targurl> }
#          Second is a list of hierarchical child object url's to search
#
########################################################
proc doTopologySearch { pattern isHost } {

    set instance [ getModuleParam instance ]
    set entities [ topoDbEntityViewInfo $instance ]
    set object [ locate entityInfoTable.entityInfoEntry.entity ]

    set matchList ""
    set nextList ""
    foreach entity $entities { 

        lextract $entity 0 id 3 desc 4 family 8 attr 10 polltype \
                         11 issoft 12 targurl

        set attr [ setAttr $attr ]

        set isNext 0
        set branch false

        if { [ isAgentPollType $polltype ] } {
            set url $targurl
            if [ string match *topology* $url ] {
                set branch true
                set isNext 1
            }

        } else {
            set url [ toe_send $object getNodeURL "" $id ]
        }
	
	set isMatch 0
	if { $isHost == 1 } {
	    if { $polltype=="ahost" || $polltype=="snmp" || $polltype=="icmp"} {
		set isMatch [ string match *$pattern* $targurl ] 
	    } 

	} else {
            set isMatch [ regexp -nocase $pattern $desc ]
	}

        if { $isMatch == 1 } {
            lappend matchList [ 
                list $id $desc $family $branch $issoft $attr $url $targurl 
            ]
        }

        if { $isNext } {
            lappend nextList [ 
                list $id $desc $family $branch $issoft $attr $url $targurl 
            ]
        }
    }
    return [ list $matchList $nextList ]
}

########################################################
#
# Method:  doTopologySearchByAlarm
#
# Purpose: Obtain hierarchical path information for nodes which
#          match the specified alarm.
#
# Usage:   snmp://host:port/<object>?topologyalarms
#
# Input:   alarm state - DWN|ERR|WRN|INF|DIS|...
#
# Output:  Two lists:
#
#          First is topology chid info for matches
#          { <id> <desc> ... }
#
#          Second is a list of hierarchical child object url's to search
#          { <id> <desc> <family> <branch> <issoft> <attr> <url> <targurl> }
#
########################################################
proc doTopologySearchByAlarm { matchStates } {

    #
    # get the alarm count index 
    #
    set alarmIndices ""
    if { $matchStates == "" } {
	set matchStates "ACK DWN ERR WRN INF IRR DIS OFF PRF"
    }

    foreach matchState $matchStates {
        set alarmIndex [ getAlarmCountIndexByState $matchState ]
        if { $alarmIndex == -1 } {
            ddl print warning "doTopologySearchByAlarm: bad alarm state $matchState\n"
            return [ list "" "" ]
        }
        lappend alarmIndices $alarmIndex
    }

    if { $alarmIndices != "" } {
        set entities [ topoDbEntityViewInfo [ getModuleParam instance ] ]
        set statuslist [ join [ getIndexedStatus ] ]
        set object [ locate entityInfoTable.entityInfoEntry.entity ]
        set entityurl [ toe_send $object getNodeURL "" "" ]
    }

    set matchList ""
    set nextList ""

    #
    # loop for each entity
    #
    foreach entity $entities { 

        #
        # get the specific alarm count for the entity
        #
        set id [ lindex $entity 0 ]
        set counts [ summarizeAlarmCounts $id ]
        foreach alarmIndex $alarmIndices {
            set count [ getAlarmCountByIndex $counts $alarmIndex ]
            if { $count } {
                break
            }
        }

        if { $count } {

            lextract $entity 0 id 1 x 2 y 3 desc 4 family 5 type 6 config \
                             8 attr 10 polltype 11 issoft 12 targurl

            set attr [ setAttr $attr ]

            if { [ isAgentPollType $polltype ] } {
                set url $targurl
            } else {
                set url ${entityurl}#${id}
            }

            #
            # check whether entity target is accessible and set flag
            # accordingly
            #
            set flag "true"
            set entityStatus [ getEntityStatus $id ]
            set parts [ split $entityStatus "\t" ]
            if { [ llength $parts ] == 3 } {
                set state [ lindex $parts 1 ]
                if { $state == "down" || 
                     $state == "error-dn" || 
                     $state == "irr" } { 
                    set flag "false"
                }
            }

            if { $polltype != "aview" || $flag == "false" } {
                set contextUrl [ getNodeURL "" $id ]
                set statidx [ lsearch $statuslist $id ]
                if { $statidx != -1 } {
                    set status  [ lindex  $statuslist [ incr statidx ] ]
                    set topochild [ list $id $x $y $desc $family $type \
                                    $config $issoft $attr $url $targurl ]
                    lappend matchList [ list $topochild $contextUrl $status $flag ]
                } else {
                    ddl print info "doTopologySearchByAlarm: no status for $id\n"
                }
            } else {
                set branch "true"
                lappend nextList [ 
                    list $id $desc $family $branch $issoft $attr $url $targurl 
                ]
            }
        }
    }
    return [ list $matchList $nextList ]
}

##############################################################################
#
# Method:  moduleAction
#
# Purpose: Process topology module unload requests since topology modules
#          have database info and can have child topologies.
#
# Usage:   snmp//host:port/<object>?moduleadminaction
#          snmp//host:port/<object>?moduleoperatoraction
#
# Inputs:  type    - admin|operator
#          action  - unload|enable|disable
#
# Output:  unloadok   - success
#          unloadfail - failure
#
##############################################################################
proc moduleAction { type action } {

    if { $type == "admin" && $action == "unload" } { 
        #
        # topology module unload request, special case
        #
        set modroot [ getModuleRoot ]
        set instance [ getModuleParam instance ]
        set pdu [ getParameter pdu ]

        if { [ toe_csend result $modroot deleteDomainView $instance $pdu ] } {
            ddl print error "moduleAction: $result\n"
            return "unloadfail" 
        }
        return $result

    } else {
        #
        # not a topology module unload request, handle normally
        #
        return [ ClassModule:moduleAction $type $action ]
    }
}

proc getAlarmCountIndexByState { state } {
    set alarms "ACK DWN ERR WRN INF IRR DIS OFF PRF"
    return [ lsearch $alarms $state ]
}

proc getAlarmCountByIndex { counts index } {
    return [ lindex $counts $index ]
}

proc getAlarmCountByState { counts state } {
    set alarms "ACK DWN ERR WRN INF IRR DIS OFF PRF"
    set index [ lsearch $alarms $state ]
    if { $index != -1 } {
        return [ lindex $counts $index ]
    }
    return -1
}

method summarizeAlarmCounts { id } {

    set alarms "ACK DWN ERR WRN INF IRR DIS OFF PRF"
    set alarmlen [ llength $alarms ]

    set counts [ getEntityStatusCounts $id ]
    set countlen [ llength $counts ]

    if { $countlen == $alarmlen } {
        return $counts

    } else {
        foreach alarm $alarms { set $alarm 0 }
        set increment [ expr $alarmlen + 1 ]

	for { set j 0 } { $j < $countlen } { incr j $increment } {
	    set idx [ expr $j + 1 ]
	    foreach alarm $alarms {
	        incr $alarm [ lindex $counts $idx ]
	        incr idx
            }
	}
    }
    return [ list $ACK $DWN $ERR $WRN $INF $IRR $DIS $OFF $PRF ]
}
