# # Copyright 2004-2008 University of Zagreb, Croatia. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # This work was supported in part by Croatian Ministry of Science # and Technology through the research contract #IP-2003-143. # #****h* imunes/nodecfg.tcl # NAME # nodecfg.tcl -- file used for manipultaion with nodes in IMUNES # FUNCTION # This module is used to define all the actions used for configuring # nodes in IMUNES. The definition of nodes is presented in NOTES # section. # # NOTES # The IMUNES configuration file contains declarations of IMUNES objects. # Each object declaration contains exactly the following three fields: # # object_class object_id class_specific_config_string # # Currently only two object classes are supported: node and link. In the # future we plan to implement a canvas object, which should allow placing # other objects into multiple visual maps. # # "node" objects are further divided by their type, which can be one of # the following: # * router # * host # * pc # * lanswitch # * hub # * rj45 # * pseudo # # The following node types are to be implemented in the future: # * text # * image # # # Routines for manipulation of per-node network configuration files # IMUNES keeps per-node network configuration in an IOS / Zebra / Quagga # style format. # # Network configuration is embedded in each node's config section via the # "network-config" statement. The following functions can be used to # manipulate the per-node network config: # # netconfFetchSection { node_id sectionhead } # Returns a section of a config file starting with the $sectionhead # line, and ending with the first occurence of the "!" sign. # # netconfClearSection { node_id sectionhead } # Removes the appropriate section from the config. # # netconfInsertSection { node_id section } # Inserts a section in the config file. Sections beginning with the # "interface" keyword are inserted at the head of the config, and # all other sequences are simply appended to the config tail. # # getIfcOperState { node_id ifc } # Returns "up" or "down". # # setIfcOperState { node_id ifc state } # Sets the new interface state. Implicit default is "up". # #Boeing: # getIfcDumpState { node_id ifc } # Returns "tcpdump on" or "tcpdump off". # #Boeing: # setIfcDumpState { node_id ifc state } # Sets the tcpdump state for the interface # # getIfcQDisc { node_id ifc } # getIfcQDisc { node_id ifc } # Returns "FIFO", "WFQ" or "DRR". # # setIfcQDisc { node_id ifc qdisc } # Sets the new queuing discipline. Implicit default is FIFO. # # getIfcQDrop { node_id ifc } # Returns "drop-tail" or "drop-head". # # setIfcQDrop { node_id ifc qdrop } # Sets the new queuing discipline. Implicit default is "drop-tail". # # getIfcQLen { node_id ifc } # Returns the queue length limit in packets. # # setIfcQLen { node_id ifc len } # Sets the new queue length limit. # # getIfcMTU { node_id ifc } # Returns the configured MTU, or an empty string if default MTU is used. # # setIfcMTU { node_id ifc mtu } # Sets the new MTU. Zero MTU value denotes the default MTU. # # getIfcIPv4addr { node_id ifc } # Returns a list of all IPv4 addresses assigned to an interface. # # setIfcIPv4addr { node_id ifc addr } # Sets a new IPv4 address(es) on an interface. The correctness of the # IP address format is not checked / enforced. # # getIfcIPv6addr { node_id ifc } # Returns a list of all IPv6 addresses assigned to an interface. # # setIfcIPv6addr { node_id ifc addr } # Sets a new IPv6 address(es) on an interface. The correctness of the # IP address format is not checked / enforced. # # getStatIPv4routes { node_id } # Returns a list of all static IPv4 routes as a list of # {destination gateway {metric}} pairs. # # setStatIPv4routes { node_id route_list } # Replace all current static route entries with a new one, in form of # a list, as described above. # # getStatIPv6routes { node_id } # Returns a list of all static IPv6 routes as a list of # {destination gateway {metric}} pairs. # # setStatIPv6routes { node_id route_list } # Replace all current static route entries with a new one, in form of # a list, as described above. # # getNodeName { node_id } # Returns node's logical name. # # setNodeName { node_id name } # Sets a new node's logical name. # # nodeType { node_id } # Returns node's type. # # getNodeModel { node_id } # Returns node's optional model identifier. # # setNodeModel { node_id model } # Sets the node's optional model identifier. # # getNodeCanvas { node_id } # Returns node's canvas affinity. # # setNodeCanvas { node_id canvas_id } # Sets the node's canvas affinity. # # getNodeCoords { node_id } # Return icon coords. # # setNodeCoords { node_id coords } # Sets the coordinates. # # getNodeLabelCoords { node_id } # Return node label coordinates. # # setNodeLabelCoords { node_id coords } # Sets the label coordinates. # # getNodeCPUConf { node_id } # Returns node's CPU scheduling parameters { minp maxp weight }. # # setNodeCPUConf { node_id param_list } # Sets the node's CPU scheduling parameters. # # ifcList { node_id } # Returns a list of all interfaces present in a node. # # peerByIfc { node_id ifc } # Returns id of the node on the other side of the interface # # logicalPeerByIfc { node_id ifc } # Returns id of the logical node on the other side of the interface. # # ifcByPeer { local_node_id peer_node_id } # Returns the name of the interface connected to the specified peer # if the peer is on the same canvas, otherwise returns an empty string. # # ifcByLogicalPeer { local_node_id peer_node_id } # Returns the name of the interface connected to the specified peer. # Returns the right interface even if the peer node is on the other # canvas. # # hasIPv4Addr { node_id } # hasIPv6Addr { node_id } # Returns true if at least one interface has an IPv{4|6} address # configured, otherwise returns false. # # removeNode { node_id } # Removes the specified node as well as all the links that bind # that node to any other node. # # newIfc { ifc_type node_id } # Returns the first available name for a new interface of the # specified type. # # All of the above functions are independent to any Tk objects. This means # they can be used for implementing tasks external to GUI, so inside the # GUI any updating of related Tk objects (such as text labels etc.) will # have to be implemented by additional Tk code. # # Additionally, an alternative configuration can be specified in # "custom-config" section. # # getCustomConfig { node_id } # # setCustomConfig { node_id cfg } # # getCustomEnabled { node_id } # # setCustomEnabled { node_id state } #**** #****f* nodecfg.tcl/typemodel # NAME # typemodel -- find node's type and routing model # SYNOPSIS # set typemod [typemodel $node_id] # FUNCTION # For input node this procedure returns the node's # type and routing model (if exists) # INPUTS # * node_id -- node id # RESULT # * typemod -- returns node's type and routing model in form type.model #**** proc typemodel { node } { return [nodeType $node] } #****f* nodecfg.tcl/getNodeLocation # NAME # getNodeLocation -- get location of the node # SYNOPSIS # set location [getNodeLocation $node_id] # FUNCTION # For input node this procedure returns the name of the CORE box # controlling the node. # INPUTS # * node_id -- node id # RESULT # * location -- returns the location of the node #**** proc getNodeLocation { node } { global $node set loc_tmp [lindex [lsearch -inline [set $node] "location *"] 1] return $loc_tmp } #****f* nodecfg.tcl/setNodeLocation # NAME # setNodeLocation -- set location of the node # SYNOPSIS # setNodeLocation $node_id $location # FUNCTION # For input node this procedure sets the name of the CORE box # controlling the node. # INPUTS # * node_id -- node id # * location -- the name of the CORE box controlling the node #**** proc setNodeLocation { node location } { global $node set i [lsearch [set $node] "location *"] if { $i >= 0 } { set $node [lreplace [set $node] $i $i] } if { $location == "" } { return } lappend $node [list location $location] return } # returns true if any connected peer has the specified location proc nodePeerHasLocation { node location } { foreach ifc [ifcList $node] { set peer [peerByIfc $node $ifc] if { [getNodeLocation $peer] == $location } { return 1 } } return 0 } #****f* nodecfg.tcl/setConfig # NAME # setConfig -- add an element to the *-config # structure # SYNOPSIS # setConfig $strlist $str # FUNCTION # Procedure returns requested element that belongs # to *-config structure. # INPUTS # * strlist -- *-config structure # * cfg -- current *-config that will be extended # with new elements # * str -- new element # RESULT # * strlist -- new *-config sructure #**** proc setConfig { strlist cfg str } { set i [lsearch $strlist "$str *"] if { $i < 0 } { if { $cfg != {} } { set newcfg [list $str $cfg] lappend strlist $newcfg } } else { set oldval [lindex [lsearch -inline $strlist "$str *"] 1] if { $oldval != $cfg } { set strlist [lreplace $strlist $i $i [list $str $cfg]] } } return $strlist } #****f* nodecfg.tcl/getConfig # NAME # getConfig -- get an element of the *-config # SYNOPSIS # getConfig $strlist $str # FUNCTION # Procedure returns requested element that belongs # to *-config structure. # INPUTS # * strlist -- *-config structure # * str -- an element of the *-config structure #**** proc getConfig { strlist str } { return [lindex [lsearch -inline $strlist "$str *"] 1] } #****f* nodecfg.tcl/getCustomEnabled # NAME # getCustomEnabled -- get custom configuration enabled state # SYNOPSIS # set enabled [getCustomEnabled $node_id] # FUNCTION # For input node this procedure returns true if custom configuration # is enabled for the specified node. # INPUTS # * node_id -- node id # RESULT # * enabled -- returns true if custom configuration is enabled #**** proc getCustomEnabled { node } { global $node if { [lindex [lsearch -inline [set $node] "custom-enabled *"] 1] == true } { return true } else { return false } } #****f* nodecfg.tcl/setCustomEnabled # NAME # setCustomEnabled -- set custom configuration enabled state # SYNOPSIS # setCustomEnabled $node_id $enabled # FUNCTION # For input node this procedure enables or disables custom configuration. # INPUTS # * node_id -- node id # * enabled -- true if enabling custom configuration, false if disabling #**** proc setCustomEnabled { node enabled } { global $node set i [lsearch [set $node] "custom-enabled *"] if { $i >= 0 } { set $node [lreplace [set $node] $i $i] } if { $enabled == true } { lappend $node [list custom-enabled $enabled] } } #****f* nodecfg.tcl/getCustomCmd # NAME # getCustomCmd -- get custom configuration command # SYNOPSIS # set command [getCustomCmd $node_id] # FUNCTION # For input node this procedure returns custom command. # INPUTS # * node_id -- node id # RESULT # * command -- custom configuration command #**** proc getCustomCmd { node } { global $node return [lindex [lsearch -inline [set $node] "custom-command *"] 1] } #****f* nodecfg.tcl/setCustomCmd # NAME # setCustomEnabled -- set custom configuration command # SYNOPSIS # setCustomCmd $node_id $command # FUNCTION # For input node this procedure sets custom command. # INPUTS # * node_id -- node id # * command -- custom configuration command #**** proc setCustomCmd { node cmd } { global $node set i [lsearch [set $node] "custom-command *"] if { $i >= 0 } { set $node [lreplace [set $node] $i $i] } lappend $node [list custom-command $cmd] } #****f* nodecfg.tcl/getCustomConfig # NAME # getCustomConfig -- get custom configuration section # SYNOPSIS # set cfg [getCustomConfig $node_id] # FUNCTION # For input node this procedure returns custom configuration section. # INPUTS # * node_id -- node id # RESULT # * cfg -- returns custom configuration section #**** proc getCustomConfig { node } { global $node set customCfgList {} set customcmd "" set customcfg "" set customcmd [lsearch -inline [set $node] "custom-command *"] set customcmdval [lindex $customcmd 1] set customcfg [lsearch -inline [set $node] "custom-config *"] set customcfgval [lindex $customcfg 1] if { $customcmd != "" } { set customid [list custom-config-id generic] set customcmd [list custom-command $customcmdval] set customcfg [list config $customcfgval] set customCfgList [list [list $customid $customcmd $customcfg]] } else { set values [lsearch -all -inline [set $node] "custom-config *"] foreach val $values { lappend customCfgList [lindex $val 1] } } return $customCfgList } proc getCustomConfigByID { node id } { set customCfgList [getCustomConfig $node] foreach element $customCfgList { if { $id == [getConfig $element "custom-config-id"] } { return [getConfig $element "config"] } } return "" } #****f* nodecfg.tcl/setCustomConfig # NAME # setCustomConfig -- set custom configuration command # SYNOPSIS # setCustomConfig $node_id $cfg # FUNCTION # For input node this procedure sets custom configuration section. # INPUTS # * node_id -- node id # * id -- custom-config id # * cmd -- custom command # * cfg -- custom configuration section # * delete -- if delete is set to 1, setCustomConfig is invoked # to delete custom-config with custom-config-id $id # * #**** proc setCustomConfig { node id cmd cfg delete } { global viewcustomid global $node # removes first occurrence of custom-command and custom-config set i [lsearch [set $node] "custom-command *"] if { $i != "-1" } { # remove custom-command set $node [lreplace [set $node] $i $i] # remove custom-config set j [lsearch [set $node] "custom-config *"] set $node [lreplace [set $node] $j $j] } # removes existing custom-config if custom-config-id matches set cnt 0 set indices [lsearch -all [set $node] "custom-config *"] foreach i $indices { set tmp [lindex [set $node] $i] set customCfg [lindex $tmp 1] set cid [lindex [lsearch -inline $customCfg "custom-config-id *"] 1] if { $cid == $id } { set $node [lreplace [set $node] $i $i] } } # adds the new config specified in the dialog box if { $delete == 0 } { if { $cfg != {} && $cmd != {} && $id != {} } { set newid [list custom-config-id $id] set viewcustomid [lindex $newid 1] set newcmd [list custom-command $cmd] set newcfg [list config $cfg] # Boeing: insert the new custom config so it's the first (active) # custom config in the list, or just add it to the end set first [lindex $indices 0] if { $first < 0 } { set first end } set $node [linsert [set $node] $first \ [ list custom-config [list $newid $newcmd $newcfg] ]] #lappend $node [ list custom-config [list $newid $newcmd $newcfg] ] } } } #****f* nodecfg.tcl/netconfFetchSection # NAME # netconfFetchSection -- fetch the network configuration section # SYNOPSIS # set section [netconfFetchSection $node_id $sectionhead] # FUNCTION # Returns a section of a network part of a configuration file starting with the $sectionhead # line, and ending with the first occurrence of the "!" sign. # INPUTS # * node_id -- node id # * sectionhead -- represents the first line of the section in network-config part of # the configuration file # RESULT # * section -- returns a part of the configuration file between sectionhead and "!" #**** proc netconfFetchSection { node sectionhead } { global $node set cfgmode global set section {} # Boeing: read custom config if enabled if { [lindex [lsearch -inline [set $node] "custom-enabled *"] 1] == true } { # this will match the first custom-config encountered set netconf [lindex [lsearch -inline [set $node] "custom-config *"] 1] set tmp [lindex [lsearch -inline $netconf "config *"] 1] if {$tmp != "" } { set netconf $tmp } } else { set netconf [lindex [lsearch -inline [set $node] "network-config *"] 1] } # if 'nocustom' keyword is passed in sectionhead, don't use custom-config if { [lsearch $sectionhead "nocustom"] > -1 } { # remove "nocustom" from sectionhead set sectionhead [lsearch -all -inline -not $sectionhead "nocustom"] # do not read custom config set netconf [lindex [lsearch -inline [set $node] "network-config *"] 1] } # end Boeing foreach line $netconf { if { $cfgmode == "section" } { if { "$line" == "!" } { return $section } lappend section "$line" continue } if { "$line" == "$sectionhead" } { set cfgmode section # Boeing: search the first part of $line for $sectionhead } elseif { "router bgp" == "$sectionhead" } { if { [string first $sectionhead $line 0] == 0 } { set cfgmode section lappend section "$line" } } } } #****f* nodecfg.tcl/netconfClearSection # NAME # netconfClearSection -- clear the section from a network-config part # SYNOPSIS # netconfClearSection $node_id $sectionhead # FUNCTION # Removes the appropriate section from the network part of the configuration. # INPUTS # * node_id -- node id # * sectionhead -- represents the first line of the section that is to be removed from network-config # part of the configuration. #**** proc netconfClearSection { node sectionhead } { global $node set i [lsearch [set $node] "network-config *"] set netconf [lindex [lindex [set $node] $i] 1] set lnum_beg -1 set lnum_end 0 foreach line $netconf { if { $lnum_beg == -1 && "$line" == "$sectionhead" } { set lnum_beg $lnum_end } if { $lnum_beg > -1 && "$line" == "!" } { set netconf [lreplace $netconf $lnum_beg $lnum_end] set $node [lreplace [set $node] $i $i \ [list network-config $netconf]] return } incr lnum_end } } #****f* nodecfg.tcl/netconfInsertSection # NAME # netconfInsertSection -- Insert the section to a network-config part of configuration # SYNOPSIS # netconfInsertSection $node_id $section # FUNCTION # Inserts a section in the configuration. Sections beginning with the # "interface" keyword are inserted at the head of the configuration, and # all other sequences are simply appended to the configuration tail. # INPUTS # * node_id -- the node id of the node whose config section is inserted # * section -- represents the section that is being inserted. If there # was a section in network config with the same section head, it is lost. #**** proc netconfInsertSection { node section } { global $node set sectionhead [lindex $section 0] netconfClearSection $node $sectionhead set i [lsearch [set $node] "network-config *"] set netconf [lindex [lindex [set $node] $i] 1] set lnum_beg end if { "[lindex $sectionhead 0]" == "interface" } { set lnum [lsearch $netconf "hostname *"] if { $lnum >= 0 } { set lnum_beg [expr $lnum + 2] } } elseif { "[lindex $sectionhead 0]" == "hostname" } { set lnum_beg 0 } if { "[lindex $section end]" != "!" } { lappend section "!" } foreach line $section { set netconf [linsert $netconf $lnum_beg $line] if { $lnum_beg != "end" } { incr lnum_beg } } set $node [lreplace [set $node] $i $i [list network-config $netconf]] } #Boeing: proc to find out whether tcpdump should be on for an interface proc getIfcDumpState { node ifc } { foreach line [netconfFetchSection $node "nocustom interface $ifc"] { if { [lindex $line 0] == "tcpdump" } { return "tcpdump on" } } return "tcpdump off" } #Boeing: proc to set tcpdump for an interface proc setIfcDumpState { node ifc state } { set ifcfg [list "interface $ifc"] if { $state == "tcpdump on" } { lappend ifcfg " tcpdump" } foreach line [netconfFetchSection $node "interface $ifc"] { if { [lindex $line 0] != "tcpdump" && \ [lrange $line 0 1] != "no tcpdump" } { lappend ifcfg $line } } netconfInsertSection $node $ifcfg } #****f* nodecfg.tcl/getIfcOperState # NAME # getIfcOperState -- get interface operating state # SYNOPSIS # set state [getIfcOperState $node_id $ifc] # FUNCTION # Returns the operating state of the specified interface. It can be "up" or "down". # INPUTS # * node_id -- node id # * ifc -- The interface that is up or down # RESULT # * state -- the operating state of the interface, can either "up" or "down". #**** proc getIfcOperState { node ifc } { foreach line [netconfFetchSection $node "nocustom interface $ifc"] { if { [lindex $line 0] == "shutdown" } { return "down" } } return "up" } #****f* nodecfg.tcl/setIfcOperState # NAME # setIfcOperState -- set interface operating state # SYNOPSIS # setIfcOperState $node_id $ifc # FUNCTION # Sets the operating state of the specified interface. It can be set to "up" or "down". # INPUTS # * node_id -- node id # * ifc -- interface # * state -- new operating state of the interface, can be either "up" or "down" #**** proc setIfcOperState { node ifc state } { set ifcfg [list "interface $ifc"] if { $state == "down" } { lappend ifcfg " shutdown" } foreach line [netconfFetchSection $node "interface $ifc"] { if { [lindex $line 0] != "shutdown" && \ [lrange $line 0 1] != "no shutdown" } { lappend ifcfg $line } } netconfInsertSection $node $ifcfg } #****f* nodecfg.tcl/getIfcQDisc # NAME # getIfcQDisc -- get interface queuing discipline # SYNOPSIS # set qdisc [getIfcQDisc $node_id $ifc] # FUNCTION # Returns one of the supported queuing discipline ("FIFO", "WFQ" or "DRR") that is activ # for the specified interface. # INPUTS # * node_id -- represents the node id of the node whose interface's queuing discipline is checked. # * ifc -- The interface name. # RESULT # * qdisc -- returns queuing discipline of the interface, can be "FIFO", "WFQ" or "DRR". #**** proc getIfcQDisc { node ifc } { foreach line [netconfFetchSection $node "nocustom interface $ifc"] { if { [lindex $line 0] == "fair-queue" } { return WFQ } if { [lindex $line 0] == "drr-queue" } { return DRR } } return FIFO } #****f* nodecfg.tcl/setIfcQDisc # NAME # setIfcQDisc -- set interface queueing discipline # SYNOPSIS # setIfcQDisc $node_id $ifc $qdisc # FUNCTION # Sets the new queuing discipline for the interface. Implicit default is FIFO. # INPUTS # * node_id -- represents the node id of the node whose interface's queuing discipline is set. # * ifc -- interface name. # * qdisc -- queuing discipline of the interface, can be "FIFO", "WFQ" or "DRR". #**** proc setIfcQDisc { node ifc qdisc } { set ifcfg [list "interface $ifc"] if { $qdisc == "WFQ" } { lappend ifcfg " fair-queue" } if { $qdisc == "DRR" } { lappend ifcfg " drr-queue" } foreach line [netconfFetchSection $node "interface $ifc"] { if { [lindex $line 0] != "fair-queue" && \ [lindex $line 0] != "drr-queue" } { lappend ifcfg $line } } netconfInsertSection $node $ifcfg } #****f* nodecfg.tcl/getIfcQDrop # NAME # getIfcQDrop -- get interface queue dropping policy # SYNOPSIS # set qdrop [getIfcQDrop $node_id $ifc] # FUNCTION # Returns one of the supported queue dropping policies ("drop-tail" or "drop-head") that is active # for the specified interface. # INPUTS # * node_id -- represents the node id of the node whose interface's queue dropping policy is checked. # * ifc -- The interface name. # RESULT # * qdrop -- returns queue dropping policy of the interface, can be "drop-tail" or "drop-head". #**** proc getIfcQDrop { node ifc } { foreach line [netconfFetchSection $node "nocustom interface $ifc"] { if { [lindex $line 0] == "drop-head" } { return drop-head } } return drop-tail } #****f* nodecfg.tcl/setIfcQDrop # NAME # setIfcQDrop -- set interface queue dropping policy # SYNOPSIS # setIfcQDrop $node_id $ifc $qdrop # FUNCTION # Sets the new queuing discipline. Implicit default is "drop-tail". # INPUTS # * node_id -- represents the node id of the node whose interface's queue droping policie is set. # * ifc -- interface name. # * qdrop -- new queue dropping policy of the interface, can be "drop-tail" or "drop-head". #**** proc setIfcQDrop { node ifc qdrop } { set ifcfg [list "interface $ifc"] if { $qdrop == "drop-head" } { lappend ifcfg " drop-head" } foreach line [netconfFetchSection $node "interface $ifc"] { if { [lindex $line 0] != "drop-head" && \ [lindex $line 0] != "drop-tail" } { lappend ifcfg $line } } netconfInsertSection $node $ifcfg } #****f* nodecfg.tcl/getIfcQLen # NAME # getIfcQLen -- get interface queue length # SYNOPSIS # set qlen [getIfcQLen $node_id $ifc] # FUNCTION # Returns the queue length limit in number of packets. # INPUTS # * node_id -- represents the node id of the node whose interface's queue length is checked. # * ifc -- interface name. # RESULT # * qlen -- queue length limit represented in number of packets. #**** proc getIfcQLen { node ifc } { foreach line [netconfFetchSection $node "nocustom interface $ifc"] { if { [lindex $line 0] == "queue-len" } { return [lindex $line 1] } } return 50 } #****f* nodecfg.tcl/setIfcQLen # NAME # setIfcQLen -- set interface queue length # SYNOPSIS # setIfcQLen $node_id $ifc $len # FUNCTION # Sets the queue length limit. # INPUTS # * node_id -- represents the node id of the node whose interface's queue length is set. # * ifc -- interface name. # * qlen -- queue length limit represented in number of packets. #**** proc setIfcQLen { node ifc len } { set ifcfg [list "interface $ifc"] foreach line [netconfFetchSection $node "interface $ifc"] { if { [lindex $line 0] != "queue-len" } { lappend ifcfg $line } } if { $len > 5 && $len != 50 } { lappend ifcfg " queue-len $len" } netconfInsertSection $node $ifcfg } #****f* nodecfg.tcl/getIfcMTU # NAME # getIfcMTU -- get interface MTU size. # SYNOPSIS # set mtu [getIfcMTU $node_id $ifc] # FUNCTION # Returns the configured MTU, or a default MTU. # INPUTS # * node_id -- represents the node id of the node whose interface's MTU is checked. # * ifc -- interface name. # RESULT # * mtu -- maximum transmission unit of the packet, represented in bytes. #**** proc getIfcMTU { node ifc } { foreach line [netconfFetchSection $node "nocustom interface $ifc"] { if { [lindex $line 0] == "mtu" } { return [lindex $line 1] } } # Return defaults switch -exact [string range $ifc 0 2] { eth { return 1500 } ser { return 2044 } } } #****f* nodecfg.tcl/setIfcMTU # NAME # setIfcMTU -- set interface MTU size. # SYNOPSIS # setIfcMTU $node_id $ifc $mtu # FUNCTION # Sets the new MTU. Zero MTU value denotes the default MTU. # INPUTS # * node_id -- represents the node id of the node whose interface's MTU is set. # * ifc -- interface name. # * mtu -- maximum transmission unit of a packet, represented in bytes. #**** proc setIfcMTU { node ifc mtu } { set ifcfg [list "interface $ifc"] foreach line [netconfFetchSection $node "interface $ifc"] { if { [lindex $line 0] != "mtu" } { lappend ifcfg $line } } switch -exact [string range $ifc 0 2] { eth { set limit 1500 } ser { set limit 2044 } } if { $mtu >= 256 && $mtu < $limit } { lappend ifcfg " mtu $mtu" } netconfInsertSection $node $ifcfg } #****f* nodecfg.tcl/getIfcIPv4addr # NAME # getIfcIPv4addr -- get interface IPv4 address. # SYNOPSIS # set addr [getIfcIPv4addr $node_id $ifc] # FUNCTION # Returns the list of IPv4 addresses assigned to the specified interface. # INPUTS # * node_id -- node id # * ifc -- interface name. # RESULT # * addr -- A list of all the IPv4 addresses assigned to the specified interface. #**** proc getIfcIPv4addr { node ifc } { set addrlist {} foreach line [netconfFetchSection $node "interface $ifc"] { if { [lrange $line 0 1] == "ip address" } { lappend addrlist [lindex $line 2] } } # XXX remove this extra check if special OpenVZ case is removed # this forces a search of network-config when no IP address has been found if { [llength $addrlist] == 0 } { foreach line [netconfFetchSection $node "nocustom interface $ifc"] { if { [lrange $line 0 1] == "ip address" } { lappend addrlist [lindex $line 2] } } } return $addrlist } #****f* nodecfg.tcl/setIfcIPv4addr # NAME # setIfcIPv4addr -- set interface IPv4 address. # SYNOPSIS # setIfcIPv4addr $node_id $ifc $addr # FUNCTION # Sets a new IPv4 address(es) on an interface. The correctness of the # IP address format is not checked / enforced. # INPUTS # * node_id -- the node id of the node whose interface's IPv4 address is set. # * ifc -- interface name. # * addr -- new IPv4 address. #**** proc setIfcIPv4addr { node ifc addr } { set ifcfg [list "interface $ifc"] foreach line [netconfFetchSection $node "interface $ifc"] { if { [lrange $line 0 1] != "ip address" } { lappend ifcfg $line } } if { $addr != "" } { lappend ifcfg " ip address $addr" } netconfInsertSection $node $ifcfg return } #****f* nodecfg.tcl/getIfcIPv6addr # NAME # getIfcIPv6addr -- get interface IPv6 address. # SYNOPSIS # set addr [getIfcIPv6addr $node_id $ifc] # FUNCTION # Returns the list of IPv6 addresses assigned to the specified interface. # INPUTS # * node_id -- the node id of the node whose interface's IPv6 addresses are returned. # * ifc -- interface name. # RESULT # * addr -- A list of all the IPv6 addresses assigned to the specified interface. #**** proc getIfcIPv6addr { node ifc } { set addrlist {} foreach line [netconfFetchSection $node "interface $ifc"] { if { [lrange $line 0 1] == "ipv6 address" } { lappend addrlist [lindex $line 2] } } return $addrlist } #****f* nodecfg.tcl/setIfcIPv6addr # NAME # setIfcIPv6addr -- set interface IPv6 address. # SYNOPSIS # setIfcIPv6addr $node_id $ifc $addr # FUNCTION # Sets a new IPv6 address(es) on an interface. The correctness of the # IP address format is not checked / enforced. # INPUTS # * node_id -- the node id of the node whose interface's IPv4 address is set. # * ifc -- interface name. # * addr -- new IPv6 address. #**** proc setIfcIPv6addr { node ifc addr } { set ifcfg [list "interface $ifc"] foreach line [netconfFetchSection $node "interface $ifc"] { if { [lrange $line 0 1] != "ipv6 address" } { lappend ifcfg $line } } if { $addr != "" } { lappend ifcfg " ipv6 address $addr" } netconfInsertSection $node $ifcfg } proc getIfcMacaddr { node ifc } { set addrlist {} foreach line [netconfFetchSection $node "interface $ifc"] { if { [lrange $line 0 1] == "mac address" } { lappend addrlist [lindex $line 2] } } return $addrlist } proc setIfcMacaddr { node ifc macaddr} { set ifcfg [list "interface $ifc"] foreach line [netconfFetchSection $node "interface $ifc"] { if { [lrange $line 0 1] != "mac address" } { lappend ifcfg $line } } if { $macaddr != "" } { lappend ifcfg " mac address $macaddr" } netconfInsertSection $node $ifcfg } #****f* nodecfg.tcl/getStatIPv4routes # NAME # getStatIPv4routes -- get static IPv4 routes. # SYNOPSIS # set routes [getStatIPv4routes $node_id] # FUNCTION # Returns a list of all static IPv4 routes as a list of # {destination gateway {metric}} pairs. # INPUTS # * node_id -- node id # RESULT # * routes -- the list of all static routes defined for the specified node #**** proc getStatIPv4routes { node } { global $node set routes {} set netconf [lindex [lsearch -inline [set $node] "network-config *"] 1] foreach entry [lsearch -all -inline $netconf "ip route *"] { lappend routes [lrange $entry 2 end] } return $routes } #****f* nodecfg.tcl/setStatIPv4routes # NAME # setStatIPv4routes -- set static IPv4 routes. # SYNOPSIS # setStatIPv4routes $node_id $routes # FUNCTION # Replace all current static route entries with a new one, in form of # a list of {destination gateway {metric}} pairs. # INPUTS # * node_id -- the node id of the node whose static routes are set. # * routes -- the list of all static routes defined for the specified node #**** proc setStatIPv4routes { node routes } { netconfClearSection $node "ip route [lindex [getStatIPv4routes $node] 0]" set section {} foreach route $routes { lappend section "ip route $route" } netconfInsertSection $node $section } #****f* nodecfg.tcl/getStatIPv6routes # NAME # getStatIPv6routes -- get static IPv6 routes. # SYNOPSIS # set routes [getStatIPv6routes $node_id] # FUNCTION # Returns a list of all static IPv6 routes as a list of # {destination gateway {metric}} pairs. # INPUTS # * node_id -- node id # RESULT # * routes -- the list of all static routes defined for the specified node #**** proc getStatIPv6routes { node } { global $node set routes {} set netconf [lindex [lsearch -inline [set $node] "network-config *"] 1] foreach entry [lsearch -all -inline $netconf "ipv6 route *"] { lappend routes [lrange $entry 2 end] } return $routes } #****f* nodecfg.tcl/setStatIPv6routes # NAME # setStatIPv4routes -- set static IPv6 routes. # SYNOPSIS # setStatIPv6routes $node_id $routes # FUNCTION # Replace all current static route entries with a new one, in form of # a list of {destination gateway {metric}} pairs. # INPUTS # * node_id -- node id # * routes -- the list of all static routes defined for the specified node #**** proc setStatIPv6routes { node routes } { netconfClearSection $node "ipv6 route [lindex [getStatIPv6routes $node] 0]" set section {} foreach route $routes { lappend section "ipv6 route $route" } netconfInsertSection $node $section } #****f* nodecfg.tcl/getNodeName # NAME # getNodeName -- get node name. # SYNOPSIS # set name [getNodeName $node_id] # FUNCTION # Returns node's logical name. # INPUTS # * node_id -- node id # RESULT # * name -- logical name of the node #**** proc getNodeName { node } { global $node set netconf [lindex [lsearch -inline [set $node] "network-config *"] 1] return [lrange [lsearch -inline $netconf "hostname *"] 1 end] } #****f* nodecfg.tcl/setNodeName # NAME # setNodeName -- set node name. # SYNOPSIS # setNodeName $node_id $name # FUNCTION # Sets node's logical name. # INPUTS # * node_id -- node id # * name -- logical name of the node #**** proc setNodeName { node name } { netconfClearSection $node "hostname [getNodeName $node]" netconfInsertSection $node [list "hostname $name"] } #****f* nodecfg.tcl/getNodeType # NAME # getNodeType -- get node type. # SYNOPSIS # set type [getNodeType $node_id] # FUNCTION # Returns node's type. # INPUTS # * node_id -- node id # RESULT # * type -- type of the node #**** proc nodeType { node } { global $node return [lindex [lsearch -inline [set $node] "type *"] 1] } #****f* nodecfg.tcl/getNodeModel # NAME # getNodeModel -- get node routing model. # SYNOPSIS # set model [getNodeModel $node_id] # FUNCTION # # INPUTS # * node_id -- node id # RESULT # * model -- routing model of the specified node #**** proc getNodeModel { node } { global $node return [lindex [lsearch -inline [set $node] "model *"] 1] } #****f* nodecfg.tcl/setNodeModel # NAME # setNodeModel -- set node routing model. # SYNOPSIS # setNodeModel $node_id $model # FUNCTION # # INPUTS # * node_id -- node id # * model -- routing model of the specified node #**** proc setNodeModel { node model } { global $node set i [lsearch [set $node] "model *"] if { $i >= 0 } { set $node [lreplace [set $node] $i $i "model $model"] } else { set $node [linsert [set $node] 1 "model $model"] } } #****f* nodecfg.tcl/getNodeCoords # NAME # getNodeCoords -- get node icon coordinates. # SYNOPSIS # set coords [getNodeCoords $node_id] # FUNCTION # Returns node's icon coordinates. # INPUTS # * node_id -- node id # RESULT # * coords -- coordinates of the node's icon a list in form of {Xcoord Ycoord} #**** proc getNodeCoords { node } { global $node return [lindex [lsearch -inline [set $node] "iconcoords *"] 1] } #****f* nodecfg.tcl/setNodeCoords # NAME # setNodeCoords -- set node's icon coordinates. # SYNOPSIS # setNodeCoords $node_id $coords # FUNCTION # Sets node's icon coordinates. # INPUTS # * node_id -- node id # * coords -- coordinates of the node's icon in form of Xcoord Ycoord #**** proc setNodeCoords { node coords } { global $node set i [lsearch [set $node] "iconcoords *"] if { $i >= 0 } { set $node [lreplace [set $node] $i $i "iconcoords {$coords}"] } else { set $node [linsert [set $node] end "iconcoords {$coords}"] } writeNodeCoords $node $coords } # this saves each node's current X,Y position to a /tmp/pycore.nnnnn/nX.xy proc writeNodeCoords { node coords } { global oper_mode g_current_session if { [info exists oper_mode] && $oper_mode != "exec" } { return } if { $g_current_session == 0 } { return } if { [nodeType $node] != "router" } { return } set fn "/tmp/pycore.$g_current_session/$node.xy" catch { set f [open $fn w] puts $f $coords close $f } } # cleanup the /tmp/pycore.nnnnn/nX.xy file proc deleteNodeCoords { node } { global g_current_session if { $g_current_session == 0 } { return } if { [nodeType $node] != "router" } { return } set fn "/tmp/pycore.$g_current_session/$node.xy" if { [file exists $fn] } { file delete $fn } } #****f* nodecfg.tcl/getNodeLabelCoords # NAME # getNodeLabelCoords -- get node's label coordinates. # SYNOPSIS # set coords [getNodeLabelCoords $node_id] # FUNCTION # Returns node's label coordinates. # INPUTS # * node_id -- node id # RESULT # * coords -- coordinates of the node's label a list in form of {Xcoord Ycoord} #**** proc getNodeLabelCoords { node } { global $node return [lindex [lsearch -inline [set $node] "labelcoords *"] 1] } #****f* nodecfg.tcl/setNodeLabelCoords # NAME # setNodeLabelCoords -- set node's label coordinates. # SYNOPSIS # setNodeLabelCoords $node_id $coords # FUNCTION # Sets node's label coordinates. # INPUTS # * node_id -- node id # * coords -- coordinates of the node's label in form of Xcoord Ycoord #**** proc setNodeLabelCoords { node coords } { global $node set i [lsearch [set $node] "labelcoords *"] if { $i >= 0 } { set $node [lreplace [set $node] $i $i "labelcoords {$coords}"] } else { set $node [linsert [set $node] end "labelcoords {$coords}"] } } # return [dx, dy] offset for a node label, which may depend on its type proc getDefaultLabelOffsets { nodetype } { set dx 0 set dy 32 if { [lsearch {hub lanswitch} $nodetype] >= 0 } { set dy 24 } return [list $dx $dy] } #****f* nodecfg.tcl/ifcList # NAME # ifcList -- get list of all interfaces # SYNOPSIS # set ifcs [ifcList $node_id] # FUNCTION # Returns a list of all interfaces present in a node. # INPUTS # * node_id -- node id # RESULT # * ifcs -- list of all node's interfaces. #**** proc ifcList { node } { global $node if { ![info exists $node] } { return "" } set interfaces "" foreach entry [lsearch -all -inline [set $node] "interface-peer *"] { lappend interfaces [lindex [lindex $entry 1] 0] } return $interfaces } #****f* nodecfg.tcl/peerByIfc # NAME # peerByIfc -- get node's peer by interface. # SYNOPSIS # set peer [peerByIfc $node_id $ifc] # FUNCTION # Returns id of the node on the other side of the interface. If the node # on the other side of the interface is situated on the other canvas or connected # via split link, this function returns a pseudo node. # INPUTS # * node_id -- node id # * ifc -- interface name # RESULT # * peer -- node id of the node on the other side of the interface #**** proc peerByIfc { node ifc } { global $node set entry [lsearch -inline [set $node] "interface-peer {$ifc *}"] return [lindex [lindex $entry 1] 1] } #****f* nodecfg.tcl/logicalPeerByIfc # NAME # logicalPeerByIfc -- get node's peer by interface. # SYNOPSIS # set peer [logicalPeerByIfc $node_id $ifc] # FUNCTION # Returns id of the node on the other side of the interface. If the node on the other # side of the interface is connected via normal link (not split) this function acts the same # as the function peerByIfc, but if the nodes are connected via split links or situated on different # canvases this function returns the logical peer node. # INPUTS # * node_id -- node id # * ifc -- interface name # RESULT # * peer -- node id of the node on the other side of the interface #**** proc logicalPeerByIfc { node ifc } { global $node set peer [peerByIfc $node $ifc] if { $peer == "" } { return "" }; # Boeing if { [nodeType $peer] != "pseudo" } { return $peer } else { set mirror_node [getNodeMirror $peer] set mirror_ifc [ifcList $mirror_node] return [peerByIfc $mirror_node $mirror_ifc] } } #****f* nodecfg.tcl/ifcByPeer # NAME # ifcByPeer -- get node interface by peer. # SYNOPSIS # set ifc [peerByIfc $node_id $peer_id] # FUNCTION # Returns the name of the interface connected to the specified peer. # If the peer node is on different canvas or connected via split link # to the specified node this function returns an empty string. # INPUTS # * node_id -- node id # * peer_id -- id of the peer node # RESULT # * ifc -- interface name #**** proc ifcByPeer { node peer } { global $node set entry [lsearch -inline [set $node] "interface-peer {* $peer}"] return [lindex [lindex $entry 1] 0] } #****f* nodecfg.tcl/ifcByLogicalPeer # NAME # ifcByPeer -- get node interface by peer. # SYNOPSIS # set ifc [peerByIfc $node_id $peer_id] # FUNCTION # Returns the name of the interface connected to the specified peer. # Returns the right interface even if the peer node is on the other # canvas or connected via split link. # INPUTS # * node_id -- node id # * peer_id -- id of the peer node # RESULT # * ifc -- interface name #**** proc ifcByLogicalPeer { node peer } { global $node set ifc [ifcByPeer $node $peer] if { $ifc == "" } { # # Must search through pseudo peers # foreach ifc [ifcList $node] { set t_peer [peerByIfc $node $ifc] if { [nodeType $t_peer] == "pseudo" } { set mirror [getNodeMirror $t_peer] if { [peerByIfc $mirror [ifcList $mirror]] == $peer } { return $ifc } } } return "" } else { return $ifc } } #****f* nodecfg.tcl/hasIPv4Addr # NAME # hasIPv4Addr -- has IPv4 address. # SYNOPSIS # set check [hasIPv4Addr $node_id] # FUNCTION # Returns true if at least one interface has an IPv4 address # configured, otherwise returns false. # INPUTS # * node_id -- node id # RESULT # * check -- true if at least one interface has IPv4 address, otherwise false. #**** proc hasIPv4Addr { node } { foreach ifc [ifcList $node] { if { [getIfcIPv4addr $node $ifc] != "" } { return true } } return false } #****f* nodecfg.tcl/hasIPv6Addr # NAME # hasIPv4Addr -- has IPv6 address. # SYNOPSIS # set check [hasIPv6Addr $node_id] # FUNCTION # Retruns true if at least one interface has an IPv6 address # configured, otherwise returns false. # INPUTS # * node_id -- node id # RESULT # * check -- true if at least one interface has IPv6 address, otherwise false. #**** proc hasIPv6Addr { node } { foreach ifc [ifcList $node] { if { [getIfcIPv6addr $node $ifc] != "" } { return true } } return false } #****f* nodecfg.tcl/removeNode # NAME # removeNode -- removes the node # SYNOPSIS # removeNode $node_id # FUNCTION # Removes the specified node as well as all the links binding that node to # the other nodes. # INPUTS # * node_id -- node id #**** proc removeNode { node } { global node_list $node foreach ifc [ifcList $node] { set peer [peerByIfc $node $ifc] set link [linkByPeers $node $peer] removeLink $link } set i [lsearch -exact $node_list $node] set node_list [lreplace $node_list $i $i] } #****f* nodecfg.tcl/getNodeCanvas # NAME # getNodeCanvas -- get node canvas id # SYNOPSIS # set canvas [getNodeCanvas $node_id] # FUNCTION # Returns node's canvas affinity. # INPUTS # * node_id -- node id # RESULT # * canvas -- canvas id #**** proc getNodeCanvas { node } { global $node return [lindex [lsearch -inline [set $node] "canvas *"] 1] } #****f* nodecfg.tcl/setNodeCanvas # NAME # setNodeCanvas -- set node canvas # SYNOPSIS # setNodeCanvas $node_id $canvas # FUNCTION # Sets node's canvas affinity. # INPUTS # * node_id -- node id # * canvas -- canvas id #**** proc setNodeCanvas { node canvas } { global $node set i [lsearch [set $node] "canvas *"] if { $i >= 0 } { set $node [lreplace [set $node] $i $i "canvas $canvas"] } else { set $node [linsert [set $node] end "canvas $canvas"] } } proc getNodeHidden { node } { global $node set h [lindex [lsearch -inline [set $node] "hidden *"] 1] if { $h == "" } { return 0 } return $h } proc setNodeHidden { node value } { global $node set i [lsearch [set $node] "hidden *"] if { $value == 0 } { if { $i >= 0 } { set $node [lreplace [set $node] $i $i] } return } if { $i >= 0 } { set $node [lreplace [set $node] $i $i "hidden $value"] } else { set $node [linsert [set $node] end "hidden $value"] } } #****f* nodecfg.tcl/newIfc # NAME # newIfc -- new interface # SYNOPSIS # set ifc [newIfc $type $node_id] # FUNCTION # Returns the first available name for a new interface of the specified type. # INPUTS # * node_id -- node id # * type -- type # RESULT # * ifc -- the first available name for a interface of the specified type #**** proc newIfc { type node } { set interfaces [ifcList $node] set firstinterface 0 for { set id $firstinterface } { [lsearch -exact $interfaces $type$id] >= 0 } {incr id} {} return $type$id } #****f* nodecfg.tcl/newNode # NAME # newNode -- new node # SYNOPSIS # set node_id [newNode $type] # FUNCTION # Returns the node id of a new node of the specified type. # INPUTS # * type -- node type # RESULT # * node_id -- node id of a new node of the specified type #**** proc newNode { type } { global node_list def_router_model global viewid catch {unset viewid} # overload the passed in parameter - allow specifing new node num if { [llength $type] > 1 } { set node [lindex $type 1] set type [lindex $type 0] } else { set node [newObjectId node] } global $node set $node {} lappend $node "type $type" if { $type == "router" } { lappend $node "model $def_router_model" set nconfig [list \ "hostname $node" \ ! ] # tunnel } elseif {$type == "rj45" || $type == "tunnel" || $type == "ktunnel" } { set nconfig [list \ "hostname UNASSIGNED" \ ! ] # set wlan default parameters upon creation } elseif { $type == "wlan" } { global DEFAULT_WLAN_MODEL DEFAULT_WLAN_MODEL_TYPES global DEFAULT_WLAN_MODEL_VALS set nconfig [list \ "hostname $type[string range $node 1 end]" \ ! \ "mobmodel" \ "coreapi" \ "$DEFAULT_WLAN_MODEL" \ ! ] lappend $node "network-config [list $nconfig]" setCustomConfig $node $DEFAULT_WLAN_MODEL $DEFAULT_WLAN_MODEL_TYPES \ $DEFAULT_WLAN_MODEL_VALS 0 } else { set nconfig [list \ "hostname $node" \ ! ] } # wlan has already changed node global above if { $type != "wlan" } { lappend $node "network-config [list $nconfig]" } lappend node_list $node return $node } #****f* nodecfg.tcl/getNodeMirror # NAME # getNodeMirror -- get node mirror # SYNOPSIS # set mirror_node_id [getNodeMirror $node_id] # FUNCTION # Returns the node id of a mirror pseudo node of the node. Mirror node is the # corresponding pseudo node. The pair of pseudo nodes, node and his mirror node, are # introduced to form a split in a link. This split can be used for avoiding crossed # links or for displaying a link between the nodes on a different canvas. # INPUTS # * node_id -- node id # RESULT # * mirror_node_id -- node id of a mirror node #**** proc getNodeMirror { node } { global $node return [lindex [lsearch -inline [set $node] "mirror *"] 1] } #****f* nodecfg.tcl/setNodeMirror # NAME # setNodeMirror -- set node mirror # SYNOPSIS # setNodeMirror $node_id $mirror_node_id # FUNCTION # Sets the node id of a mirror pseudo node of the specified node. Mirror node is the # corresponding pseudo node. The pair of pseudo nodes, node and his mirror node, are # introduced to form a split in a link. This split can be used for avoiding crossed # links or for displaying a link between the nodes on a different canvas. # INPUTS # * node_id -- node id # * mirror_node_id -- node id of a mirror node #**** proc setNodeMirror { node value } { global $node set i [lsearch [set $node] "mirror *"] if { $value == "" } { set $node [lreplace [set $node] $i $i] } else { set $node [linsert [set $node] end "mirror $value"] } } #****f* nodecfg.tcl/setType # NAME # setType -- set node's type. # SYNOPSIS # setType $node_id $type # FUNCTION # Sets node's type. # INPUTS # * node_id -- node id # * type -- type of node #**** proc setType { node type } { global $node set i [lsearch [set $node] "type *"] if { $i >= 0 } { set $node [lreplace [set $node] $i $i "type $type"] } else { set $node [linsert [set $node] 1 "type $type"] } } # begin Boeing: node commands specific to wlan proc getNodeRange { node } { global $node return [lindex [lsearch -inline [set $node] "range *"] 1] } proc setNodeRange { node value } { global $node set i [lsearch [set $node] "range *"] if { $value == "" } { if { $i > 0 } { set $node [lreplace [set $node] $i $i] } return } if { $i > 0 } { set $node [lreplace [set $node] $i $i "range $value"] } else { set $node [linsert [set $node] end "range $value"] } return } # end Boeing # Boeing - custom post config commands proc getCustomPostConfigCommands { node } { global $node return [lindex [lsearch -inline [set $node] "custom-post-config-commands *"] 1] } #Boeing custom pre config commands proc getCustomPreConfigCommands { node } { global $node return [lindex [lsearch -inline [set $node] "custom-pre-config-commands *"] 1] } #Boeing custom post config commands proc setCustomPostConfigCommands { node cfg } { global $node set i [lsearch [set $node] "custom-post-config-commands *"] if { $i >= 0 } { set $node [lreplace [set $node] $i $i] } if { $cfg != {} } { lappend $node [list custom-post-config-commands $cfg] } return } # get the services saved for this node; if want_defaults is true and no services # have been configured, return the default services for this node type proc getNodeServices { node want_defaults } { global $node set i [lsearch [set $node] "services *"] set s [lindex [lindex [set $node] $i] 1] if { $want_defaults && $i < 0 } { set s [getNodeTypeServices [getNodeModel $node]] } return $s } # save the list of services configured for this node proc setNodeServices { node services } { global $node set i [lsearch [set $node] "services *"] if { $i >= 0 } { set $node [lreplace [set $node] $i $i "services {$services}"] } else { set $node [linsert [set $node] end "services {$services}"] } } # Boeing - custom image proc getCustomImage { node } { global $node return [lindex [lsearch -inline [set $node] "custom-image *"] 1] } # Boeing - custom image proc setCustomImage { node image } { global $node set i [lsearch [set $node] "custom-image *"] if { $i >= 0 } { set $node [lreplace [set $node] $i $i] } if { $image != "" } { lappend $node [list custom-image $image] } return } # if cmd=save save all node positions, otherwise reset them with cmd=reset proc resetAllNodeCoords { cmd } { global node_list g_saved_node_coords zoom # save the node coordinates to a global array if { $cmd == "save" } { array unset g_saved_node_coords foreach node $node_list { set coords [getNodeCoords $node] if { $coords == "" } { continue } array set g_saved_node_coords [list $node $coords] } # restore the node coordinates from the global array } elseif { $cmd == "reset" } { if { ![array exists g_saved_node_coords] } { return } foreach node $node_list { if { ![info exists g_saved_node_coords($node)] } { continue } set coords $g_saved_node_coords($node) if { [llength $coords] != 2 } { continue } set x [expr {$zoom * [lindex $coords 0]}] set y [expr {$zoom * [lindex $coords 1]}] moveNodeAbs .c $node $x $y } } }