core-extra/gui/nodecfg.tcl

2018 lines
54 KiB
Tcl

#
# 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
}
}
}