From patchwork Mon Jun 20 02:35:50 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "Huang, ChenyuX" X-Patchwork-Id: 113081 Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 470FEA0093; Mon, 20 Jun 2022 04:36:07 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 010D84067C; Mon, 20 Jun 2022 04:36:07 +0200 (CEST) Received: from mga05.intel.com (mga05.intel.com [192.55.52.43]) by mails.dpdk.org (Postfix) with ESMTP id 505C840150 for ; Mon, 20 Jun 2022 04:36:03 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1655692564; x=1687228564; h=from:to:cc:subject:date:message-id:mime-version: content-transfer-encoding; bh=VAjWf1zNdMyGmDqtajEzvBk9hlybSb78Yfp06eojlpk=; b=YhJHI4aUiBNoKQc0LKtUTKBxDpYPBQkl0u7U4MLNHWvm4WiKfC7kz5g5 appthFN59Fo4g2F67usTZNkP2P+bgTtNIPQA0SrXBr+Q6drdOk3LV8H6W YcRv7Xkix0Txk7gWrmanYoOSmtN339uor06k5s4SJq6+jGOMGPWcqFO9N i+20ocgiOUwOmyps07zJK10O4iHX74jC3UPzVXh+NP3490DqhYUMe1Nd3 Z2RcyFMOafbfDXJYlxCKqxx6if97/J0Vfa+VrxlTlVwWVjWIU42x9wSzO CofvNtYPG0Oiooq3BQYMKIwjULfwymS7nBbl2dgXJvgAL81/kyAvEk3Ev Q==; X-IronPort-AV: E=McAfee;i="6400,9594,10380"; a="366111283" X-IronPort-AV: E=Sophos;i="5.92,306,1650956400"; d="scan'208";a="366111283" Received: from fmsmga007.fm.intel.com ([10.253.24.52]) by fmsmga105.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 19 Jun 2022 19:36:00 -0700 X-IronPort-AV: E=Sophos;i="5.92,306,1650956400"; d="scan'208";a="590958420" Received: from unknown (HELO localhost.localdomain) ([10.239.252.107]) by fmsmga007-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 19 Jun 2022 19:35:54 -0700 From: ChenYu Huang To: dts@dpdk.org Cc: lijuan.tu@intel.com, qingx.sun@intel.com, junx.dong@intel.com, daxuex.gao@intel.com Subject: [PATCH] unpack tclclient.tgz and tgen.tgz Date: Mon, 20 Jun 2022 10:35:50 +0800 Message-Id: <20220620023550.64779-1-chenyux.huang@intel.com> X-Mailer: git-send-email 2.25.1 MIME-Version: 1.0 X-BeenThere: dts@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: test suite reviews and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dts-bounces@dpdk.org From: Daxue Gao Extract ixia dependances for better review and understanding in repo. Signed-off-by: Daxue Gao --- dep/tclclient.tgz | Bin 199327 -> 0 bytes dep/tclclient/IxiaWish.tcl | 133 + .../ixTcl1.0/Dap/dapconfiguration.tcl | 217 ++ dep/tclclient/ixTcl1.0/Dap/ixDapConfig.tcl | 1702 +++++++++ dep/tclclient/ixTcl1.0/Generic/actions.tcl | 3241 +++++++++++++++++ .../ixTcl1.0/Generic/addressTableUtils.tcl | 117 + dep/tclclient/ixTcl1.0/Generic/calculate.tcl | 527 +++ .../ixTcl1.0/Generic/chassisUtils.tcl | 239 ++ .../ixTcl1.0/Generic/clientUtils.tcl | 319 ++ dep/tclclient/ixTcl1.0/Generic/constants.tcl | 131 + .../ixTcl1.0/Generic/conversions.tcl | 275 ++ .../ixTcl1.0/Generic/dataValidation.tcl | 1855 ++++++++++ .../ixTcl1.0/Generic/defineCommand.tcl | 441 +++ dep/tclclient/ixTcl1.0/Generic/defineTest.tcl | 161 + .../ixTcl1.0/Generic/dhcpExchange.tcl | 2605 +++++++++++++ .../ixTcl1.0/Generic/dialogUtils.tcl | 230 ++ .../ixTcl1.0/Generic/eventHandler.tcl | 124 + .../ixTcl1.0/Generic/featureUtils.tcl | 324 ++ .../ixTcl1.0/Generic/fileTransferClient.tcl | 538 +++ dep/tclclient/ixTcl1.0/Generic/ftp.tcl | 154 + .../ixTcl1.0/Generic/highLevelAPI.tcl | 2642 ++++++++++++++ dep/tclclient/ixTcl1.0/Generic/interface.tcl | 262 ++ .../ixTcl1.0/Generic/interfaceTableUtils.tcl | 850 +++++ dep/tclclient/ixTcl1.0/Generic/ipV6Utils.tcl | 1479 ++++++++ .../ixTcl1.0/Generic/ixFileUtils.tcl | 158 + dep/tclclient/ixTcl1.0/Generic/ixGraph.tcl | 982 +++++ dep/tclclient/ixTcl1.0/Generic/log.tcl | 177 + .../ixTcl1.0/Generic/logCollector.tcl | 162 + dep/tclclient/ixTcl1.0/Generic/map.tcl | 383 ++ dep/tclclient/ixTcl1.0/Generic/miiUtils.tcl | 619 ++++ dep/tclclient/ixTcl1.0/Generic/miscCmds.tcl | 992 +++++ dep/tclclient/ixTcl1.0/Generic/multiUser.tcl | 402 ++ .../ixTcl1.0/Generic/packetBuilder.tcl | 922 +++++ dep/tclclient/ixTcl1.0/Generic/parseArgs.tcl | 87 + dep/tclclient/ixTcl1.0/Generic/platform.tcl | 88 + dep/tclclient/ixTcl1.0/Generic/random.tcl | 109 + .../ixTcl1.0/Generic/scriptmateBackCompat.tcl | 65 + dep/tclclient/ixTcl1.0/Generic/socket.tcl | 718 ++++ dep/tclclient/ixTcl1.0/Generic/statistics.tcl | 1222 +++++++ .../ixTcl1.0/Generic/stringUtils.tcl | 632 ++++ .../ixTcl1.0/Generic/switchLearn.tcl | 2030 +++++++++++ dep/tclclient/ixTcl1.0/Generic/tclServer.tcl | 458 +++ .../ixTcl1.0/Generic/testCmdControl.tcl | 538 +++ dep/tclclient/ixTcl1.0/Generic/trafficMix.tcl | 329 ++ .../ixTcl1.0/Generic/utilWrappers.tcl | 357 ++ dep/tclclient/ixTcl1.0/Generic/utils.tcl | 2319 ++++++++++++ dep/tclclient/ixTcl1.0/Generic/vlanUtils.tcl | 327 ++ dep/tclclient/ixTcl1.0/Generic/xmlUtils.tcl | 241 ++ dep/tclclient/ixTcl1.0/ixInit.tcl | 65 + dep/tclclient/ixTcl1.0/ixTclHal.tcl | 192 + dep/tclclient/ixTcl1.0/ixTclHalSetup.tcl | 400 ++ dep/tclclient/ixTcl1.0/ixTclSetup.tcl | 244 ++ dep/tclclient/ixTcl1.0/ixiaDCB.tcl | 588 +++ dep/tclclient/ixTcl1.0/ixiaPing6.tcl | 140 + dep/tclclient/ixTcl1.0/ixiarc.tcl | 60 + dep/tclclient/ixTcl1.0/pkgIndex.tcl | 54 + dep/tclclient/ixTcl1.0/tclIndex | 836 +++++ dep/tgen.tgz | Bin 134392 -> 0 bytes dep/tgen/Pktgen.lua | 276 ++ dep/tgen/PktgenGUI.lua | 113 + dep/tgen/tgen | Bin 0 -> 300314 bytes framework/project_dpdk.py | 3 + 62 files changed, 35854 insertions(+) delete mode 100644 dep/tclclient.tgz create mode 100644 dep/tclclient/IxiaWish.tcl create mode 100644 dep/tclclient/ixTcl1.0/Dap/dapconfiguration.tcl create mode 100644 dep/tclclient/ixTcl1.0/Dap/ixDapConfig.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/actions.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/addressTableUtils.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/calculate.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/chassisUtils.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/clientUtils.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/constants.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/conversions.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/dataValidation.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/defineCommand.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/defineTest.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/dhcpExchange.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/dialogUtils.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/eventHandler.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/featureUtils.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/fileTransferClient.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/ftp.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/highLevelAPI.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/interface.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/interfaceTableUtils.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/ipV6Utils.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/ixFileUtils.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/ixGraph.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/log.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/logCollector.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/map.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/miiUtils.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/miscCmds.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/multiUser.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/packetBuilder.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/parseArgs.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/platform.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/random.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/scriptmateBackCompat.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/socket.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/statistics.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/stringUtils.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/switchLearn.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/tclServer.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/testCmdControl.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/trafficMix.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/utilWrappers.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/utils.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/vlanUtils.tcl create mode 100644 dep/tclclient/ixTcl1.0/Generic/xmlUtils.tcl create mode 100644 dep/tclclient/ixTcl1.0/ixInit.tcl create mode 100644 dep/tclclient/ixTcl1.0/ixTclHal.tcl create mode 100644 dep/tclclient/ixTcl1.0/ixTclHalSetup.tcl create mode 100644 dep/tclclient/ixTcl1.0/ixTclSetup.tcl create mode 100644 dep/tclclient/ixTcl1.0/ixiaDCB.tcl create mode 100644 dep/tclclient/ixTcl1.0/ixiaPing6.tcl create mode 100644 dep/tclclient/ixTcl1.0/ixiarc.tcl create mode 100644 dep/tclclient/ixTcl1.0/pkgIndex.tcl create mode 100644 dep/tclclient/ixTcl1.0/tclIndex delete mode 100644 dep/tgen.tgz create mode 100644 dep/tgen/Pktgen.lua create mode 100644 dep/tgen/PktgenGUI.lua create mode 100755 dep/tgen/tgen diff --git a/dep/tclclient.tgz b/dep/tclclient.tgz deleted file mode 100644 index 96038ffcbf3bef07fab73fc6348c5b6caa73d28a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199327 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

7Mr6)yF|V6Z2jElF}%UPcegq|zV* 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 diff --git a/dep/tclclient/IxiaWish.tcl b/dep/tclclient/IxiaWish.tcl new file mode 100644 index 00000000..9c70420c --- /dev/null +++ b/dep/tclclient/IxiaWish.tcl @@ -0,0 +1,133 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. + +# IxiaWish.tcl sets up the Tcl environment to use the correct multiversion-compatible +# applications, as specified by Application Selector. + +# Note: this file must remain compatible with tcl 8.3, because it will be sourced by scriptgen +namespace eval ::ixiaInstall:: { + # For debugging, you can point this to an alternate location. It should + # point to a directory that contains the "TclScripts" subdirectory. + set tclDir "/root" + + # For debugging, you can point this to an alternate location. It should + # point to a directory that contains the IxTclHal.dll + set ixosTclDir "/root" + + set tclLegacyDir [file join $tclDir "../.."] + + # Calls appinfo to add paths to IxOS dependencies (such as IxLoad or IxNetwork). + proc ixAddPathsFromAppinfo {installdir} { + package require registry + + set installInfoFound false + foreach {reg_path} [list "HKEY_LOCAL_MACHINE\\SOFTWARE\\Ixia Communications\\AppInfo\\InstallInfo" "HKEY_CURRENT_USER\\SOFTWARE\\Ixia Communications\\AppInfo\\InstallInfo"] { + if { [ catch {registry get $reg_path "HOMEDIR"} r] == 0 } { + set appinfo_path $r + set installInfoFound true + break + } + } + # If the registy information was not found in either place, warn the user + if { [string equal $installInfoFound "false"] } { + return -code error "Could not find AppInfo registry entry" + } + + # Call appinfo to get the list of all dependencies: + regsub -all "\\\\" $appinfo_path "/" appinfo_path + set appinfo_executable [file attributes "$appinfo_path/Appinfo.exe" -shortname] + set appinfo_command "|$appinfo_executable --app-path \"$installdir\" --get-dependencies" + set appinfo_handle [open $appinfo_command r+ ] + set appinfo_session {} + + while { [gets $appinfo_handle line] >= 0 } { + # Keep track of the output to report in the error message below + set appinfo_session "$appinfo_session $line\n" + + regsub -all "\\\\" $line "/" line + regexp "^(.+):\ (.*)$" $line all app_name app_path + # If there is a dependency listed, add the path. + if {![string equal $app_path ""] } { + # Only add it if it's not already present: + if { -1 == [lsearch -exact $::auto_path $app_path ] } { + lappend ::auto_path $app_path + lappend ::auto_path [file join $app_path "TclScripts/lib"] + append ::env(PATH) [format ":%s" $app_path] + } + } + } + + # If appinfo returned a non-zero result, this catch block will trigger. + # In that case, show what we tried to do, and the resulting response. + if { [catch {close $appinfo_handle} r] != 0} { + return -code error "Appinfo error, \"$appinfo_command\" returned: $appinfo_session" + } + } + + # Adds all needed Ixia paths + proc ixAddPaths {installdir} { + set ::env(IXTCLHAL_LIBRARY) [file join $installdir "ixTcl1.0"] + set ::env(IXIA_VERSION) "6.20" + lappend ::auto_path $installdir + #lappend ::auto_path [file join $installdir "TclScripts/lib"] + lappend ::auto_path [file join $installdir "ixTcl1.0"] + #if { [catch {::ixiaInstall::ixAddPathsFromAppinfo $installdir} result] } { + # Not necessarily fatal + # puts [format "WARNING!!! Unable to add paths from Appinfo: %s" $result] + #} + append ::env(PATH) ":${installdir}" + + # Fall back to the old locations, in case a non-multiversion-aware + # IxLoad or IxNetwork is installed. + #lappend ::auto_path [file join $installdir "../../TclScripts/lib"] + #append ::env(PATH) [format ";%s" [file join $installdir "../.."]] + + # if {![string equal $::ixiaInstall::tclDir $::ixiaInstall::ixosTclDir]} { + # append ::env(PATH) [format ";%s" $::ixiaInstall::ixosTclDir] + # } + } +} +::ixiaInstall::ixAddPaths $::ixiaInstall::tclDir + +catch { + # Try to set things up for Wish. + # This section will not run in IxExplorer or IxTclInterpreter, hence the catch block. + if {[lsearch [package names] "Tk"] >= 0} { + console show + wm iconbitmap . [file join $::ixiaInstall::tclDir "ixos.ico"] + + # It is not easy to tell ActiveState wish from the Ixia-compiled wish. + # The tcl_platform variable shows one difference: ActiveState implements threading + if {![info exists ::tcl_platform(threaded)]} { + # Activestate prints this on its own, otherwise, we add it here: + puts -nonewline "(TclScripts) 1 % " + } + } +} diff --git a/dep/tclclient/ixTcl1.0/Dap/dapconfiguration.tcl b/dep/tclclient/ixTcl1.0/Dap/dapconfiguration.tcl new file mode 100644 index 00000000..3122079b --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Dap/dapconfiguration.tcl @@ -0,0 +1,217 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 5.20 +# +# File: dapconfiguration.tcl +# +# This file contains the configuration parameters for running ixDapConfig::main. +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 11-14-02 Debby Stopp +# +######################################################################################## + +source ixDapConfig.tcl +logOn ixDapConfig.log + +## +# Application name +# Need to set the exact name of the application to be downloaded to each port +## +set applicationName "" + + +## +# Startup procedure/executable +# If there's a startup procedure that needs to be excuted after downloading, put the name +# of that executable here. The entire patch to the executable must be specified in the startupProcedureExe. +# No action taken if variable is set to "" +## +set startupProcedureExe "" + + +## +# Specify login name to use for taking ownership of ports. +# Ownership is *mandatory* for this application. +## +ixDapLogin "ixDapConfig" + + +## +# Chassis list +# Put chassis ip OR DNS host name; put in order of usage. +# First chassis == ID 1, second chassis == ID 2, etc... +## +ixDapConfig::setChassisList {loopback} + + +## +# Port array & ip stuff - Configure port interface details +# +# NOTE: +# portID == { } +## +ixDapAddPortInterface -portId {1 1 1} \ + -macAddress "00 de bb 01 01 01" \ + -ipAddress "198.18.1.100" \ + -gateway "198.18.1.1" \ + -mask "255.255.255.0" \ + -enableVlan false \ + -vlanId 101 + +ixDapAddPortInterface -portId {1 1 1} \ + -macAddress "00 de bb 01 02 01" \ + -ipAddress "198.18.2.100" \ + -gateway "198.18.2.1" \ + -mask "255.255.255.0" \ + -enableVlan false \ + -vlanId 101 + +ixDapAddPortInterface -portId {1 1 1} \ + -macAddress "00 de bb 01 03 01" \ + -ipAddress "198.18.3.100" \ + -gateway "198.18.3.1" \ + -mask "255.255.255.0" \ + -enableVlan false \ + -vlanId 101 + +ixDapAddPortInterface -portId {1 1 2} \ + -macAddress "00 de bb 01 01 02" \ + -ipAddress "198.18.1.102" \ + -gateway "198.18.1.1" \ + -mask "255.255.255.0" \ + -enableVlan true \ + -vlanId 102 + +ixDapAddPortInterface -portId {1 1 3} \ + -macAddress "00 de bb 01 01 03" \ + -ipAddress "198.18.1.103" \ + -gateway "198.18.1.1" \ + -mask "255.255.255.0" \ + -enableVlan false \ + -vlanId 103 + +ixDapAddPortInterface -portId {1 1 4} \ + -macAddress "00 de bb 01 01 04" \ + -ipAddress "198.18.1.104" \ + -gateway "198.18.1.1" \ + -mask "255.255.255.0" \ + -enableVlan false \ + -vlanId 104 + + +## +# Config test network topology - Add route table details +# +## +ixDapAddRouteTable -portId {1 1 1} \ + -target "net" \ + -ipAddress "198.20.0.0" \ + -gateway "198.18.2.1" \ + -mask "255.255.0.0" + +ixDapAddRouteTable -portId {1 1 1} \ + -target "host" \ + -ipAddress "198.20.1.100" \ + -gateway "198.18.3.1" \ + -mask "255.255.255.255" + +## +# Config port filter - Add port filter details +# +## +ixDapAddPortFilter -reset true \ + -portList {{1 1 1} {1 1 2}} \ + -action "enable" \ + -type "ip-protocols" \ + -valueList {4 17} + +ixDapAddPortFilter -reset false \ + -portList {{1 1 1}} \ + -action "disable" \ + -type "udp-ports" \ + -valueList {22-23 25} + +ixDapAddPortFilter -reset false \ + -portList {{1 1 1}} \ + -action "enable" \ + -type "tcp-ports" \ + -valueList {22-23 25} + +## +# alternatively, set port list via loop: +## +#set firstChassisId 1 +#set lastChassisId 1 +#set firstCardId 3 +#set lastCardId 3 +#set firstPortId 5 +#set lastPortId 8 +#set ipAddress "198.18.1.100" +#set gateway "198.18.1.1" +#set mask "255.255.255.0" +#set incrSubnetByte 3 +# +#for {set chassis $firstChassisId} {$chassis <= $lastChassisId} {incr chassis} { +# for {set card $firstCardId} {$card <= $lastCardId} {incr card} { +# for {set port $firstPortId} {$port <= $lastPortId} {incr port} { +# set macAddress [format "00 de bb %02x %02x %02x" $chassis $card $port] +# ixDapConfigPort $chassis $card $port $macAddress $ipAddress $gateway $mask +# set ipAddress [incrIpField $ipAddress $incrSubnetByte] +# set gateway [incrIpField $gateway $incrSubnetByte] +# } +# } +#} + +######################################################################################### +# end of user configuration stuff +######################################################################################## + +######################################################################################## +# execute config/download app here +######################################################################################## +if {![ixDapConfig::main -appName $applicationName]} { + if {$startupProcedureExe != ""} { + if [catch {exec $startupProcedureExe &} error] { + logMsg $error + } + } + + logMsg "\n*** Setup now complete!!! ***" +} + + + + + diff --git a/dep/tclclient/ixTcl1.0/Dap/ixDapConfig.tcl b/dep/tclclient/ixTcl1.0/Dap/ixDapConfig.tcl new file mode 100644 index 00000000..7971be46 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Dap/ixDapConfig.tcl @@ -0,0 +1,1702 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 5.20 +# +# File: ixDapConfig.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 11-14-02 Debby Stopp +# +######################################################################################## + +package req IxTclServices + +namespace eval ixDapConfig { + variable chassisList + variable tclServer "" + variable userName "ixDapConfig" + + variable appName + variable defaultPortGroupId 1126 + + if [info exists interfaceArray] { + unset interfaceArray + } + variable interfaceArray + variable interfacePortIdIndex 0 + variable interfaceMacAddressIndex 1 + variable interfaceIpAddressIndex 2 + variable interfaceGwIndex 3 + variable interfaceMaskIndex 4 + variable interfaceEnableVlanIndex 5 + variable interfaceVlanIdIndex 6 + + if [info exists routeArray] { + unset routeArray + } + variable routeArray + variable routePortIdIndex 0 + variable routeTargetIndex 1 + variable routeIpAddressIndex 2 + variable routeGwIndex 3 + variable routeMaskIndex 4 + + if [info exists filterArray] { + unset filterArray + } + variable filterArray + variable filterResetIndex 0 + variable filterPortListIndex 1 + variable filterActionIndex 2 + variable filterTypeIndex 3 + variable filterValueListIndex 4 + + variable logCmd logMsg + variable errorCmd errorMsg +} + +proc ixDapConfigPort {chassis card port macAddress ipAddress gateway mask {enableVlan false} {vlanId 0}} \ +{ + set retCode [ixDapAddPortInterface -portId [list $chassis $card $port] -macAddress $macAddress \ + -ipAddress $ipAddress -gateway $gateway -mask $mask -enableVlan $enableVlan -vlanId $vlanId] + return $retCode +} + +proc ixDapAddPortInterface {args} \ +{ + set retCode [::ixDapConfig::addInterfaceToArray $args] + return $retCode +} + +proc ixDapAddRouteTable {args} \ +{ + set retCode [::ixDapConfig::addRouteToArray $args] + return $retCode +} + +proc ixDapAddPortFilter {args} \ +{ + set retCode [::ixDapConfig::addFilterToArray $args] + return $retCode +} + +proc ixDapBaseIpAddresses {args} \ +{ + set retCode $::TCL_ERROR + if {[ixConnectToChassis $args] == $::TCL_OK} { + set retCode [::ixDapConfig::validateBaseIpAddresses $args] + } + + return $retCode +} + +proc ixDapCleanUp {} {return [::ixDapConfig::cleanUp]} +proc ixDapSetBaseIp {chassisName ipAddress {mask 255.255.0.0}} {return [::ixDapConfig::setBaseIp $chassisName $ipAddress $mask]} +proc ixDapAddRoute {destination mask interface} {return [::ixDapConfig::addRoute $destination $mask $interface]} +proc ixDapDelRoute {destination} {return [::ixDapConfig::delRoute $destination]} + +proc ixDapLogin {userName} {return [::ixDapConfig::login $userName]} + +proc ixDapConfig::Log {msg} { + variable logCmd; eval [concat $logCmd [list $msg]] +} +proc ixDapConfig::Error {msg} { + variable errorCmd; eval [concat $errorCmd [list $msg]] +} + + +######################################################################## +# Procedure: init +# +# This initializes the ixDapConfig namespace +# +# Arguments(s): +# +######################################################################## +proc ixDapConfig::init {{appName ""}} \ +{ + set retCode $::TCL_OK + + if {$appName != ""} { + setAppName $appName + } else { + set retCode $::TCL_ERROR + Error "No application name configured - check \"applicationName\"" + } + + return $retCode +} + + +######################################################################## +# Procedure: login +# +# This procedure logins the current user name; login will be enforced +# +# Arguments(s): +# +######################################################################## +proc ixDapConfig::login {{newUserName "ixDapConfig"}} \ +{ + variable userName + + if {$newUserName != ""} { + set userName $newUserName + } + ixLogin $userName +} + + +######################################################################## +# Procedure: printVersion +# +# This prints version info +# +# Arguments(s): +# +######################################################################## +proc ixDapConfig::printVersion {} \ +{ + set versionString [format "\n\t%s\n\t%s\n\t%s\n\n" \ + [version cget -installVersion] \ + [version cget -productVersion] \ + [version cget -copyright] ] + Log $versionString +} + + +######################################################################## +# Procedure: viewRoutes +# +# This procedure prints the current route config "route print" +# +# Arguments(s): +# +######################################################################## +proc ixDapConfig::viewRoutes {baseIpAddressList} \ +{ + set routeWindow .routeWindow + + # we need to create a new one + if [winfo exists $routeWindow] { + catch {::destroy $routeWindow} + } + + toplevel $routeWindow -class Dialog + wm withdraw $routeWindow + wm title $routeWindow "Route View" + wm resizable $routeWindow 0 0 + + set routeText [exec route print] + set height [regsub -all "\n" $routeText "\n" temp] + set width [string length [lindex $routeText 0]] + + set routeTextBox [createViewFrame $baseIpAddressList .routeWindow $height $width] + + $routeTextBox insert end $routeText + + wm deiconify $routeWindow +} + + +######################################################################## +# Procedure: addRoute +# +# This procedure adds a route config "route add" +# +# Arguments(s): +# destination - destination ip address, ie., 10.0.0.0 +# mask - associated mask, ie., 255.255.0.0 +# interface - to route through +# +######################################################################## +proc ixDapConfig::addRoute {destination mask interface} \ +{ + set retCode $::TCL_OK + + catch {exec route delete $destination} + if [catch {exec route add -p $destination mask $mask $interface} error] { + Log $error + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: delRoute +# +# This procedure deletes a route config "route delete" +# +# Arguments(s): +# destination - destination ip address, ie., 10.0.0.0 +# +######################################################################## +proc ixDapConfig::delRoute {destination} \ +{ + set retCode $::TCL_OK + + catch {exec route delete $destination} + + return $retCode +} + + +################################################################################## +# Procedure: ixDapConfig::createViewFrame +# +# Description: Create frame for viewing stuff +# +# Arguments: none +# +# Returns: TCL_OK: success +# +################################################################################## +proc ixDapConfig::createViewFrame { baseIpAddressList parent {height 25} {width 80} {xpad 3} {ypad 2}} \ +{ + + option add *font ansi + incr width $xpad + incr height $ypad + + set width [expr ($width < 80) ? $width : 80] + set height [expr ($height < 50) ? $height : 50] + + set labelFrame [frame $parent.routeViewFrame] + set infoLabelFrame $labelFrame + + grid $labelFrame -row 0 -column 0 -padx 0 -pady 10 -sticky w + + set numBaseIp [llength $baseIpAddressList] + if {$numBaseIp <= 1} { + set text1 "The Ixia port's management address is" + set text2 "Please ensure that your PC has a route to this network." + } else { + set text1 "The Ixia ports' management addresses are" + set text2 "Please ensure that your PC has routes to these networks." + } + + set labelIndex 0 + set infoLabel$labelIndex \ + [label $labelFrame.infoLabel$labelIndex -text $text1 -font {ansi 8} -width 92] + grid [set infoLabel$labelIndex] -row $labelIndex -column 0 -padx 6 -pady 0 -sticky w + + foreach baseIp $baseIpAddressList { + incr labelIndex + set infoLabel$labelIndex \ + [label $labelFrame.infoLabel$labelIndex -text "\t - $baseIp/16" -font {ansi 8} -width 92] + grid [set infoLabel$labelIndex] -row $labelIndex -column 0 -padx 6 -pady 0 -sticky w + } + + incr labelIndex + set infoLabel$labelIndex \ + [label $labelFrame.infoLabel$labelIndex -text $text2 -font {ansi 8} -width 92] + grid [set infoLabel$labelIndex] -row $labelIndex -column 0 -padx 6 -pady 0 -sticky w + + set routeFrame [frame $parent.routeFrame] + + # Create a list widget to contain all the route + set widgets(routeText) [text $routeFrame.routeText \ + -height $height -width $width -wrap none -font {courier 8} -xscrollcommand "$routeFrame.xScroll set" \ + -yscrollcommand "$routeFrame.yScroll set"] + + set xScroll [scrollbar $routeFrame.xScroll -orient horizontal \ + -command "$widgets(routeText) xview"] + set yScroll [scrollbar $routeFrame.yScroll -orient vertical \ + -command "$widgets(routeText) yview"] + + grid $widgets(routeText) -row 1 -column 0 -sticky news + grid $xScroll -row 2 -column 0 -sticky we + grid $yScroll -row 1 -column 1 -sticky ns + grid rowconfigure $routeFrame 1 -weight 1 + grid columnconfigure $routeFrame 0 -weight 1 + + grid $infoLabelFrame -row 0 -column 0 -padx 16 -pady 10 -sticky w -columnspan 2 + grid $routeFrame -row 1 -column 0 -padx 20 -pady 10 -sticky wens + grid columnconfigure $parent 0 -weight 1 + grid rowconfigure $parent 1 -weight 1 + grid columnconfigure $parent 1 -weight 1 + + Log "\n*** Please check the Route View window and make sure a route exist for the following network address.\n" + Log " Network Address: \{$baseIpAddressList\} " + return $widgets(routeText) +} + + +######################################################################## +# Procedure: setAppName +# +# This sets the appName for the ixDapConfig namespace +# +# Arguments(s): +# +######################################################################## +proc ixDapConfig::setAppName {userAppName} \ +{ + variable appName + + set appName $userAppName +} + + +######################################################################## +# Procedure: setChassisList +# +# This sets the chassisList for the ixDapConfig namespace +# +# Arguments(s): +# +######################################################################## +proc ixDapConfig::setChassisList {userChassisList {userTclServer ""}} \ +{ + variable chassisList + variable tclServer + + set retCode $::TCL_OK + + set chassisList $userChassisList + set tclServer $userTclServer + + return $retCode +} + + +######################################################################## +# Procedure: getBaseIpAddressList +# +# This gets the chassis baseIpAddressList. Once error getting any one of +# chassis, reset baseIpAddressList to empty which means error happened. +# +# Arguments(s): +# +######################################################################## +proc ixDapConfig::getBaseIpAddressList {} \ +{ + variable chassisList + + set baseIpAddressList [list] + + foreach chassis $chassisList { + if {![chassis get $chassis]} { + lappend baseIpAddressList [chassis cget -baseIpAddress] + } else { + Error "Error getting chassis $chassis" + set baseIpAddressList [list] + break + } + } + + return $baseIpAddressList +} + + +######################################################################## +# Procedure: addInterfaceToArray +# +# This procedure add interface to interfaceArray +# +# Arguments(s): +# +######################################################################## +proc ixDapConfig::addInterfaceToArray {args} \ +{ + variable interfaceArray + + set retCode $::TCL_OK + + set portId none + set macAddress "00 00 00 00 00 00" + set ipAddress "0.0.0.0" + set mask "0.0.0.0" + set gateway "0.0.0.0" + set enableVlan false + set vlanId 0 + + set arraySize [array size interfaceArray] + foreach arg [lindex $args 0] { + if {[regexp {^-[a-zA-Z].+} $arg]} { + # + # Current argument is preceeded by '-'. + # + switch -- $arg { + "-portId" - + "-macAddress" - + "-ipAddress" - + "-mask" - + "-gateway" - + "-enableVlan" - + "-vlanId" { + set currentOption [string trimleft $arg -] + set currentAction getValue + } + default { + Error "Invalid option \"$arg\"" + set retCode $::TCL_ERROR + break + } + } + } else { + if {[info exists currentAction]} { + switch $currentAction { + "getValue" { + switch $currentOption { + portId { + set $currentOption $arg + } + macAddress { + set $currentOption $arg + } + ipAddress { + set $currentOption $arg + } + mask { + set $currentOption $arg + } + gateway { + set $currentOption $arg + } + enableVlan { + set $currentOption $arg + } + vlanId { + set $currentOption $arg + } + } + catch {unset currentOption} + catch {unset currentAction} + } + default { + set retCode $::TCL_ERROR + break + } + } + } else { + set retCode $::TCL_ERROR + break + } + } + } + + if {$retCode == $::TCL_OK && $portId != "none"} { + set interfaceArray($arraySize) \ + [list $portId $macAddress $ipAddress $gateway $mask $enableVlan $vlanId] + } + + return $retCode +} + + +######################################################################## +# Procedure: getInterfacePortList +# +# This procedure gets the port list to use w/this application +# +# Arguments(s): +# +######################################################################## +proc ixDapConfig::getInterfacePortList {{unique 1}} \ +{ + variable interfaceArray + variable interfacePortIdIndex + + set portList [list] + foreach {index interface} [array get interfaceArray] { + set port [lindex $interface $interfacePortIdIndex] + if {$unique == $::true} { + if {[lsearch $portList $port] == -1} { + lappend portList [join [split $port ',']] + } + } else { + lappend portList [join [split $port ',']] + } + } + + return [lnumsort $portList] +} + + + +######################################################################## +# Procedure: getSortListFromArray +# +# This procedure gets sort list from array like {0 caa 1 bbb 2 abc} +# +# Arguments(s): +# +######################################################################## +proc ixDapConfig::getSortListFromArray {arrayName} \ +{ + variable $arrayName + + set itemList {} + + set arraySize [array get $arrayName] + + foreach {index args} [array get $arrayName] { + if {[llength $itemList] == 0} { + lappend itemList $index $args + } else { + set insertIndex 0 + foreach {tempIndex tempArgs} $itemList { + if {$index < $tempIndex} { + set itemList [linsert $itemList $insertIndex $index $args] + } else { + set insertIndex [expr $insertIndex + 2] + if {[llength $itemList] == [expr $insertIndex]} { + set itemList [linsert $itemList $insertIndex $index $args] + } + } + } + } + } + + return $itemList +} + +######################################################################## +# Procedure: removeInterfaceFromArray +# +# This procedure remove interface from interfaceArray +# +# Arguments(s): +# +######################################################################## +proc ixDapConfig::removeInterfaceFromArray {c l p} \ +{ + variable interfaceArray + variable interfacePortIdIndex + + foreach {index interface} [array get interfaceArray] { + set myPort [lindex $interface $interfacePortIdIndex] + if {$c == [lindex $myPort 0] && $l == [lindex $myPort 1] && $p == [lindex $myPort 2]} { + unset interfaceArray($index) + } + } +} + + +######################################################################## +# Procedure: addRouteToArray +# +# This procedure add route to routeArray +# +# Arguments(s): +# +######################################################################## +proc ixDapConfig::addRouteToArray {args} \ +{ + variable routeArray + + set retCode $::TCL_OK + set portId none + set target "net" + set ipAddress "0.0.0.0" + set gateway "0.0.0.0" + set mask "0.0.0.0" + + set arraySize [array size routeArray] + foreach arg [lindex $args 0] { + if {[regexp {^-[a-zA-Z].+} $arg]} { + # + # Current argument is preceeded by '-'. + # + switch -- $arg { + "-portId" - + "-target" - + "-ipAddress" - + "-mask" - + "-gateway" { + set currentOption [string trimleft $arg -] + set currentAction getValue + } + default { + Error "Invalid option \"$arg\"" + set retCode $::TCL_ERROR + break + } + } + } else { + if {[info exists currentAction]} { + switch $currentAction { + "getValue" { + switch $currentOption { + portId { + set $currentOption $arg + } + target { + set $currentOption $arg + } + ipAddress { + set $currentOption $arg + } + mask { + set $currentOption $arg + } + gateway { + set $currentOption $arg + } + } + catch {unset currentOption} + catch {unset currentAction} + } + default { + set retCode $::TCL_ERROR + break + } + } + } else { + set retCode $::TCL_ERROR + break + } + } + } + + if {$retCode == $::TCL_OK && $portId != "none"} { + set routeArray($arraySize) [list $portId $target $ipAddress $gateway $mask] + } + + return $retCode +} + + +######################################################################## +# Procedure: addFilterToArray +# +# This procedure add filter to filterArray +# +# Arguments(s): +# +######################################################################## +proc ixDapConfig::addFilterToArray {args} \ +{ + variable filterArray + + set retCode $::TCL_OK + + set reset false + set action enable + set portList none + set type none + set valueList none + + set arraySize [array size filterArray] + foreach arg [lindex $args 0] { + if {[regexp {^-[a-zA-Z].+} $arg]} { + # + # Current argument is preceeded by '-'. + # + switch -- $arg { + "-reset" - + "-action" - + "-portList" - + "-type" - + "-valueList" { + set currentOption [string trimleft $arg -] + set currentAction getValue + } + default { + Error "Invalid option \"$arg\"" + set retCode $::TCL_ERROR + break + } + } + } else { + if {[info exists currentAction]} { + switch $currentAction { + "getValue" { + switch $currentOption { + reset { + set $currentOption $arg + } + action { + set $currentOption $arg + } + portList { + set $currentOption $arg + } + type { + set $currentOption $arg + } + valueList { + set $currentOption $arg + } + } + catch {unset currentOption} + catch {unset currentAction} + } + default { + set retCode $::TCL_ERROR + break + } + } + } else { + set retCode $::TCL_ERROR + break + } + } + } + + if {$retCode == $::TCL_OK && $portList != "none"} { + set filterArray($arraySize) [list $reset $portList $action $type $valueList] + } + + return $retCode +} + + +######################################################################## +# Procedure: getValueFromList +# +# This procedure gets value from itemList by passing index +# +# Arguments(s): +# +######################################################################## +proc ixDapConfig::getValueFromList {itemList index} \ +{ + set retValue "" + + if {$index < [llength $itemList]} { + set retValue [lindex $itemList $index] + } + + return $retValue +} + + +######################################################################## +# Procedure: isSubnetOverlapped +# +# This procedure checks for overlapping subnets, should be eventually +# be moved into main code for 3.70. +# +# Arguments(s): +# +######################################################################## +proc ixDapConfig::isSubnetOverlapped {ipAddr1 mask1 ipAddr2 mask2} \ +{ + set retCode $::false + + set resultMask [expr [ip2num $mask1] & [ip2num $mask2]] + set range1Subnet [expr [ip2num $ipAddr1] & $resultMask] + set range2Subnet [expr [ip2num $ipAddr2] & $resultMask] + + if {$range1Subnet == $range2Subnet} { + set retCode $::true + } + + return $retCode +} + + +######################################################################## +# Procedure: setBaseIp +# +# This procedure sets a new base IP address on the chassis +# +# Arguments(s): +# chassisName - name or ID of chassis +# ipAddress - base ip address to use +# mask - mask to use w/base ip address +# +# Return: +# returns TCL_OK if successful +# +######################################################################## +proc ixDapConfig::setBaseIp {chassisName ipAddress {mask 255.255.0.0}} \ +{ + set retCode $::TCL_OK + + if [chassis get $chassisName] { + if [ixConnectToChassis $chassisName] { + Error "Error - invalid chassis $chassisName specified." + return $::TCL_ERROR + } + } + + chassis config -baseIpAddress $ipAddress + chassis config -baseAddressMask $mask + if [chassis setBaseIp $chassisName] { + Error "Unable to set base IP address $ipAddress/$mask." + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: validateBaseIpAddresses +# +# This procedure validates & checks for overlapping ip addresses among +# all base ip address in all chassis in chain +# +# Arguments(s): +# +# Return: +# returns TCL_OK if all valid base ip address & no overlap +# +######################################################################## +proc ixDapConfig::validateBaseIpAddresses {chassisList} \ +{ + set currentBaseIp "" + set currentMask "" + set overlapping $::false + + Log "\n**************************************************************" + foreach myChassis $chassisList { + if [chassis get $myChassis] { + Error "Error getting chassis $myChassis" + set retCode $::TCL_ERROR + break + } + set nextBaseIp [chassis cget -baseIpAddress] + set nextMask [chassis cget -baseAddressMask] + Log "Chassis $myChassis base IP Address == $nextBaseIp/$nextMask" + if {$currentBaseIp != "" } { + if [isSubnetOverlapped $currentBaseIp $currentMask $nextBaseIp $nextMask] { + set overlapping $::true + break + } + } + set currentBaseIp $nextBaseIp + set currentMask $nextMask + } + + Log "**************************************************************\n" + + if {$overlapping} { + Log "----> WARNING: Overlapping base ip address configuration detected in your chassis chain!!!" + Log " Please verify base ip address configuration on all chassis in chain" + Log " To modify/update the base ip address of a chassis, use the following command:\n" + Log " ixDapSetBaseIp \n\n" + } + + return [expr $overlapping?$::TCL_ERROR:$::TCL_OK] +} + + +######################################################################## +# Procedure: setupPortInterfaces +# +# This procedure adds the interfaces for the ixDapConfig +# +# Arguments(s): write - write to hardware, default is write +# +######################################################################## +proc ixDapConfig::setupPortInterfaces {PortList {write write}} \ +{ + upvar $PortList portList + + variable interfaceArray + variable interfacePortIdIndex + variable interfaceMacAddressIndex + variable interfaceIpAddressIndex + variable interfaceGwIndex + variable interfaceMaskIndex + variable interfaceEnableVlanIndex + variable interfaceVlanIdIndex + + set retCode $::TCL_OK + + Log "" ;# empty line + ip setDefault + set configedPortList {} + set interfaceList [getSortListFromArray interfaceArray] + + if [interfaceTable::setDefault portList] { + Error "Error setting interface table to default" + set retCode $::TCL_ERROR + break + } + + foreach {index interface} $interfaceList { + set myPort [getValueFromList $interface $interfacePortIdIndex] + set macAddress [getValueFromList $interface $interfaceMacAddressIndex] + set ipAddress [getValueFromList $interface $interfaceIpAddressIndex] + set gateway [getValueFromList $interface $interfaceGwIndex] + set mask [getValueFromList $interface $interfaceMaskIndex] + set enableVlan [getValueFromList $interface $interfaceEnableVlanIndex] + set vlanId [getValueFromList $interface $interfaceVlanIdIndex] + + set c [lindex $myPort 0] + set l [lindex $myPort 1] + set p [lindex $myPort 2] + if {[lsearch $configedPortList $myPort] != -1} { + continue + } else { + lappend configedPortList $myPort + } + + if [port get $c $l $p] { + Error "Error getting port $c $l $p" + set retCode $::TCL_ERROR + break + } + + port config -MacAddress $macAddress + + if {$enableVlan == "true"} { + # protocol cget -enable802dot1qTag is for all ports in configArp + # once one of ports is vlan enabled, we need to config enable802dot1qTag true + protocol config -enable802dot1qTag true + vlan config -vlanID $vlanId + if [vlanUtils::setPortTagged $c $l $p] { + Error "Error setting vlan tag for port $c $l $p" + set retCode $::TCL_ERROR + break + } + if [vlan set $c $l $p] { + Error "Error setting vlan for port $c $l $p" + set retCode $::TCL_ERROR + break + } + } else { + if [vlanUtils::setPortUntagged $c $l $p] { + Error "Error unsetting vlan tag for port $c $l $p" + set retCode $::TCL_ERROR + break + } + if [vlan set $c $l $p] { + Error "Error setting vlan for port $c $l $p" + set retCode $::TCL_ERROR + break + } + } + + if [port set $c $l $p] { + Error "Error setting port $c $l $p" + set retCode $::TCL_ERROR + break + } + + ip config -sourceIpAddr $ipAddress + ip config -destDutIpAddr $gateway + ip config -sourceIpMask $mask + + if [ip set $c $l $p] { + Error "Error setting ip $c $l $p" + set retCode $::TCL_ERROR + break + } + Log "Adding an interface on port $c $l $p: MAC:$macAddress, IP:$ipAddress, GW:$gateway, Mask:$mask, Vlan Enable:$enableVlan, Vlan Id:$vlanId" + } + + if {$retCode == $::TCL_OK} { + set retCode [configureArp configedPortList configedPortList nowrite] + } + + if {$retCode == $::TCL_OK} { + set configedPortList {} + foreach {index interface} [array get interfaceArray] { + set myPort [getValueFromList $interface $interfacePortIdIndex] + if {[lsearch $configedPortList $myPort] == -1} { + lappend configedPortList $myPort + continue + } else { + set retCode [addPortInterface $interface $write] + } + } + } + + if {$retCode == $::TCL_OK && $write == "write"} { + if [writeConfigToHardware configedPortList] { + Error "Error writing ports to hardware" + set retCode $::TCL_ERROR + } + } + + + return $retCode +} + + +######################################################################## +# Procedure: addPortInterface +# +# This procedure add one interface +# +# Arguments(s): write - write to hardware, default is write +# +######################################################################## +proc ixDapConfig::addPortInterface {interface {write write}} \ +{ + variable interfaceArray + variable interfacePortIdIndex + variable interfaceMacAddressIndex + variable interfaceIpAddressIndex + variable interfaceGwIndex + variable interfaceMaskIndex + variable interfaceEnableVlanIndex + variable interfaceVlanIdIndex + + set retCode $::TCL_OK + + set myPort [getValueFromList $interface $interfacePortIdIndex] + set macAddress [getValueFromList $interface $interfaceMacAddressIndex] + set ipAddress [getValueFromList $interface $interfaceIpAddressIndex] + set gateway [getValueFromList $interface $interfaceGwIndex] + set mask [getValueFromList $interface $interfaceMaskIndex] + set enableVlan [getValueFromList $interface $interfaceEnableVlanIndex] + set vlanId [getValueFromList $interface $interfaceVlanIdIndex] + set maskWidth [getIpV4MaskWidth $mask] + + set c [lindex $myPort 0] + set l [lindex $myPort 1] + set p [lindex $myPort 2] + set portList [list $myPort] + + # Add multiple interfaces into protocol interface table + if [interfaceTable select $c $l $p] { + Error "Error seleting port $c $l $p" + set retCode $::TCL_ERROR + break + } + + interfaceEntry clearAllItems addressTypeIpV4 + interfaceEntry clearAllItems addressTypeIpV6 + interfaceIpV4 setDefault + interfaceIpV4 config -ipAddress $ipAddress + interfaceIpV4 config -gatewayIpAddress $gateway + interfaceIpV4 config -maskWidth $maskWidth + if {[interfaceEntry addItem addressTypeIpV4]} { + Error "Error adding item into interfaceEntry" + set retCode $::TCL_ERROR + break + } + + interfaceEntry setDefault + interfaceEntry config -enable true + interfaceEntry config -description [interfaceTable::formatEntryDescription $c $l $p] + interfaceEntry config -macAddress $macAddress + interfaceEntry config -enableVlan $enableVlan + interfaceEntry config -vlanId $vlanId + if {[interfaceTable addInterface]} { + Error "Unable to add interface to Interface Table for port $c $l $p" + set retCode $::TCL_ERROR + break + } + interfaceEntry clearAllItems addressTypeIpV4 + interfaceEntry clearAllItems addressTypeIpV6 + + Log "Adding an interface on port $c $l $p: MAC:$macAddress, IP:$ipAddress, GW:$gateway, Mask:$mask, VLAN Enable:$enableVlan, VLAN ID:$vlanId" + + return $retCode +} + + +######################################################################## +# Procedure: setupRouteTable +# +# This procedure setup port routing table +# +# Arguments(s): None +# +######################################################################## +proc ixDapConfig::setupRouteTable {} \ +{ + variable routeArray + variable routePortIdIndex + variable routeTargetIndex + variable routeIpAddressIndex + variable routeGwIndex + variable routeMaskIndex + + set retCode $::TCL_OK + set configPortList [getInterfacePortList] + + Log "" ;# empty line + set routeList [getSortListFromArray routeArray] + foreach {index route} $routeList { + set myPort [getValueFromList $route $routePortIdIndex] + set target [getValueFromList $route $routeTargetIndex] + set ipAddress [getValueFromList $route $routeIpAddressIndex] + set gateway [getValueFromList $route $routeGwIndex] + set mask [getValueFromList $route $routeMaskIndex] + + set c [lindex $myPort 0] + set l [lindex $myPort 1] + set p [lindex $myPort 2] + set portList [list $myPort] + + if {[lsearch $configPortList $myPort] == -1} { + Log "Port $myPort doesn't exist. Ignore adding a route on port $c $l $p:Target:$target, IP:$ipAddress, GW:$gateway, Mask:$mask" + } else { + # delete a route if it exists before adding it + managePcpuCommand portList [format "route del -%s %s gw %s netmask %s" $target $ipAddress $gateway $mask] + set errorInfo "" + if [managePcpuCommand portList [format "route add -%s %s gw %s netmask %s" $target $ipAddress $gateway $mask]] { + Error "Error adding a route for port $c $l $p:Target:$target, IP:$ipAddress, GW:$gateway, Mask:$mask" + set retCode $::TCL_ERROR + break + } else { + Log "Adding a route on port $c $l $p:Target:$target, IP:$ipAddress, GW:$gateway, Mask:$mask" + } + } + } + + return $retCode +} + + +######################################################################## +# Procedure: setupPortFilters +# +# This procedure sets up filter on ports +# +# Arguments(s): None +# +######################################################################## +proc ixDapConfig::setupPortFilters {} \ +{ + variable filterArray + variable filterResetIndex + variable filterPortListIndex + variable filterActionIndex + variable filterTypeIndex + variable filterValueListIndex + + set retCode $::TCL_OK + set configPortList [getInterfacePortList] + + Log "" ;# empty line + set filterList [getSortListFromArray filterArray] + foreach {index filter} $filterList { + set reset [getValueFromList $filter $filterResetIndex] + set portList [getValueFromList $filter $filterPortListIndex] + set action [getValueFromList $filter $filterActionIndex] + set type [getValueFromList $filter $filterTypeIndex] + set valueList [getValueFromList $filter $filterValueListIndex] + + set removePortList {} + set newPortList {} + foreach myPort $portList { + if {[lsearch $configPortList $myPort] == -1} { + lappend removePortList $myPort + } else { + lappend newPortList $myPort + } + } + + if {[llength $removePortList] != 0} { + Log "Port $removePortList doesn't exist. Ignore adding a filter on port $removePortList: Reset:$reset, Action:$action, Type:$type, ValueList:$valueList" + } + if {[llength $newPortList] != 0} { + # Add a route on portList + if {$reset == "true"} { + if [managePcpuCommand newPortList "filter --reset"] { + Error "Error resetting filter for port $newPortList" + set retCode $::TCL_ERROR + break + } else { + Log "Resetting filter on port $newPortList" + } + } + + if [manageFilter newPortList $action $type $valueList] { + Error "Error adding filter on port $newPortList: Reset:$reset, Action:$action, Type:$type, ValueList:$valueList" + set retCode $::TCL_ERROR + break + } else { + Log "Adding a filter on port $newPortList: Reset:$reset, Action:$action, Type:$type, ValueList:$valueList" + } + } + } + + return $retCode +} + + +######################################################################## +# Procedure: manageFilter +# +# This procedure manage filter IP protocol pcpu command excuted on the ports +# +# Arguments(s): PortList - port list +# action - enable or disable +# type - ip-protocols or icmp-types or udp-ports or tcp-ports +# valueList - type value list something like {1 2 3} +# +######################################################################## +proc ixDapConfig::manageFilter {PortList action type valueList} \ +{ + upvar $PortList portList + + set retCode $::TCL_OK + set action [string tolower $action] + set type [string tolower $type] + + if {$action != "enable" && $action != "disable"} { + Error "Valid action value is enable or disable" + set retCode $::TCL_ERROR + } + + if {$type != "ip-protocols" && $type != "icmp-types" && \ + $type != "udp-ports" && $type != "tcp-ports"} { + Error "Valid type value is ip-protocols, icmp-types, udp-ports or tcp-ports" + set retCode $::TCL_ERROR + } + + if {$retCode == $::TCL_OK} { + set pcpuCommand [format "filter --%s-%s=%s" $action $type [join $valueList ,]] + set retCode [managePcpuCommand portList $pcpuCommand] + } + + return $retCode +} + + +######################################################################## +# Procedure: managePcpuCommand +# +# This procedure manage pcpu command excuted on the ports +# +# Arguments(s): PortList - port list +# pcpuCommand - pcpuCommand in string such as ls +# +######################################################################## +proc ixDapConfig::managePcpuCommand {PortList pcpuCommand} \ +{ + upvar $PortList portList + return [issuePcpuCommand portList $pcpuCommand] +} + + +######################################################################## +# Procedure: downloadPackage +# +# This procedure downloads the package to the ports +# +# Arguments(s): +# +######################################################################## +proc ixDapConfig::downloadPackage {portList} \ +{ + return [managePackage $portList downloadPackage] +} + + +######################################################################## +# Procedure: deletePackage +# +# This procedure deletes downloaded package on the ports +# +# Arguments(s): +# +######################################################################## +proc ixDapConfig::deletePackage {portList} \ +{ + return [managePackage $portList deletePackage -noverbose] +} + + +######################################################################## +# Procedure: managePackage +# +# This procedure is a utility for manager the package +# +# Arguments(s): +# portList +# action - downloadPackage | deletePackage +# +######################################################################## +proc ixDapConfig::managePackage {portList action {verbose -verbose}} \ +{ + variable defaultPortGroupId + variable appName + set retCode $::TCL_OK + + # need a portgroup + portGroup destroy $defaultPortGroupId + if [portGroup create $defaultPortGroupId] { + Error "Error creating port group $defaultPortGroupId" + set retCode $::TCL_ERROR + return $retCode + } + + foreach port $portList { + scan $port "%d %d %d" c l p + if [portGroup add $defaultPortGroupId $c $l $p] { + Error "Error adding port $c $l $p to port group $defaultPortGroupId" + set retCode $::TCL_ERROR + break + } + } + + if {$retCode == $::TCL_OK} { + switch $action { + "downloadPackage" { + if {$verbose == "-verbose"} { + Log "\nDownloading application $appName on port list..." + } + if [serviceManager $action $appName $defaultPortGroupId] { + # Get failed portList + set failedPorts [list] + foreach port $portList { + scan $port "%d %d %d" c l p + set installedPackages [split [serviceManager getInstalledPackages $c $l $p] ,] + if {[lsearch $installedPackages $appName] < 0} { + lappend failedPorts [list $c $l $p] + } + } + Error "Error downloading package $appName on port $failedPorts" + set retCode $::TCL_ERROR + } else { + if {$verbose == "-verbose"} { + Log "Download complete" + } + } + } + "deletePackage" { + if {$verbose == "-verbose"} { + Log "\nDeleting application $appName on port list..." + } + serviceManager $action $appName $defaultPortGroupId + set retCode $::TCL_OK + } + } + } + + portGroup destroy $defaultPortGroupId + + return $retCode +} + + +######################################################################## +# Procedure: pingBaseIpAddress +# +# This procedure ping chassis base Ip address. +# +# Arguments(s): retryTime: default value is 1 +# +######################################################################## +proc ixDapConfig::pingBaseIpAddress {{retryTime 1}} \ +{ + variable chassisList + + set retCode $::TCL_OK + + foreach chassis $chassisList { + if {![chassis get $chassis]} { + set baseIpAddress [chassis cget -baseIpAddress] + regsub -all ".0$" $baseIpAddress ".1" baseIpAddress + set i 1 + Log "" ;# empty line + while {$i <= $retryTime} { + Log "Pinging $baseIpAddress for chassis $chassis - $i of $retryTime ..." + set replyMsg "Reply from $baseIpAddress" + set pingResults [eval {exec ping $baseIpAddress}] + if [regexp $replyMsg $pingResults] { + break + } else { + if {$i == $retryTime} { + Error "Error pinging $baseIpAddress for chassis $chassis" + set retCode $::TCL_ERROR + break + } + } + incr i + } + } else { + Error "Error getting chassis $chassis" + set retCode $::TCL_ERROR + } + + if {$retCode} { + break + } else { + Log "Ping to $baseIpAddress for chassis $chassis is successful" + } + } + + return $retCode +} + + +######################################################################## +# Procedure: cleanUp +# +# This procedure clean up global array. +# +# Arguments(s): +# +######################################################################## +proc ixDapConfig::cleanUp {} \ +{ + variable interfaceArray + variable routeArray + variable filterArray + + if [info exists interfaceArray] { + unset interfaceArray + } + + if [info exists routeArray] { + unset routeArray + } + + if [info exists filterArray] { + unset filterArray + } + + return $::TCL_OK +} + + +######################################################################## +# Procedure: main +# +# The main procedure for the ixDapConfig package +# +# Options: +# +# +# -appName _name_of_application_to_download_ +# +# name of application to download +# +# -setFactoryDefaults _boolean_ +# reset all the ports to factory defaults as part of the download +# process. +# +# -checkLinkState _boolean_ +# +# check the link state of the target ports prior to +# starting the download process. if the link state is +# down on any of the target ports, the download process is +# stopped and $::TCL_ERROR is returned +# +# -deletePackage _boolean_ +# +# delete downloaded package before downloading package +# +# -verbose _boolean_ +# be more verbose when logging status messages +# +# The following options may merit removal +# since the application could employ them reasonably outside of main ?? +# +# -hideMainWindow _boolean_ +# Hide the main Tk window "." +# +# -pingBaseIpAddress _boolean_ +# +# ping the base ip addresses of all the chassis +# that are part of the download process. If +# if any of the chassis do not repsoned to the ping +# then pop up a routeview window and return $::TCL_ERROR +# +# Logging and Error messages: +# +# By default, logging and error messages are generated via the std. +# ixia API functions 'logMsg' and 'errorMsg'. These can be overridden +# by resetting the namespace varibles +# ixDapConfig::logCmd +# and +# ixDapConfig::errorCmd +# +# Should be functions that allow one to set these variables ?? +# +# Return Values: +# +# $::TCL_ERROR on success +# $::TCL_OK on error +# +######################################################################## +proc ixDapConfig::main {args} \ +{ + variable chassisList + variable tclServer "" + + set retCode $::TCL_OK + + # + # option defaults + # + array set opts [list \ + -hideMainWindow $::true \ + -appName "" \ + -setFactoryDefaults $::true \ + -checkLinkState $::false \ + -deletePackage $::true \ + -pingBaseIpAddress $::true \ + -verbose $::true \ + ] + + if {![string match "-*" [lindex $args 0]]} { + set opts(-appName) [lindex $args 0] + array set opts [lrange $args 1 end] + } else { + if {[llength $args] == 1} { + # Allow option pairs to be passed as a single list + set args [lindex $args 0] + } + array set opts $args + } + + if {$opts(-hideMainWindow)} { + wm withdraw . + } + + if {$opts(-verbose)} { + printVersion + } + + if [init $opts(-appName)] { + return $::TCL_ERROR + } + + + #**************************************************************** + # Delete all chassis from the chain before connecting to them + # This is a bug workaround somewhere for something + # downstream. See Debby Stopp for details + #**************************************************************** + if {[info commands chassisChain] != ""} { + chassisChain removeAll + } + if [ixProxyConnect $tclServer $chassisList] { + return $::TCL_ERROR + } + #**************************************************************** + + # Login is mandatory for this application... + if {[session cget -userName] == ""} { + ixDapConfig::login + } + + Log "Validating chassis base IP addresses..." + if [validateBaseIpAddresses $chassisList] { + return $::TCL_ERROR + } + + set portIndex 0 + set portList [getInterfacePortList] + foreach myPort $portList { + scan $myPort "%d %d %d" c l p + + if {![port isValidFeature $c $l $p $::portFeatureLocalCPU]} { + Log "PortCPU not supported on port $c $l $p - removing port from list" + set portList [lreplace $portList $portIndex $portIndex] + removeInterfaceFromArray $c $l $p + } else { + incr portIndex + } + } + + set portIndex 0 + foreach myPort $portList { + scan $myPort "%d %d %d" c l p + if [ixPortTakeOwnership $c $l $p] { + set portList [lreplace $portList $portIndex $portIndex] + removeInterfaceFromArray $c $l $p + } else { + incr portIndex + } + } + + if {[llength $portList] == 0} { + Log "No ports in port list" + return $::TCL_ERROR + } + + if {$opts(-setFactoryDefaults)} { + Log "Setting factory defaults on all ports in port list..." + if [setFactoryDefaults $portList] { + Error "Error setting factory defaults on one or more ports" + return $::TCL_ERROR + } + + if [ixWritePortsToHardware portList] { + Error "Error writing ports to hardware" + return $::TCL_ERROR + } + } + + if {$opts(-checkLinkState)} { + if [ixCheckLinkState portList] { + return $::TCL_ERROR + } + } + + if [setupPortInterfaces portList] { + return $::TCL_ERROR + } + + if [setupRouteTable] { + return $::TCL_ERROR + } + + if [setupPortFilters] { + return $::TCL_ERROR + } + + + if {$opts(-deletePackage)} { + if [deletePackage $portList] { + return $::TCL_ERROR + } + } + + if [downloadPackage $portList] { + return $::TCL_ERROR + } + + # Ping chassis base ip address. If failed after retryTime, launch viewRoutes. + if {$opts(-pingBaseIpAddress)} { + set retryTime 3 + if {[pingBaseIpAddress $retryTime]} { + set retCode $::TCL_ERROR + # Move viewRoutes gui here and make it behind wish console + # If baseIpAddressList is empty, we don't show viewRoutes gui. + set baseIpAddressList [getBaseIpAddressList] + if {[llength $baseIpAddressList] > 0 } { + viewRoutes [getBaseIpAddressList] + console hide + console show + } + } + } + + return $retCode +} + + + + + + + diff --git a/dep/tclclient/ixTcl1.0/Generic/actions.tcl b/dep/tclclient/ixTcl1.0/Generic/actions.tcl new file mode 100644 index 00000000..409153f6 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/actions.tcl @@ -0,0 +1,3241 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: actions.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 12-30-1998 DS +# +# Description: This file contains common procs used for specific actions, +# such as startTx, stopTx, etc +# +################################################################################## + + +######################################################################## +# Procedure: clearStatsAndTransmit +# +# This command clears the stat counters, starts capture & starts transmit +# +# Arguments(s): +# TxRxArray - array of ports to transmit, ie. one2oneArray +# duration - approximate time of transmit +# staggeredStart - if set to 'staggeredStart' the transmit will be staggered +# calcAvgRates - "yes" if we want to get the rates while transmitting +# avgRateArray - array containing the average running rate for tx/rx ports +# +######################################################################## +proc clearStatsAndTransmit {TxRxArray duration {staggeredStart notStaggeredStart} {calcAvgRates no} \ + {AvgRateArray ""} {calcQosRates no} {QosRateArray ""} {calcTxRxRates no} \ + {delay delay}} \ +{ + upvar $TxRxArray txRxArray + upvar $AvgRateArray arArray + upvar $QosRateArray qosRateArray + + set txPorts [getTxPorts txRxArray] + + if {[advancedTestParameter cget -primeDut] == $::true} { + startTx txRxArray $staggeredStart + after 2000 + stopTx txRxArray + } + + set retCode [prepareToTransmit txRxArray] + + if {$retCode == 0} { + set retCode [startTx txRxArray $staggeredStart] + } + + if {$retCode == 0 && $delay == "delay"} { + if {$duration > 0} { + logMsg "Transmitting frames for $duration seconds" + } else { + logMsg "Transmitting frames for < 1 second" + } + + if {$calcAvgRates == "yes"} { + if {$calcQosRates == "yes" && $calcTxRxRates == "no" } { + upvar $QosRateArray qosAvgRateArray + set retCode [collectRates txRxArray arArray $duration qosRate qosAvgRateArray ] + } elseif { $calcQosRates == "yes" && $calcTxRxRates == "yes" } { + upvar $QosRateArray qrArray + # Note: We need to get rid of this method when we get rid of cable modem suite + set retCode [getRunRatePerSecond txRxArray arArray qrArray $duration] + + } else { + set retCode [collectRates txRxArray arArray $duration] + } + } else { + # Wait for all frames to be transmitted, print msg to screen while waiting.. + if [writeWaitForTransmit $duration] { + if [stopTx txPorts] { + errorMsg "Error stopping Tx on one or more ports." + set retCode 1 + } + set ::ixStopTest 1 + } + } + } + + return $retCode +} + + +######################################################################## +# Procedure: transmitAndCollectRxRatesPerSecond +# +# This command starts transmit and collecting those receive rates we want +# +# Arguments(s): +# TxRxArray - array of ports to transmit, ie. one2oneArray +# RateArray - array of rates we want +# args - list of stat names we want to get rate of +# duration - approximate time of transmit +# staggeredStart - if set to 'staggeredStart' the transmit will be staggered +# +######################################################################## +proc transmitAndCollectRxRatesPerSecond {TxRxArray RxRateArray rxRateArgs duration {staggeredStart notStaggeredStart}} \ +{ + upvar $TxRxArray txRxArray + upvar $RxRateArray rxRateArray + + set retCode [prepareToTransmit txRxArray] + + if {$retCode == 0} { + set retCode [startTx txRxArray $staggeredStart] + } + + if {$duration > 0} { + logMsg "Transmitting frames for $duration seconds" + } else { + logMsg "Transmitting frames for < 1 second" + } + + set retCode [collectRxRatesPerSecond txRxArray rxRateArray $rxRateArgs $duration] + + # Wait for all frames to be transmitted, print msg to screen while waiting.. + if [writeWaitForTransmit $duration] { + if [stopTx txRxArray] { + errorMsg "Error stopping Tx on one or more ports." + set retCode 1 + } + } + + return $retCode +} + + +######################################################################## +# Procedure: collectRxRatesPerSecond +# +# This command starts transmit and collecting those receive rates we want +# +# Arguments(s): +# TxRxArray - array of ports to transmit +# RxRateArray - array of receive rates we want +# rxRateArgs - list of stat names we want to get receive rate of +# duration - approximate time of transmit +# +######################################################################## +proc collectRxRatesPerSecond {TxRxArray RxRateArray rxRateArgs duration} \ +{ + upvar $TxRxArray txRxArray + upvar $RxRateArray rxRateArray + + set retCode 0 + set count 0 + + set rxPortList [getRxPorts txRxArray] + + if [createDialog "Transmit Status"] { + set cmd logMsg + } else { + set cmd writeDialog + } + + set timeStart [clock seconds] + set getStatTime 0 + + for {set timeCtr 1} {$timeCtr <= $duration} {incr timeCtr} { + $cmd "Transmitted $timeCtr of $duration seconds" + mpincr count + if [getRunningRate $rxPortList rxRateArray $rxRateArgs $count] { + set retCode 1 + } + set getStatTime [mpexpr [clock seconds] - $timeStart] + if {$getStatTime <= $timeCtr} { + after 1000 + } + } + debugMsg "rxRateArray: [array get rxRateArray]" + + incr timeCtr -1 + if {$duration != $timeCtr} { + logMsg "******* Test terminated by user after $timeCtr seconds" + } + + logMsg "Done transmitting for $duration seconds...\n" + + # destroy the dialog box if it is created + if {$cmd == "writeDialog"} { + destroyDialog + } + + return $retCode +} + + +######################################################################## +# Procedure: prepareToTransmit +# +# This command stops current transmision, clears the stat counters. +# +# Arguments(s): +# TxRxArray - array of ports to transmit, ie. one2oneArray +# +######################################################################## +proc prepareToTransmit {TxRxArray} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set allPorts [getAllPorts txRxArray] + set rxPorts [getRxPorts txRxArray] + + if [stopTx allPorts] { + errorMsg "Error stopping Tx on one or more ports." + set retCode $::TCL_ERROR + } + + if {$retCode == $::TCL_OK} { + set retCode [checkAllTransmitDone txRxArray] + } + + set commandList [list resetStatistics clearTimeStamp] + if [issuePortGroupCommand $commandList allPorts] { + errorMsg "Error: Unable to issue port group commands: $commandList" + set retCode $::TCL_ERROR + } + + set commandList [list startCapture startPacketGroups] + if [issuePortGroupCommand $commandList rxPorts] { + errorMsg "Error: Unable to issue port group commands: $commandList" + set retCode $::TCL_ERROR + } + + return $retCode +} + + + +######################################################################## +# Procedure: writeWaitForTransmit +# +# This command writes the transmit msg to a little text box +# +######################################################################## +proc writeWaitForTransmit {duration {destroy destroy}} \ +{ + set retCode [writeWaitMessage "Transmit Status" "Transmitting" $duration $destroy] + + return $retCode +} + + +######################################################################## +# Procedure: writeWaitForPause +# +# This command writes the pause msg to a little text box +# +######################################################################## +proc writeWaitForPause {dialogName duration {destroy destroy}} \ +{ + set retCode [writeWaitMessage $dialogName "Pausing" $duration $destroy] + + return $retCode +} + + + +######################################################################## +# Procedure: writeWaitMessage +# +# This command writes the msg to a little text box +# +######################################################################## +proc writeWaitMessage {dialogName messageType duration {destroy destroy}} \ +{ + global ixStopTest + + set retCode 0 + + debugMsg "Begin $messageType for $duration seconds...\n" + + set messageProc writeDialog + + if {$duration <= 1 || [createDialog $dialogName]} { + set messageProc ixPuts + } + + set currentTime [clock seconds] + for {set timeCtr 1} {$timeCtr <= $duration && $ixStopTest != 1} {incr timeCtr} { + $messageProc "$messageType $timeCtr of $duration seconds" + + while {[expr {[clock seconds] - $currentTime}] < 1} { + update + after 20 + } + set currentTime [clock seconds] + } + + incr timeCtr -1 + + if {$duration != $timeCtr} { + $messageProc "******* Test terminated by user after $timeCtr seconds" + set retCode 1 + } else { + if {$duration < 1 } { + logMsg "Done after < 1 second.\n" + } else { + logMsg "Done after $duration seconds.\n" + } + } + + if {$messageProc == "writeDialog" && $destroy == "destroy"} { + destroyDialog + } + + return $retCode +} + + +######################################################################## +# Procedure: issuePortGroupCommand +# +# This command builds a port group & issues the specified command, then +# destroys the port group when it's done +# +######################################################################## +proc issuePortGroupCommand {command TxRxList {verbose noVerbose} {LastTimestamp ""} {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxList txRxList + upvar $LastTimestamp lastTimestamp + + return [issuePortGroupMethod txRxList lastTimestamp -method setCommand -commandList $command -groupId $groupId -$verbose -$create -$destroy] +} + + +######################################################################## +# Procedure: getPortGroupObject +# +# This command builds a port group & issues the specified command, then +# destroys the port group when it's done +# +######################################################################## +proc getPortGroupObject {object TxRxList {verbose noVerbose} {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxList txRxList + + set lastTimestamp "" + return [issuePortGroupMethod txRxList lastTimestamp -method get -commandList $object -groupId $groupId -$verbose -$create -$destroy] +} + + +######################################################################## +# Procedure: issuePortGroupMethod +# +# This command builds a port group & issues the specified command, then +# destroys the port group when it's done +# +# Argument(s): +# PortArray either list of ports or array of ports +# args options include: +# -method +# -commandList +# -verbose +# -noVerbose +# -groupId +# -create/-noCreate +# -destroy/-noDestroy +# -duration +# +######################################################################## +proc issuePortGroupMethod { PortArray LastTimestamp args } \ +{ + upvar $PortArray portArray + upvar $LastTimestamp lastTimestamp + + set retCode $::TCL_OK + + # default some vars here... + set method setCommand + set commandList [list] + set verbose $::true + set groupId 710 + set create $::true + set destroy $::true + set duration 0 + set command none + set finalCommandList [list] + + foreach arg [join $args] { + # just go ahead & remove the '-', makes things easier + set dash [expr [regsub -all {^-} $arg "" arg]?"-":""] + + if { $arg == "commandList" } { + set $command $arg + set command commandList + continue + } else { + if { $command == "commandList" } { + if { $dash == "" && [lsearch $finalCommandList $arg] <= 0 } { + lappend finalCommandList $arg + set command commandList + set $command $finalCommandList + continue + } else { + set $command $finalCommandList + set command none + } + } + } + + switch $command { + method - + groupId - + duration - + noDestroy { + set $command $arg + set command none + } + + none { + switch $arg { + method { + set command method + } + duration { + set command duration + } + groupId { + set command groupId + } + verbose { + set verbose $::true + } + noVerbose - + noverbose { + set verbose $::false + } + create { + set create $::true + } + nocreate - + noCreate { + set create $::false + } + destroy { + set destroy $::true + } + nodestroy - + noDestroy { + set destroy $::false + } + default { + errorMsg "Parameter not supported: $dash$arg" + set retCode $::TCL_ERROR + } + } + } + default { + errorMsg "Error in parameters: $args" + set retCode $::TCL_ERROR + } + } + } + #logMsg "\n method:$method, verbose:$verbose, groupId:$groupId, duration:$duration, \ + # create:$create, destroy:$destroy, commandList:$commandList\n" + + + if [llength $portArray] { + set verbose "-$verbose" + if {$create == $::true} { + debugMsg "issuePortGroupCommand: create port group $groupId" + if [portGroup create $groupId] { + errorMsg $verbose "Error creating port group $groupId" + set retCode $::TCL_ERROR + } + } + + if {$retCode == 0} { + set tx_l 0 + set tx_p 0 + foreach tx_port $portArray { + if {[scan $tx_port "%d %d %d" tx_c tx_l tx_p] != 3} { + errorMsg $verbose "Error in input port array: $tx_port" + set retCode $::TCL_ERROR + } else { + if [portGroup add $groupId $tx_c $tx_l $tx_p] { + errorMsg $verbose "Error adding [getPortId $tx_c $tx_l $tx_p] to Tx Port Group." + set retCode $::TCL_ERROR + } + } + } + + foreach command $commandList { + switch $method { + setCommand { + debugMsg "issuePortGroupCommand: method:$method, Command -$command, portArray -$portArray" + + # We don't want to change the original return values ($::TCL_OK/$::TCL_ERROR) of issuePortGroupMethod. + set retVal [portGroup $method $groupId $command] + + switch $retVal \ + $::ixTcl_notLicensed { + errorMsg "Error setting command $command for port group $groupId - ixTcl_notLicensed" + set retCode $::TCL_ERROR + }\ + $::ixTcl_notAvailable { + errorMsg "Error setting command $command for port group $groupId - ixTcl_notAvailable" + set retCode $::TCL_ERROR + }\ + $::ixTcl_generalError { + errorMsg "Error setting command $command for port group $groupId - ixTcl_generalError" + set retCode $::TCL_ERROR + }\ + $::ixTcl_ok -\ + default { + } + + set lastTimestamp [portGroup cget -lastTimestamp] + } + get { + if {[portGroup $method $groupId $command]} { + errorMsg $verbose "Error getting $method $command for port group $groupId" + set retCode $::TCL_ERROR + } + } + clearScheduledTransmitTime { + if {[portGroup $method $groupId]} { + errorMsg $verbose "Error $method for port group $groupId" + set retCode $::TCL_ERROR + } + } + + setScheduledTransmitTime { + if {[portGroup $method $groupId $duration]} { + errorMsg $verbose "Error $method for port group $groupId" + set retCode $::TCL_ERROR + } + } + } + } + } + + if {$destroy == $::true} { + debugMsg "issuePortGroupCommand: destroy port group $groupId" + if [portGroup destroy $groupId] { + errorMsg $verbose "Error destroying port group $groupId" + set retCode $::TCL_ERROR + } + } + } else { + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: startTx +# +# This command arms each Tx port & then sends out a pulse to the master +# to begin transmitting +# +# Arguments: +# TxRxArray - either array or list containing ports to start +# transmit on +# +######################################################################## +proc startTx {TxRxArray {staggeredStart notStaggeredStart} {FirstTimestamp ""} {groupId 710} {create create} {destroy destroy}} \ +{ + global ixgFirstTimeStamp + + upvar $TxRxArray txRxArray + upvar $FirstTimestamp firstTimestamp + + set retCode $::TCL_OK + set firstTimestamp 0 + + set txRxList [getTxPorts txRxArray] + + if {$staggeredStart == "notStaggeredStart" || $staggeredStart == "false"} { + set command startTransmit + } else { + set command staggeredStartTransmit + } + + if [issuePortGroupCommand $command txRxList noVerbose firstTimestamp $groupId $create $destroy] { + errorMsg "Error starting Transmission for port group" + set retCode $::TCL_ERROR + } + + set ixgFirstTimeStamp $firstTimestamp + + return $retCode +} + + +######################################################################## +# Procedure: startStaggeredTx +# +# This command arms each Tx port & then sends out a pulse to the master +# to begin transmitting +# +# Arguments: +# TxRxArray - either array or list containing ports to start +# transmit on +# +######################################################################## +proc startStaggeredTx {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + return [startTx txRxArray staggeredStart firstTimeStamp $groupId $create $destroy] +} + + +######################################################################## +# Procedure: stopTx +# +# This command arms each Tx port & then sends out a pulse to the master +# to stop transmitting +# +# Arguments: +# TxRxArray - either array or list containing ports to stop +# transmit on +# +######################################################################## +proc stopTx {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set txRxList [getTxPorts txRxArray] + if [issuePortGroupCommand stopTransmit txRxList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error stopping Transmission on port group" + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: startPortTx +# +# This command starts Tx on a single port; it will also stop transmit & +# zero stats on this port before transmitting. +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc startPortTx {chassis lm port {FirstTimestamp ""} {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $FirstTimestamp firstTimestamp + set retCode $::TCL_OK + + set portList [list [list $chassis $lm $port]] + if [issuePortGroupCommand startTransmit portList noVerbose firstTimestamp $groupId $create $destroy] { + errorMsg "Error starting Transmission on port $chassis,$lm,$port" + set retCode $::TCL_ERROR + } + if [info exists firstTimestamp] { + debugMsg "startPortTx on port $chassis,$lm,$port: firstTimestamp = $firstTimestamp" + } + return $retCode +} + + +######################################################################## +# Procedure: stopPortTx +# +# This command stops Tx on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc stopPortTx {chassis lm port {groupId 710} {create create} {destroy destroy}} \ +{ + set retCode $::TCL_OK + + set portList [list [list $chassis $lm $port]] + if [issuePortGroupCommand stopTransmit portList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error stopping Transmission on port $chassis,$lm,$port" + set retCode $::TCL_ERROR + } + return $retCode +} + + +######################################################################## +# Procedure: startCapture +# +# This command turns on capture for each Rx port +# +# Arguments: +# TxRxArray - either array or list containing ports to start +# capture on +# +######################################################################## +proc startCapture {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode 0 + + set txRxList [getRxPorts txRxArray] + if [issuePortGroupCommand startCapture txRxList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error starting Capture for port group" + set retCode 1 + } + + return $retCode +} + + +######################################################################## +# Procedure: startPrbsCapture +# +# This command turns on capture and Packet Groups for each Rx port +# +# Arguments: +# TxRxArray - either array or list containing ports to start +# capture on +# +######################################################################## +proc startPrbsCapture {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode 0 + + set txRxList [getRxPorts txRxArray] + if [issuePortGroupCommand startPrbsCapture txRxList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error starting PRBS metric collection for port group" + set retCode 1 + } + + return $retCode +} + + +######################################################################## +# Procedure: stopCapture +# +# This command stops capture for each Rx port +# +# Arguments: +# TxRxArray - either array or list containing ports to stop +# capture on +# +######################################################################## +proc stopCapture {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode 0 + + set txRxList [getRxPorts txRxArray] + if [issuePortGroupCommand stopCapture txRxList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error stopping Capture for port group" + set retCode 1 + } + + return $retCode +} + +######################################################################## +# Procedure: stopPrbsCapture +# +# This command stops capture and Packet Groups for each Rx port +# +# Arguments: +# TxRxArray - either array or list containing ports to stop +# capture on +# +######################################################################## +proc stopPrbsCapture {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode 0 + + set txRxList [getRxPorts txRxArray] + if [issuePortGroupCommand stopPrbsCapture txRxList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error stopping PRBS metric collection for port group" + set retCode 1 + } + + return $retCode +} + +######################################################################## +# Procedure: clearPrbsCapture +# +# This command clears PRBS capture for each Rx port +# +# Arguments: +# TxRxArray - either array or list containing ports to stop +# capture on +# +######################################################################## +proc clearPrbsCapture {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode 0 + + set txRxList [getRxPorts txRxArray] + if [issuePortGroupCommand clearPrbsCapture txRxList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error clearing PRBS capture for port group" + set retCode 1 + } + + return $retCode +} + + +######################################################################## +# Procedure: startPortCapture +# +# This command starts capture on a single port; +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc startPortCapture {chassis lm port {groupId 710} {create create} {destroy destroy}} \ +{ + set retCode 0 + + set portList [list [list $chassis $lm $port]] + if [issuePortGroupCommand startCapture portList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error starting Capture on port $chassis,$lm,$port" + set retCode 1 + } + + return $retCode +} + + +######################################################################## +# Procedure: startPortPrbsCapture +# +# This command starts capture and Packet Groups on a single port; +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc startPortPrbsCapture {chassis lm port {groupId 710} {create create} {destroy destroy}} \ +{ + set retCode 0 + + set portList [list [list $chassis $lm $port]] + if [issuePortGroupCommand startPrbsCapture portList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error starting PRBS metric collection on port $chassis,$lm,$port" + set retCode 1 + } + + return $retCode +} + + + +######################################################################## +# Procedure: stopPortCapture +# +# This command stops capture on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 1 if OK, 0 if port not configured +# +######################################################################## +proc stopPortCapture {chassis lm port {groupId 710} {create create} {destroy destroy}} \ +{ + set retCode 0 + + set portList [list [list $chassis $lm $port]] + if [issuePortGroupCommand stopCapture portList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error stopping Capture on port $chassis,$lm,$port" + set retCode $::TCL_ERROR + } + + return $retCode +} + +######################################################################## +# Procedure: stopPortPrbsCapture +# +# This command stops capture and Packet Groups on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 1 if OK, 0 if port not configured +# +######################################################################## +proc stopPortPrbsCapture {chassis lm port {groupId 710} {create create} {destroy destroy}} \ +{ + set retCode 0 + + set portList [list [list $chassis $lm $port]] + if [issuePortGroupCommand stopPrbsCapture portList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error stopping PRBS metric collection on port $chassis,$lm,$port" + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: clearPortPrbsCapture +# +# This command clears PRBS capture on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 1 if OK, 0 if port not configured +# +######################################################################## +proc clearPortPrbsCapture {chassis lm port {groupId 710} {create create} {destroy destroy}} \ +{ + set retCode 0 + + set portList [list [list $chassis $lm $port]] + if [issuePortGroupCommand clearPrbsCapture portList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error clearing PRBS capture on port $chassis,$lm,$port" + set retCode $::TCL_ERROR + } + + return $retCode +} + +######################################################################## +# Procedure: startPacketGroups +# +# This command arms each Rx port & then sends out a pulse to the master +# to initiate packetGroup stats +# +# Arguments: +# TxRxArray - either array or list containing ports to start +# capturing latency stats on +# +######################################################################## +proc startPacketGroups {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set txRxList [getRxPorts txRxArray] + + if [issuePortGroupCommand startPacketGroups txRxList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error starting packetGroup stats for port group" + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: stopPacketGroups +# +# This command arms each Rx port & then sends out a pulse to the master +# to stop packetGroup stats +# +# Arguments: +# TxRxArray - either array or list containing ports to stop +# capturing latency stats on +# +######################################################################## +proc stopPacketGroups {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set txRxList [getRxPorts txRxArray] + if [issuePortGroupCommand stopPacketGroups txRxList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error stopping packetGroup stats for port group" + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: startPortPacketGroups +# +# This command starts packetGroup stats on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc startPortPacketGroups {chassis lm port {groupId 710} {create create} {destroy destroy}} \ +{ + set retCode $::TCL_OK + + set portList [list [list $chassis $lm $port]] + if [issuePortGroupCommand startPacketGroups portList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error starting packetGroup stats on port $chassis,$lm,$port" + set retCode $::TCL_ERROR + } + + return $retCode +} + +######################################################################## +# Procedure: stopPortPacketGroups +# +# This command stops packetGroup stats on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc stopPortPacketGroups {chassis lm port {groupId 710} {create create} {destroy destroy}} \ +{ + set retCode $::TCL_OK + + set portList [list [list $chassis $lm $port]] + if [issuePortGroupCommand stopPacketGroups portList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error stopping packetGroup stats on port $chassis,$lm,$port" + set retCode $::TCL_ERROR + } + return $retCode +} + +######################################################################## +# Procedure: clearPacketGroups +# +# This command arms each Rx port & then sends out a pulse to the master +# to clear packetGroup stats +# +# Arguments: +# TxRxArray - either array or list containing ports to stop +# capturing latency stats on +# +######################################################################## +proc clearPacketGroups {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set txRxList [getRxPorts txRxArray] + if [issuePortGroupCommand clearPacketGroups txRxList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error clearing packetGroup stats for port group" + set retCode $::TCL_ERROR + } + + return $retCode +} + +######################################################################## +# Procedure: clearPortPacketGroups +# +# This command clears packetGroup stats on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc clearPortPacketGroups {chassis lm port {groupId 710} {create create} {destroy destroy}} \ +{ + set retCode $::TCL_OK + + set portList [list [list $chassis $lm $port]] + if [issuePortGroupCommand clearPacketGroups portList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error clearing packetGroup stats on port $chassis,$lm,$port" + set retCode $::TCL_ERROR + } + return $retCode +} + + +######################################################################## +# Procedure: startCollisions +# +# This command arms each Rx port & then sends out a pulse to the master +# to initiate collisions +# +# Arguments: +# TxRxArray - either array or list containing ports to start collisions +# +######################################################################## +proc startCollisions {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode 0 + + set rxPortList [getRxPorts txRxArray] + + if [issuePortGroupCommand collisionStart rxPortList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error starting collisions stats for port group" + set retCode 1 + } + + return $retCode +} + + +######################################################################## +# Procedure: stopCollisions +# +# This command arms each Rx port & then sends out a pulse to the master +# to stop collisions +# +# Arguments: +# TxRxArray - either array or list containing ports to stop collisions +# +######################################################################## +proc stopCollisions {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode 0 + + set rxPortList [getRxPorts txRxArray] + if [issuePortGroupCommand collisionStop rxPortList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error stopping collisions stats for port group" + set retCode 1 + } + + return $retCode +} + + +######################################################################## +# Procedure: startPortCollisions +# +# This command starts collisions on a single port +# +# Arguments(s): +# c - chassis +# l - card +# p - port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc startPortCollisions {c l p {groupId 710} {create create} {destroy destroy}} \ +{ + set retCode 0 + + set portList [list [list $c $l $p]] + + if [startCollisions portList $groupId $create $destroy] { + errorMsg "Error starting collisions stats on port [getPortId $c $l $p]" + set retCode 1 + } + + return $retCode +} + +######################################################################## +# Procedure: stopPortCollisions +# +# This command stops collisions on a single port +# +# Arguments(s): +# c - chassis +# l - card +# p - port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc stopPortCollisions {c l p {groupId 710} {create create} {destroy destroy}} \ +{ + set retCode 0 + + set portList [list [list $c $l $p]] + if [stopCollisions portList $groupId $create $destroy] { + errorMsg "Error stopping collisions stats on port [getPortId $c $l $p]" + set retCode 1 + } + return $retCode +} + + +######################################################################## +# Procedure: zeroStats +# +# This command zeros all stats +# +# Arguments: +# TxRxArray - either array or list containing ports to zero +# stats on +# +######################################################################## +proc zeroStats {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + logMsg "Resetting Statistics ..." + + set txRxList [getAllPorts txRxArray] + if [issuePortGroupCommand resetStatistics txRxList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error resetting stats for port group" + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: zeroPortStats +# +# This command zeros all stats on this port +# +# Argument(s): +# chassis chassis ID +# lm Load Module number +# port port number +# +######################################################################## +proc zeroPortStats {chassis lm port {groupId 710} {create create} {destroy destroy}} \ +{ + set retCode $::TCL_OK + + set portList [list [list $chassis $lm $port]] + if [issuePortGroupCommand resetStatistics portList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error resetting stats on port $chassis,$lm,$port" + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: clearPerStreamTxStats +# +# This command zeros all stream Tx stats on all ports +# +# Arguments: +# TxRxArray - either array or list containing ports to zero +# stats on +# +######################################################################## +proc clearPerStreamTxStats {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set txRxList [getAllPorts txRxArray] + if [issuePortGroupCommand clearPerStreamTxStats txRxList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error clearing per stream Tx stats for port group" + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: clearPerStreamTxPortStats +# +# This command zeros all stream Tx stats on this port +# +# Argument(s): +# chassis chassis ID +# lm Load Module number +# port port number +# +######################################################################## +proc clearPerStreamTxPortStats {chassis lm port {groupId 710} {create create} {destroy destroy}} \ +{ + set retCode $::TCL_OK + + set portList [list [list $chassis $lm $port]] + if [issuePortGroupCommand clearPerStreamTxStats portList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error clearing per stream Tx stats on port $chassis,$lm,$port" + set retCode $::TCL_ERROR + } + + return $retCode +} + +######################################################################## +# Procedure: clearPcsLaneStatistics +# +# This command zeros all pcs data lane stats on all ports +# +# Arguments: +# TxRxArray - either array or list containing ports to zero +# stats on +# +######################################################################## +proc clearPcsLaneStatistics {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set txRxList [getAllPorts txRxArray] + if [issuePortGroupCommand clearPcsLaneStats txRxList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error clearing pcs lane stats for port group" + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: clearPcsLanePortStatistics +# +# This command zeros all pcs data lane stats on this ports +# +# Argument(s): +# chassis chassis ID +# lm Load Module number +# port port number +# +######################################################################## +proc clearPcsLanePortStatistics {chassis lm port {groupId 710} {create create} {destroy destroy}} \ +{ + set retCode $::TCL_OK + + set portList [list [list $chassis $lm $port]] + if [issuePortGroupCommand clearPcsLaneStats portList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error clearing pcs lane stats on port $chassis,$lm,$port" + set retCode $::TCL_ERROR + } + + return $retCode +} + + + +######################################################################## +# Procedure: clearTimeStamp +# +# This command synchronizes the timestamp value among all chassis +# +# Arguments: +# TxRxArray - either array or list containing ports to clear/synchronize +# time stamp on +# +######################################################################## +proc clearTimeStamp {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode 0 + + set txRxList [getAllPorts txRxArray] + if [issuePortGroupCommand clearTimeStamp txRxList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error clearing the time stamp for port group " + set retCode 1 + } + + return $retCode +} + + +######################################################################## +# Procedure: flushAddressTable +# +# This command writes MII in order to flush the address table. Note: +# this may not work with all DUTs. +# +# Arguments(s): +# PortMap one2oneArray, one2manyArray, many2oneArray or many2manyArray +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc flushAddressTable { PortMap {checkLink yes}} \ +{ + upvar $PortMap portMap + + set retCode $::TCL_OK + + set portList [getAllPorts portMap] + + changePortLoopback portList $::true verbose + checkLinkState portMap portsToRemove messageOff + changePortLoopback portList $::false verbose + + # check the link state of all ports; if down, remove from list + if {$checkLink == "yes"} { + if [checkLinkState portList portsToRemove noMessage] { + if { [llength $portsToRemove] >0 && [advancedTestParameter cget -removePortOnLinkDown] == "true" } { + set retCode [removePorts portList $portsToRemove] + } else { + set errMsg "Link down on one or more ports" + set retCode $::TCL_ERROR + } + } + } + + return $retCode +} + + +######################################################################## +# Procedure: enableArpResponse +# +# This command gets the MAC & IP addresses for that port, sets up the +# address table and enables the arp response engine for all ports in +# the portlist +# +# Arguments(s): +# mapType - either oneIpToOneMAC or manyIpToOneMAC +# PortMap - list or array of ports, ie. ixgSortMap +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc enableArpResponse { mapType PortMap {write nowrite}} \ +{ + upvar $PortMap portMap + set retCode 0 + + set portList [getAllPorts portMap] + foreach myport $portList { + scan $myport "%d %d %d" c l p + + if [enablePortArpResponse $mapType $c $l $p nowrite] { + set retCode 1 + } + } + + if {$retCode == 0 && $write == "write"} { + set retCode [writeConfigToHardware portMap] + } + + return $retCode +} + + +######################################################################## +# Procedure: enablePortArpResponse +# +# This command gets the MAC & IP addresses for that port, sets up the +# address table and enables the arp response engine for the specified port +# +# Arguments(s): +# mapType - either oneIpToOneMAC or manyIpToOneMAC +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc enablePortArpResponse { mapType chassis lm port {write write}} \ +{ + set retCode 0 + + protocolServer config -enableArpResponse true + + if [protocolServer set $chassis $lm $port] { + errorMsg "Error setting protocol server on $chassis $lm $port" + set retCode 1 + } + + if {$write == "write" && $retCode == 0} { + if [protocolServer write $chassis $lm $port] { + errorMsg "Error writing protocol server on $chassis $lm $port" + set retCode 1 + } + } + + return $retCode +} + + +######################################################################## +# Procedure: disableArpResponse +# +# This command disables the arp response engine for all ports in +# the portlist +# +# Arguments(s): +# PortMap - list or array of ports, ie. ixgSortMap +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc disableArpResponse { PortMap {write nowrite}} \ +{ + upvar $PortMap portMap + set retCode 0 + + set portList [getAllPorts portMap] + foreach myport $portList { + scan $myport "%d %d %d" c l p + + if [disablePortArpResponse $c $l $p nowrite] { + set retCode 1 + } + } + + if {$retCode == 0 && $write == "write"} { + set retCode [writeConfigToHardware portMap] + } + + return $retCode +} + + +######################################################################## +# Procedure: disablePortArpResponse +# +# This command disables the arp response engine for the specified port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc disablePortArpResponse {chassis lm port {write write}} \ +{ + set retCode 0 + + if [protocolServer get $chassis $lm $port] { + errorMsg "Error getting protocol server on $chassis $lm $port" + set retCode 1 + } + + protocolServer config -enableArpResponse false + + if [protocolServer set $chassis $lm $port] { + errorMsg "Error setting protocol server on $chassis $lm $port" + set retCode 1 + } + + if {$write == "write" && $retCode == 0} { + if [protocolServer write $chassis $lm $port] { + errorMsg "Error writing protocol server on $chassis $lm $port" + set retCode 1 + } + } + + return $retCode +} + + + +######################################################################## +# Procedure: transmitArpRequest +# +# This command transmits an Arp request via the protocol server. +# +# Arguments: +# TxRxArray - either array or list containing ports to transmit +# arp request on +# +######################################################################## +proc transmitArpRequest {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode 0 + + set txRxList [getTxPorts txRxArray] + if [issuePortGroupCommand transmitArpRequest txRxList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error transmitting arp request for port group" + set retCode 1 + } + + + return $retCode +} + + +######################################################################## +# Procedure: transmitPortArpRequest +# +# This command transmits an Arp request via the protocol server on a +# single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc transmitPortArpRequest {chassis lm port} \ +{ + set retCode 0 + + if [arpServer sendArpRequest $chassis $lm $port] { + errorMsg "Error transmitting arp request on port $chassis,$lm,$port" + set retCode 1 + } + + return $retCode +} + + +######################################################################## +# Procedure: clearArpTable +# +# This command clears the arp table via the protocol server. +# +# Arguments: +# TxRxArray - either array or list containing ports to clear +# arp table on +# +######################################################################## +proc clearArpTable {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode 0 + + set txRxList [getTxPorts txRxArray] + if [issuePortGroupCommand clearArpTable txRxList noVerbose lastTimestamp $groupId $create $destroy] { + errorMsg "Error clearing arp for port group" + set retCode 1 + } + + return $retCode +} + + +######################################################################## +# Procedure: clearPortArpTable +# +# This command clears the arp table on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc clearPortArpTable {chassis lm port} \ +{ + set retCode 0 + + if [arpServer clearArpTable $chassis $lm $port] { + errorMsg "Error clearing arp table on port $chassis,$lm,$port" + set retCode 1 + } + + return $retCode +} + + + +######################################################################## +# Procedure: setDataIntegrityMode +# +# This command sets all the RX ports in the list or array to data +# integrity mode +# +# Arguments(s): +# TxRxArray - list or array of RX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc setDataIntegrityMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + return [changePortReceiveMode txRxArray $::portRxDataIntegrity $write] +} + +######################################################################## +# Procedure: setPrbsMode +# +# This command sets all the RX ports in the list or array to data +# integrity mode +# +# Arguments(s): +# TxRxArray - list or array of RX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc setPrbsMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + return [changePortReceiveMode txRxArray $::portRxModePrbs $write] +} + +######################################################################## +# Procedure: setPacketGroupMode +# +# This command sets all the RX ports in the list or array to packet +# group mode +# +# Arguments(s): +# TxRxArray - list or array of RX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc setPacketGroupMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + return [changePortReceiveMode txRxArray $::portPacketGroup $write] +} + + +######################################################################## +# Procedure: setWidePacketGroupMode +# +# This command sets all the RX ports in the list or array to wide packet +# group mode +# +# Arguments(s): +# TxRxArray - list or array of RX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc setWidePacketGroupMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + return [changePortReceiveMode txRxArray $::portRxModeWidePacketGroup $write] +} + + +######################################################################## +# Procedure: setCaptureMode +# +# This command sets all the RX ports in the list or array to capture +# mode +# +# Arguments(s): +# TxRxArray - list or array of RX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc setCaptureMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + return [changePortReceiveMode txRxArray $::portCapture $write] +} + + +######################################################################## +# Procedure: setTcpRoundTripFlowMode +# +# This command sets all the RX ports in the list or array to round trip +# tcp flow mode +# +# Arguments(s): +# TxRxArray - list or array of TX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc setTcpRoundTripFlowMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + global portRxTcpRoundTrip + + return [changePortReceiveMode txRxArray $portRxTcpRoundTrip $write] +} + + +######################################################################## +# Procedure: setPacketStreamMode +# +# This command sets all the TX ports in the list or array to packet +# stream mode +# +# Arguments(s): +# TxRxArray - list or array of TX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc setPacketStreamMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + global portTxPacketStreams + + return [changePortTransmitMode txRxArray $portTxPacketStreams $write] +} + + +######################################################################## +# Procedure: setPacketFlowMode +# +# This command sets all the TX ports in the list or array to packet +# flow mode +# +# Arguments(s): +# TxRxArray - list or array of TX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc setPacketFlowMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set retCode [changePortTransmitMode txRxArray $::portTxPacketFlows $write] + switch $retCode " + $::TCL_OK - + $::ixTcl_unsupportedFeature { + set retCode $::TCL_OK + } + " + return $retCode +} + + +######################################################################## +# Procedure: setAdvancedStreamSchedulerMode +# +# This command sets all the TX ports in the list or array to use the +# advanced scheduler +# +# Arguments(s): +# TxRxArray - list or array of TX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc setAdvancedStreamSchedulerMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set retCode [changePortTransmitMode txRxArray $::portTxModeAdvancedScheduler $write] + switch $retCode " + $::TCL_OK - + $::ixTcl_unsupportedFeature { + set retCode $::TCL_OK + } + " + return $retCode +} + + +######################################################################## +# Procedure: setFirstLastTimestampMode +# +# This command sets all the TX ports in the list or array to PG TimeStamp +# +# Arguments(s): +# TxRxArray - list or array of TX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc setFirstLastTimestampMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + global portRxFirstTimeStamp + + return [changePortReceiveMode txRxArray $portRxFirstTimeStamp $write] +} + +######################################################################## +# Procedure: setDataIntegrityMode +# +# This command sets all the TX ports in the list or array to Data integrity +# Mode +# +# Arguments(s): +# TxRxArray - list or array of TX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc setDataIntegrityMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + global portRxDataIntegrity + + return [changePortReceiveMode txRxArray $portRxDataIntegrity $write] +} + + +######################################################################## +# Procedure: setSequenceCheckingMode +# +# This command sets all the TX ports in the list or array to Sequence +# Checking Mode +# +# Arguments(s): +# TxRxArray - list or array of TX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc setSequenceCheckingMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + return [changePortReceiveMode txRxArray $::portRxSequenceChecking $write] +} + + +######################################################################## +# Procedure: changePortTransmitMode +# +# This command sets all the TX ports in the list or array to the +# specified transmit mode & optionally writes it to hw +# +# NOTE: This proc does not affect oc12 cards because transmit modes +# are not yet supported on those cards. +# +# NOTE: For OC48, if the transmit mode is specifed as "packetFlows", +# this proc will download the advancedScheduler fpga. +# +# Arguments(s): +# TxRxArray - list or array of TX ports to change +# transmitMode - either portTxPacketStreams or portTxPacketFlows +# write - write ports to hw as they are modified +# +######################################################################## +proc changePortTransmitMode {TxRxArray transmitMode {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + set retCode 0 + set infoFlag 1 + + foreach tx_port [getTxPorts txRxArray] { + scan $tx_port "%d %d %d" tx_c tx_l tx_p + + # Check if the transmit mode is valid for the port, if it is not valid, just do nothing. + # Some tests use packetFlow mode, but it is not available for 10100DPM, OC48 and oc192 + # (only packetStream & AdvancedScheduler modes applicable to 10100DPM, OC48 and oc192), + # in this case we still use packetStream mode. + + set retCode [port setTransmitMode $transmitMode $tx_c $tx_l $tx_p] + + switch $retCode { + 0 { + lappend modList [list $tx_c $tx_l $tx_p] + } + 1 { + errorMsg "Error setting port [getPortId $tx_c $tx_l $tx_p]" + continue + } + 100 { + errorMsg "Port [getPortId $tx_c $tx_l $tx_p] is unavailable, check ownership." + continue + } + 101 { + # Note: we won't print out this warning msg temporarily. + # logMsg "!WARNING:[getTxRxModeString $transmitMode ] not supported on port\ + # [getPortId $tx_c $tx_l $tx_p], actual transmit rate may vary from configured rate." + + set infoFlag 0 + continue + } + 200 { + set retCode 0 + continue + } + } + } + + if {$write == "write" && [info exists modList]} { + logMsg "Changing TX port mode - downloading FPGA, please wait ..." + set retCode [writePortsToHardware modList -noProtocolServer] + } else { + if {$infoFlag} { + logMsg "Configuring TX port mode to [getTxRxModeString $transmitMode]" + } + } + + return $retCode +} + + +######################################################################## +# Procedure: changePortReceiveMode +# +# This command sets all the RX ports in the list or array to the +# specified Receive mode & optionally writes it to hw +# +# Arguments(s): +# TxRxArray - list or array of RX ports to change +# receiveMode - +# write - write ports to hw as they are modified +# +######################################################################## +proc changePortReceiveMode {TxRxArray receiveMode {write nowrite} {verbose yes} } \ +{ + upvar $TxRxArray txRxArray + set retCode 0 + + set txRxList [getRxPorts txRxArray] + + # Checking link state here because if the link is coming up or not quite up yet when we do + # the port set, then the port gets set all wrong... + +# checkLinkState txRxList portsToRemove noMessage + + foreach rx_port $txRxList { + scan $rx_port "%d %d %d" rx_c rx_l rx_p + set retCode [port setReceiveMode $receiveMode $rx_c $rx_l $rx_p] + + switch $retCode { + 0 { + lappend modList [list $rx_c $rx_l $rx_p] + } + 1 { + errorMsg "Error setting port [getPortId $rx_c $rx_l $rx_p]" + continue + } + 100 { + errorMsg "Port [getPortId $rx_c $rx_l $rx_p] is unavailable, check ownership." + continue + } + 101 { + errorMsg "!WARNING:[getTxRxModeString $receiveMode RX] mode not supported on port \ + [getPortId $rx_c $rx_l $rx_p]" + continue + } + 200 { + set retCode 0 + continue + } + } + } + + if {$write == "write" && [info exists modList]} { + logMsg "Changing RX port mode - downloading FPGA, please wait ..." + if [writePortsToHardware modList -noProtocolServer] { + set retCode 1 + } + set retCode [checkLinkState modList] + } else { + if {$verbose == "yes" } { + logMsg "Configuring RX port mode to [getTxRxModeString $receiveMode RX]" + } + } + + return $retCode +} + + +######################################################################## +# Procedure: writeToHardware +# +# This command writes into hardware. +# +# Argument(s): +# PortArray either list of ports or array of ports +# +######################################################################## +proc writeToHardware {PortArray args} \ +{ + upvar $PortArray portArray + ixProfile + set retCode $::TCL_OK + + set portList [getAllPorts portArray] + + # default some vars here... + set method write + set verbose $::true + set groupId 1126 + set create $::true + set destroy $::true + + # this one is *new*, set to false to avoid stopping the protocol server + # when you really just want to write all streams + filters... + # default is to write protocolServer to support backwards compatibility + set writeProtocolServer $::true + + set command none + foreach arg [join $args] { + # just go ahead & remove the '-', makes things easier + set dash [expr [regsub -all {^-} $arg "" arg]?"-":""] + switch $command { + method - + groupId - + noDestroy { + set $command $arg + set command none + } + none { + switch $arg { + method { + set command method + } + protocolServer - + writeProtocolServer { + set writeProtocolServer $::true + } + noProtocolServer - + noWriteProtocolServer { + set writeProtocolServer $::false + } + verbose { + set verbose $::true + } + noVerbose - + noverbose { + set verbose $::false + } + groupId { + set command groupId + } + create { + set create $::true + } + nocreate - + noCreate { + set create $::false + } + destroy { + set destroy $::true + } + nodestroy - + noDestroy { + set destroy $::false + } + default { + errorMsg "Parameter not supported: $dash$arg" + set retCode $::TCL_ERROR + } + } + } + default { + errorMsg "Error in parameters: $args" + set retCode $::TCL_ERROR + } + } + } + debugMsg "method:$method, writeProtocolServer:$writeProtocolServer, verbose:$verbose, groupId:$groupId, create:$create, destroy:$destroy" + + if [llength $portList] { + if {$create} { + portGroup destroy $groupId + if [portGroup create $groupId] { + errorMsg "Error creating port group $groupId" + set retCode $::TCL_ERROR + } + } + + if {$retCode == 0} { + if [catch { + foreach tx_port $portList { + scan $tx_port "%d %d %d" tx_c tx_l tx_p + + if [portGroup add $groupId $tx_c $tx_l $tx_p] { + errorMsg "Error adding [getPortId $tx_c $tx_l $tx_p] to port group" + set retCode $::TCL_ERROR + } + } + + if {$verbose} { + ixPuts "--->Writing configuration to hardware..." + } + if [portGroup $method $groupId $writeProtocolServer] { + errorMsg "Error writing configuration for port group $groupId" + set retCode $::TCL_ERROR + } + + if {$verbose} { + ixPuts " done writing configuration to hardware..." + } + } error] { + errorMsg $error + set retCode $::TCL_ERROR + } + } + if {$destroy} { + if [portGroup destroy $groupId] { + errorMsg "Error destroying port group" + set retCode $::TCL_ERROR + } + } + } else { + errorMsg "No ports in port list/map" + set retCode $::TCL_ERROR + } + + ixProfile + return $retCode +} + + +######################################################################## +# Procedure: writeToHardwareAsChunks +# +# This command writes the ports, including speed, etc into hardware. It +# differs from writeConfigToHardware because this command writes all +# the phy as well as the configuration. +# +# Argument(s): +# PortArray either list of ports or array of ports +# action write | writeConfig +# args options include: +# -verbose +# -noVerbose +# -writeProtocolServer +# -noProtocolServer +# -groupId +# -create/-noCreate +# +######################################################################## +proc writeToHardwareAsChunks {PortArray action args} \ +{ + upvar $PortArray portArray + +############## +## funky workaround to avoid sending too much crap at once to the server +############## + set retCode $::TCL_OK + + set chunkSize [advancedTestParameter cget -portWriteChunkSize] + set allPorts [getAllPorts portArray] + set from 0 + + if {[llength $allPorts]} { + + if {$chunkSize > 0} { + set to [expr $chunkSize - 1] + } else { + set to [llength $allPorts] + } + set actionGroup [lrange $allPorts $from $to] + set myArgs [concat $args "-noVerbose"] + + while {$actionGroup != ""} { + if {$to >= [llength $allPorts]} { + set myArgs $args + } + if [writeToHardware actionGroup [join [list -method $action [join $myArgs]]]] { + set retCode $::TCL_ERROR + } + if {$chunkSize > 0} { + incr from $chunkSize + incr to $chunkSize + + set actionGroup [lrange $allPorts $from $to] + } else { + set actionGroup "" + } + } + } else { + errorMsg "No ports in port list/map" + set retCode $::TCL_ERROR + } + + return $retCode +} + + + +######################################################################## +# Procedure: writePortsToHardware +# +# This command writes the ports, including speed, etc into hardware. It +# differs from writeConfigToHardware because this command writes all +# the phy as well as the configuration. +# +# Argument(s): +# PortArray either list of ports or array of ports +# args options include: +# -verbose +# -noVerbose +# -writeProtocolServer +# -noProtocolServer +# -groupId +# -create/-noCreate +# +######################################################################## +proc writePortsToHardware {PortArray args} \ +{ + upvar $PortArray portArray + + return [writeToHardwareAsChunks portArray write [join $args]] +} + + +######################################################################## +# Procedure: writeConfigToHardware +# +# This command writes the port array into hardware +# +# Argument(s): +# PortArray either list of ports or array of ports +# args options include: +# -verbose +# -noVerbose +# -writeProtocolServer +# -noProtocolServer +# -groupId +# -create/-noCreate +# +######################################################################## +proc writeConfigToHardware {PortArray args} \ +{ + upvar $PortArray portArray + + return [writeToHardwareAsChunks portArray writeConfig [join $args]] +} + + +######################################################################## +# Procedure: resetSequenceIndex +# +# This command reset the sequence index +# +# Argument(s): +# PortArray either list of ports or array of ports +# +######################################################################## +proc resetSequenceIndex {TxRxArray { groupId 710 } {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set txRxList [getRxPorts txRxArray] + if [issuePortGroupCommand resetSequenceIndex txRxList noVerbose firstTimestamp $groupId $create $destroy] { + errorMsg "Error reseting the sequence index for port group" + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: resetPortSequenceIndex +# +# This command reset sequence index on a single port. +# +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc resetPortSequenceIndex {chassis lm port {FirstTimestamp ""} {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $FirstTimestamp firstTimestamp + set retCode $::TCL_OK + + set portList [list [list $chassis $lm $port]] + if [issuePortGroupCommand resetSequenceIndex portList noVerbose firstTimestamp $groupId $create $destroy] { + errorMsg "Error reseting the sequence index on port $chassis,$lm,$port" + set retCode $::TCL_ERROR + } + return $retCode +} + + +######################################################################## +# Procedure: loadPoePulse +# +# This command loads the poe pulse +# +# Argument(s): +# PortArray either list of ports or array of ports +# +######################################################################## +proc loadPoePulse {TxRxArray { groupId 710 } {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set txRxList [getAllPorts txRxArray] + if [issuePortGroupCommand loadPoePulse txRxList noVerbose firstTimestamp $groupId $create $destroy] { + errorMsg "Error loading the poe pulse for port group" + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: loadPortPoEPulse +# +# This command loads the poe pulse on a single port. +# +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc loadPortPoePulse {chassis lm port} \ +{ + set retCode $::TCL_OK + + set portList [list [list $chassis $lm $port]] + return [loadPoePulse portList] +} + + +######################################################################## +# Procedure: armPoeTrigger +# +# This command arms the poe trigger +# +# Argument(s): +# PortArray either list of ports or array of ports +# +######################################################################## +proc armPoeTrigger {TxRxArray { groupId 710 } {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set txRxList [getAllPorts txRxArray] + if [issuePortGroupCommand armPoeTrigger txRxList noVerbose firstTimestamp $groupId $create $destroy] { + errorMsg "Error arming the poe trigger for port group" + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: armPortPoeTrigger +# +# This command arms the poe trigger on a single port. +# +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc armPortPoeTrigger {chassis lm port} \ +{ + set retCode $::TCL_OK + + set portList [list [list $chassis $lm $port]] + return [armPoeTrigger portList] +} + + +######################################################################## +# Procedure: abortPoeArm +# +# This command aborts the poe arm +# +# Argument(s): +# PortArray either list of ports or array of ports +# +######################################################################## +proc abortPoeArm {TxRxArray { groupId 710 } {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set txRxList [getAllPorts txRxArray] + if [issuePortGroupCommand abortPoeArm txRxList noVerbose firstTimestamp $groupId $create $destroy] { + errorMsg "Error aborting the poe arm for port group" + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: abortPortPoeArm +# +# This command aborts the poe arm on a single port. +# +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc abortPortPoeArm {chassis lm port} \ +{ + set retCode $::TCL_OK + + set portList [list [list $chassis $lm $port]] + return [abortPoeArm portList] +} + + +######################################################################## +# Procedure: resetSequenceIndex +# +# This command reset the sequence index +# +# Argument(s): +# PortArray either list of ports or array of ports +# +######################################################################## +proc resetSequenceIndex {TxRxArray { groupId 710 } {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode 0 + + set txRxList [getRxPorts txRxArray] + if [issuePortGroupCommand resetSequenceIndex txRxList noVerbose firstTimestamp $groupId $create $destroy] { + errorMsg "Error reseting the sequence index for port group" + set retCode 1 + } + + return $retCode +} + + +######################################################################## +# Procedure: resetPortSequenceIndex +# +# This command reset sequence index on a single port. +# +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc resetPortSequenceIndex {chassis lm port {FirstTimestamp ""} {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $FirstTimestamp firstTimestamp + set retCode 0 + + set portList [list [list $chassis $lm $port]] + if [issuePortGroupCommand resetSequenceIndex portList noVerbose firstTimestamp $groupId $create $destroy] { + errorMsg "Error reseting the sequence index on port $chassis,$lm,$port" + set retCode 1 + } + if [info exists firstTimestamp] { + debugMsg "resetPortSequenceIndex on port $chassis,$lm,$port: firstTimestamp = $firstTimestamp" + } + return $retCode +} + + +######################################################################## +# Procedure: loadPoEPulse +# +# This command loads the poe pulse +# +# Argument(s): +# PortArray either list of ports or array of ports +# +######################################################################## +proc loadPoEPulse {TxRxArray { groupId 710 } {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set txRxList [getAllPorts txRxArray] + if [issuePortGroupCommand loadPoEPulse txRxList noVerbose firstTimestamp $groupId $create $destroy] { + errorMsg "Error loading the poe pulse for port group" + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: loadPortPoEPulse +# +# This command loads the poe pulse on a single port. +# +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc loadPortPoEPulse {chassis lm port {FirstTimestamp ""} {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $FirstTimestamp firstTimestamp + set retCode $::TCL_OK + + set portList [list [list $chassis $lm $port]] + if [issuePortGroupCommand loadPoEPulse portList noVerbose firstTimestamp $groupId $create $destroy] { + errorMsg "Error loading the poe pulse on port $chassis,$lm,$port" + set retCode $::TCL_ERROR + } + + return $retCode +} + + + + +######################################################################## +# Procedure: restartAutoNegotiation +# +# This command restarts auto negotiation OR restarts PPP negotiation +# +# Argument(s): +# TxRxArray List of ports or array of ports +# +# Results : 0 : No error found +# 1 : Error found +# +######################################################################## +proc restartAutoNegotiation {TxRxArray {groupId 710} {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set txRxList [getAllPorts txRxArray] + if [issuePortGroupCommand restartAutoNegotiate txRxList noVerbose firstTimestamp $groupId $create $destroy] { + errorMsg "Error restarting auto negotiation" + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: rebootLocalCpu +# +# This command reboots the local Cpu on a port list +# +# Argument(s): +# PortArray - either list of ports or array of ports +# +# Return: +# 0 if OK, 1 if action failed +# +######################################################################## +proc rebootLocalCpu {TxRxArray { groupId 710 } {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + set txRxList [getAllPorts txRxArray] + + foreach portItem $txRxList { + scan $portItem "%d %d %d" chassId cardId portId + + set retValue [port isValidFeature $chassId $cardId $portId portFeatureLocalCPU] + + switch $retValue { + 1 { + lappend cpuPortList [list $chassId $cardId $portId] + } + 0 { + errorMsg "!WARNING: portFeatureLocalCPU is not supported on port [getPortId $chassId $cardId $portId]" + continue + } + } + } + + if {[info exists cpuPortList]} { + logMsg "Rebooting port cpu, please wait ..." + if [issuePortGroupCommand rebootLocalCPU cpuPortList noVerbose firstTimestamp $groupId $create $destroy] { + errorMsg "Error rebooting local cpu for port group" + set retCode $::TCL_ERROR + } + } + + return $retCode +} + + +######################################################################## +# Procedure: rebootPortLocalCpu +# +# This command reboots the local Cpu on a single port. +# +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if action failed +# +######################################################################## +proc rebootPortLocalCpu {chassis lm port {groupId 710} {create create} {destroy destroy}} \ +{ + set retCode $::TCL_OK + + set portList [list [list $chassis $lm $port]] + return [rebootLocalCpu portList $groupId $create $destroy] +} + + +######################################################################## +# Procedure: simulatePhysicalInterfaceDown +# +# This command simulates physical interface down on a port list +# +# Argument(s): +# PortArray - either list of ports or array of ports +# +# Return: +# 0 if OK, 1 if action failed +# +######################################################################## +proc simulatePhysicalInterfaceDown {TxRxArray { groupId 710 } {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + set txRxList [getAllPorts txRxArray] + + foreach portItem $txRxList { + scan $portItem "%d %d %d" chassId cardId portId + + set retValue [port isValidFeature $chassId $cardId $portId portFeatureSimulateCableDisconnect] + + switch $retValue { + 1 { + lappend newPortList [list $chassId $cardId $portId] + } + 0 { + errorMsg "!WARNING: portFeatureSimulateCableDisconnect is not supported on port [getPortId $chassId $cardId $portId]" + continue + } + } + } + + if {[info exists newPortList]} { + logMsg "Simulating physical interface down, please wait ..." + if [issuePortGroupCommand simulatePhysicalInterfaceDown newPortList noVerbose firstTimestamp $groupId $create $destroy] { + errorMsg "Error simulating physical interface down for port group" + set retCode $::TCL_ERROR + } + } + + return $retCode +} + + +######################################################################## +# Procedure: simulatePortPhysicalInterfaceDown +# +# This command simulates physical interface down on a single port. +# +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if action failed +# +######################################################################## +proc simulatePortPhysicalInterfaceDown {chassis lm port {groupId 710} {create create} {destroy destroy}} \ +{ + set retCode $::TCL_OK + + set portList [list [list $chassis $lm $port]] + return [simulatePhysicalInterfaceDown portList $groupId $create $destroy] +} + + + +######################################################################## +# Procedure: simulatePhysicalInterfaceUp +# +# This command simulates physical interface up on a port list +# +# Argument(s): +# PortArray - either list of ports or array of ports +# +# Return: +# 0 if OK, 1 if action failed +# +######################################################################## +proc simulatePhysicalInterfaceUp {TxRxArray { groupId 710 } {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + set txRxList [getAllPorts txRxArray] + + foreach portItem $txRxList { + scan $portItem "%d %d %d" chassId cardId portId + + set retValue [port isValidFeature $chassId $cardId $portId portFeatureSimulateCableDisconnect] + + switch $retValue { + 1 { + lappend newPortList [list $chassId $cardId $portId] + } + 0 { + errorMsg "!WARNING: portFeatureSimulateCableDisconnect is not supported on port [getPortId $chassId $cardId $portId]" + continue + } + } + } + + if {[info exists newPortList]} { + logMsg "Simulating physical interface up, please wait ..." + if [issuePortGroupCommand simulatePhysicalInterfaceUp newPortList noVerbose firstTimestamp $groupId $create $destroy] { + errorMsg "Error simulating physical interface up for port group" + set retCode $::TCL_ERROR + } + } + + return $retCode +} + + +######################################################################## +# Procedure: simulatePortPhysicalInterfaceUp +# +# This command simulates physical interface up on a single port. +# +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if action failed +# +######################################################################## +proc simulatePortPhysicalInterfaceUp {chassis lm port {groupId 710} {create create} {destroy destroy}} \ +{ + set retCode $::TCL_OK + + set portList [list [list $chassis $lm $port]] + return [simulatePhysicalInterfaceUp portList $groupId $create $destroy] +} + + + +######################################################################## +# Procedure: startAtmOamTransmit +# +# This command starts the atm oam transmit +# +# Argument(s): +# PortArray either list of ports or array of ports +# +######################################################################## +proc startAtmOamTransmit {TxRxArray { groupId 710 } {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set txRxList [getAllPorts txRxArray] + if [issuePortGroupCommand startAtmOamTx txRxList noVerbose firstTimestamp $groupId $create $destroy] { + errorMsg "Error starting Atm Oam transmit for port group" + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: startPortAtmOamTransmit +# +# This command starts the atm oam transmit on a single port. +# +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc startPortAtmOamTransmit {chassis lm port} \ +{ + set retCode $::TCL_OK + + set portList [list [list $chassis $lm $port]] + return [startAtmOamTransmit portList] +} + + +######################################################################## +# Procedure: stopAtmOamTransmit +# +# This command starts the atm oam transmit +# +# Argument(s): +# PortArray either list of ports or array of ports +# +######################################################################## +proc stopAtmOamTransmit {TxRxArray { groupId 710 } {create create} {destroy destroy}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set txRxList [getAllPorts txRxArray] + if [issuePortGroupCommand stopAtmOamTx txRxList noVerbose firstTimestamp $groupId $create $destroy] { + errorMsg "Error stoping Atm Oam transmit for port group" + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: stopPortAtmOamTransmit +# +# This command starts the atm oam transmit on a single port. +# +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc stopPortAtmOamTransmit {chassis lm port} \ +{ + set retCode $::TCL_OK + + set portList [list [list $chassis $lm $port]] + return [stopAtmOamTransmit portList] +} + + +######################################################################## +# Procedure: setScheduledTransmitTime +# +# This command builds a port group, sets/clears the scheduled transmit +# time and destroys the port group when it's done +# +######################################################################## +proc setScheduledTransmitTime {TxRxArray duration {groupId 710}} \ +{ + upvar $TxRxArray txRxArray + + set txRxList [getTxPorts txRxArray] + set object [list noCommand] + set lastTimestamp "" + + if { $duration } { + return [issuePortGroupMethod txRxList lastTimestamp -method setScheduledTransmitTime -duration $duration \ + -commandList $object -groupId $groupId -noVerbose -create -destroy ] + } else { + return [issuePortGroupMethod txRxList lastTimestamp -method clearScheduledTransmitTime \ + -commandList $object -groupId $groupId -noVerbose -create -destroy ] + + } +} + +######################################################################## +# Procedure: setAutoInstrumentationMode +# +# This command sets all the RX ports in the list or array to packet +# group mode +# +# Arguments(s): +# TxRxArray - list or array of RX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc setAutoDetectInstrumentationMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set portList [getRxPorts txRxArray] + set validPortList [list] + + foreach portItem $portList { + scan $portItem "%d %d %d" chassId cardId portId + + if {[port isValidFeature $chassId $cardId $portId $::portFeatureAutoDetectRx]} { + if {[port get $chassId $cardId $portId]} { + errorMsg "Error getting port [getPortId $chassId $cardId $portId]." + set retCode $::TCL_ERROR + break + } + + if {[port isValidFeature $chassId $cardId $portId $::portFeatureRxWidePacketGroups]} { + set receiveMode [expr ($::portRxModeWidePacketGroup | $::portRxDataIntegrity | $::portRxSequenceChecking)] + } else { + set receiveMode [expr ($::portPacketGroup | $::portRxDataIntegrity | $::portRxSequenceChecking)] + } + + port config -receiveMode $receiveMode + port config -enableAutoDetectInstrumentation $::true + + if {[port set $chassId $cardId $portId]} { + errorMsg "Error setting port [getPortId $chassId $cardId $portId]." + set retCode $::TCL_ERROR + break + } + lappend validPortList [list $chassId $cardId $portId] + } + } + + if {$write == "write" && [llength $validPortList]} { + logMsg "Changing RX port mode to [getTxRxModeString $receiveMode RX] - please wait ..." + if [writePortsToHardware validPortList -noProtocolServer] { + set retCode 1 + } + set retCode [checkLinkState validPortList] + } else { + logMsg "Configuring RX port mode to [getTxRxModeString $receiveMode RX]" + } + + return $retCode +} + +######################################################################## +# Procedure: enablePortIntrinsicLatencyAdjustment +# +# This command enables the Intrinsic Latency Adjustment on the ports +# that support the feature +# +# Arguments(s): +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc enablePortIntrinsicLatencyAdjustment { chassId cardId portId enable {write nowrite}} \ +{ + set portList [list [list $chassId $cardId $portId]] + + return [enableIntrinsicLatencyAdjustment portList $enable $write] +} + + +######################################################################## +# Procedure: enableIntrinsicLatencyAdjustment +# +# This command enables the Intrinsic Latency Adjustment on the ports +# that support the feature +# +# Arguments(s): +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc enableIntrinsicLatencyAdjustment { TxRxArray enable {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set portList [getAllPorts txRxArray] + set validPortList [list] + + if { $enable } { + set messageStr "enabling" + } else { + set messageStr "disabling" + } + + foreach portItem $portList { + scan $portItem "%d %d %d" chassId cardId portId + + set retCode [ixUtils enableIntrinsicLatencyAdjustment $chassId $cardId $portId $enable] + if { $retCode != $::TCL_OK } { + logMsg "Error $messageStr of intrinsic latency adjustment for $chassId $cardId $portId" + } else { + lappend validPortList [list $chassId $cardId $portId] + } + } + + if { [llength $validPortList] } { + if {$write == "write" } { + logMsg "Enabling intrinsic latency adjustment on port(s) - please wait ..." + if {[writeConfigToHardware validPortList -noProtocolServer]} { + set retCode $::TCL_ERROR + } + } else { + logMsg "Configured $messageStr of intrinsic latency adjustment for port $chassId $cardId $portId" + } + } else { + logMsg "No valid ports in the list for intrinsic latency feature." + } + + + return $retCode +} + +######################################################################## +# Procedure: isIntrinsicLatencyAdjustmentEnabled +# +# This command enables the Intrinsic Latency Adjustment on the ports +# that support the feature +# +# Arguments(s): +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc isIntrinsicLatencyAdjustmentEnabled { chassId cardId portId } \ +{ + return [ixUtils isIntrinsicLatencyAdjustmentEnabled $chassId $cardId $portId] +} + + diff --git a/dep/tclclient/ixTcl1.0/Generic/addressTableUtils.tcl b/dep/tclclient/ixTcl1.0/Generic/addressTableUtils.tcl new file mode 100644 index 00000000..188ca8a8 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/addressTableUtils.tcl @@ -0,0 +1,117 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: addressTableUtils.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 04/05/2000 DS +# +# Description: This file contains common procs used for ipAddressTable +# manipulation +# +################################################################################## + + +######################################################################## +# Procedure: ipAddressSetDefault +# +# This command sets the ipAddressTable & iAddressTableItem to defaults +# +# +######################################################################## +proc ipAddressSetDefault {} \ +{ + ipAddressTable setDefault + ipAddressTableItem setDefault +} + + +######################################################################## +# Procedure: updateIpAddressTable +# +# This command updates the ipAddressTable w/current ip & port objects +# +# Arguments(s): +# chassis +# card +# port +# +######################################################################## +proc updateIpAddressTable {chassis card port {write nowrite}} \ +{ + set retCode 0 + + if [ip get $chassis $card $port] { + errorMsg "Error getting ip on port $chassis $card $port" + set retCode 1 + } + + if [port get $chassis $card $port] { + errorMsg "Error getting port $chassis $card $port" + set retCode 1 + } + + ipAddressTableItem config -fromIpAddress [ip cget -sourceIpAddr] + ipAddressTableItem config -gatewayIpAddress [ip cget -destDutIpAddr] + ipAddressTableItem config -fromMacAddress [port cget -MacAddress] + ipAddressTableItem config -numAddresses [port cget -numAddresses] + + if [ipAddressTableItem set] { + errorMsg "Error setting ipAddressTableItem" + set retCode 1 + } + + if [ipAddressTable addItem] { + errorMsg "Error adding item to ipAddressTable" + set retCode 1 + } + + ipAddressTable config -defaultGateway [ipAddressTableItem cget -gatewayIpAddress] + if [ipAddressTable set $chassis $card $port] { + errorMsg "Error setting ipAddressTable on port $chassis $card $port" + set retCode 1 + } + + if {$write == "write" && $retCode == 0} { + if [protocolServer write $chassis $card $port] { + errorMsg "Error writing protocol server on $chassis $card $port" + set retCode 1 + } + } + + return $retCode +} + + diff --git a/dep/tclclient/ixTcl1.0/Generic/calculate.tcl b/dep/tclclient/ixTcl1.0/Generic/calculate.tcl new file mode 100644 index 00000000..4dbdcdf2 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/calculate.tcl @@ -0,0 +1,527 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: calculate.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 12-30-1998 DS +# +# Description: This file contains common procs used for calculating things +# +################################################################################## + + +######################################################################## +# Procedure: mpincr +# +# This command uses mpexpr stuff to overlay the Tcl incr for use w/ +# 64-bit numbers. +# +######################################################################## +proc mpincr {Value {incrAmt 1}} \ +{ + upvar $Value value + + set value [mpexpr $value + $incrAmt] + return $value +} + +######################################################################## +# Procedure: calculatePercentLossExact +# +# This command calculates the percent loss based on tx/rx frames - +# returns *exact* percent loss (not prettied up) +# +# Arguments(s): +# txFrames - number of transmitted frames +# rxFrames - number of received frames +# +######################################################################## +proc calculatePercentLossExact {txFrames rxFrames} \ +{ + set loss 0.00 + + if [catch {mpexpr double($txFrames-$rxFrames)/$txFrames * 100.} loss] { + set loss 100.00 + } + + if {$loss < 0.} { + set loss 0.00 + } + + return $loss +} + + +######################################################################## +# Procedure: calculatePercentLoss +# +# This command calculates the percent loss based on tx/rx frames & +# returns a formatted string of the form "%6.2f" +# +# Arguments(s): +# txFrames - number of transmitted frames +# rxFrames - number of received frames +# +######################################################################## +proc calculatePercentLoss {txFrames rxFrames} \ +{ + set loss [calculatePercentLossExact $txFrames $rxFrames] + set percentLossFormat [advancedTestParameter cget -percentLossFormat] + set loss [formatNumber $loss $percentLossFormat] + + # check to see if we received more frames than we transmitted + # Since 32 bit counter (mpexpr) is used here, if we get a 32 bit long number (in binary) whose most + # significant bit is 1, it will be recognized as a negtive number. So we use regexp + # to determinde wheather $txFrames - $rxFrames is a negative number. + if { [regexp {^-[0-9]+$} [mpexpr $txFrames - $rxFrames]] } { + set loss "$loss - NOTE: Received more frames than tx'd" + } + #if {[mpexpr $txFrames-$rxFrames] < 0} { + # set loss "$loss - NOTE: Received more frames than tx'd" + #} + + return $loss +} + + +######################################################################## +# Procedure: calculatePercentThroughput +# +# This command calculates the percent throughput based on tx/rx frames & +# returns a formatted string of the form "%6.2f" +# +# Arguments(s): +# tputRate - throughput rate +# maxRate - max throughput rate +# +######################################################################## +proc calculatePercentThroughput {tputRate maxRate} \ +{ + set thruput 0.00 + + if [catch {mpexpr ($tputRate*100.)/$maxRate} thruput] { + set thruput 0. + } + + return [format "%6.2f" $thruput] +} + + +######################################################################## +# Procedure: calculateDuration +# +# This command calculates the approximate transmit time +# +# Arguments(s): +# numTxFrames - total number of frames to transmit +# frameRate - transmit rate +# numFrames - the 'numFrame' value set to stream config -numFrames +# loopcount - the loopcount +# +######################################################################## +proc calculateDuration {numTxFrames frameRate {numFrames 1} {loopcount 1}} \ +{ + set duration 0 + + if {$frameRate > 0} { + if [catch {mpexpr $numTxFrames/$frameRate} duration] { + if [catch {mpexpr $numFrames/$frameRate * $loopcount} duration] { + logMsg "******* WARNING:::: duration is longer than 4294967295 seconds!!!" + set duration 4294967295 + } + } + } else { + logMsg "****** WARNING:::: frameRate is set to 0, duration set to 0" + } + + return $duration +} + + +######################################################################## +# Procedure: calculateTotalBursts +# +# This command calculates the number of bursts for the specified duration, +# +# Arguments(s): +# framerate - in pps +# ifg - in nanoseconds +# burstsize - in fps +# ibg - inter burst gap, in nanoseconds +# duration - duration to calculate number of bursts over +# +# Returns: +# total number of bursts per this duration +# +######################################################################## +proc calculateTotalBursts {framerate ifg burstsize ibg {duration 1}} \ +{ + set megabits 1000000. + set nanoseconds 1000000000. + + if [catch {mpexpr $burstsize*((1./$framerate)* $nanoseconds)} packetTime] { + errorMsg "****** Error calculating packet time: $packetTime, packet time set to 0" + set packetTime 0 + } + + if [catch {mpexpr round(($duration*$nanoseconds)/(($packetTime - $ifg) + $ibg))} numBursts] { + errorMsg "****** Error calculating numBursts: $numBursts, numBursts set to 0" + set numBursts 0 + } + + return $numBursts +} + + + +######################################################################## +# Procedure: calculateAvgLatency +# +# This command calculates the average latency of the latencies in the +# array +# +# Arguments(s): +# LatencyArray - array containing latency values +# +######################################################################## +proc calculateAvgLatency {LatencyArray} \ +{ + if [info exists LatencyArray] { + upvar $LatencyArray latencyArray + } else { + return 0 + } + + set avgLatency 0 + + set count [llength [array names latencyArray]] + foreach txMap [lsort [array names latencyArray]] { + if {$latencyArray($txMap) == 0} { + mpincr count -1 + } + } + + if {$count > 0} { + foreach txMap [lsort [array names latencyArray]] { + if {$latencyArray($txMap) != 0} { + if [catch {mpexpr round($avgLatency + ((double($latencyArray($txMap)))/$count))} temp] { + logMsg "******* ERROR::: Port $txMap - $temp" + } else { + set avgLatency $temp + } + } + } + } + + return $avgLatency +} + + +######################################################################## +# Procedure: calculateLoopCounterFromTxFrames +# +# This command calculates the loopcounter based on the numFrame parameter +# used for stream. Note that numFrame is a 24-bit number; if numFrame is +# greater than 24 bits than loopcount must be used in conjuction. +# +# Arguments(s): +# totalFrames - total number of frames to transmit +# +# Return: +# loopcount +# +######################################################################## +proc calculateLoopCounterFromTxFrames {totalFrames} \ +{ + upvar $totalFrames numFrames + + set loopcount 1 + + if [catch {expr $numFrames & 0xffffff}] { + # bigger than 32 bit number + set loopcount 1 + set numLength [string length $numFrames] + while {$numLength > 9} { + set numFrames [string range $numFrames 0 [expr $numLength - 2]] + set numLength [string length $numFrames] + mpincr loopcount + } + mpincr loopcount -1 + set loopcount [mpexpr round(pow(10,$loopcount))] + } else { + set temp $numFrames + while {[mpexpr $temp & 0xffffff] != $temp && $loopcount < 10} { + set temp [mpexpr $numFrames >> 1] + mpincr loopcount + } + set numFrames [mpexpr $numFrames/$loopcount] + } + + return $loopcount +} + + +######################################################################## +# Procedure: calculateStreamNumFrames +# +# This command calculates the stream numFrames value. Assumes a max +# numFrame value of 0xffffffff. +# +# Arguments(s): +# framerate - fps +# duration - seconds +# +# Return: +# numFrames; throws an exception if the duration gets modified. +# +######################################################################## +proc calculateStreamNumFrames {framerate Duration {maxNumFrames 0xffffffff}} \ +{ + upvar $Duration duration + + set numFrames [mpexpr $framerate * $duration] + if {[isNegative [mpexpr $maxNumFrames - $numFrames]]} { + set duration [mpexpr $maxNumFrames/$framerate] + + errorMsg "Error - selected duration $duration is too long - change to max duration of $duration" + set numFrames [mpexpr $framerate * $duration] + + return -code error -errorinfo $numFrames + } + + return $numFrames +} + + +######################################################################## +# Procedure: getTransmitTime +# +# This command calculates the total transmit time for the current stream +# configuration from the pulse startTx time - the last frame captured. +# If no frames were captured, it returns the original duration. +# +# Arguments(s): +# PortArray - array of ports +# originalDuration - original duration of test +# +# Returns: +# test duration calculated from captured frame or original duration +# if no frames captured. +# +######################################################################## +proc getTransmitTime {PortArray originalDuration {DurationArray ""} {Warnings ""}} \ +{ + upvar $PortArray portArray + upvar $DurationArray durationArray + upvar $Warnings warnings + + set errorCode 0 + set duration $originalDuration + catch {unset durationArray} + + requestStats portArray + + # get the test duration + foreach txMap [array names portArray] { + scan $txMap "%d,%d,%d" tx_c tx_l tx_p + + # initialize array + set durationArray($tx_c,$tx_l,$tx_p) $originalDuration + + if [statList get $tx_c $tx_l $tx_p] { + errorMsg "Error getting Tx statistics for [getPortId $tx_c $tx_l $tx_p]" + set errorCode 1 + continue + } + if [catch {mpexpr [statList cget -transmitDuration]/1000000000.} tempDuration] { + if [catch {getDurationFromCapture portArray $tx_c $tx_l $tx_p $duration} tempDuration] { + set errorCode 1 + continue + } + + } + + if {$tempDuration < $originalDuration} { + set duration $originalDuration + } + + lappend durationList $duration + set durationArray($tx_c,$tx_l,$tx_p) $duration + } + + if {($errorCode == 1) && ($warnings != "")} { + set warnings "\n\n***** Warning *****\n\ + Instrumented packets were dropped by the DUT and the rate\n\ + cannot be reliably measured on one or more ports.\n\n\ + To reliably measure rate, change the tolerance for dropped\n\ + packets to zero.\n" + } + + if [info exists durationList] { + set duration [lindex [lnumsort $durationList] 0] + } + if [info exists durationArray] { + debugMsg "getTransmitTime: [array get durationArray]" + } + + return $duration +} + + +######################################################################## +# Procedure: getDurationFromCapture +# +# This command calculates the total transmit time using the capture buffer. +# If no frames were captured, it returns the original duration. +# +# Arguments(s): +# originalDuration - original duration of test +# +# Returns: +# test duration calculated from captured frame or original duration +# if no frames captured. +# +######################################################################## +proc getDurationFromCapture {PortArray tx_c tx_l tx_p originalDuration} \ +{ + upvar $PortArray portArray + + set maxDuration 0 + foreach rxMap $portArray($tx_c,$tx_l,$tx_p) { + scan $rxMap "%d %d %d" rx_c rx_l rx_p + + if [capture get $rx_c $rx_l $rx_p] { + set errorMessage "Error getting capture data on [getPortId $rx_c $rx_l $rx_p]" + set errorCode 1 + continue + } + set numCapPackets [capture cget -nPackets] + + # get first packet + if [captureBuffer get $rx_c $rx_l $rx_p] { + set errorMessage "Error getting capture buffer for [getPortId $rx_c $rx_l $rx_p]" + set errorCode 1 + continue + } + + if [captureBuffer getframe 1] { + set errorMessage "Error getting frame from capture buffer for [getPortId $rx_c $rx_l $rx_p]" + set errorCode 1 + continue + } + set firstTimeStamp [captureBuffer cget -fir] + debugMsg "firstTimeStamp:$firstTimeStamp" + debugMsg "ixgFirstTimeStamp:$::ixgFirstTimeStamp" + + if [captureBuffer get $rx_c $rx_l $rx_p $numCapPackets $numCapPackets] { + set errorMessage "Error getting capture buffer for [getPortId $rx_c $rx_l $rx_p]" + set errorCode 1 + continue + } + if [captureBuffer getframe 1] { + set errorMessage "Error getting frame from capture buffer for [getPortId $rx_c $rx_l $rx_p]" + set errorCode 1 + continue + } + set duration [mpexpr ([captureBuffer cget -timestamp] - $firstTimeStamp)/1000000000.] + debugMsg "duration:$duration" + + if {$duration < $originalDuration || $numCapPackets < [mpexpr $originalDuration * 2]} { + set duration $originalDuration + } + # get the max transmit time for TX ports that have more than one RX ports + if {$duration > $maxDuration} { + set maxDuration $duration + } + } + + if {$errorCode == 1} { + return -code error -errorinfo $errorMessage + } else { + return $maxDuration + } +} + +namespace eval ixMath { + +######################################################################## +# Procedure: min +# +# This command calculates the minimum value +# +# Arguments(s): +######################################################################## +proc min {x y} \ +{ + return [mpexpr ($x < $y) ? $x : $y ] +} + + +######################################################################## +# Procedure: max +# +# This command calculates the maximum value +# +# Arguments(s): +######################################################################## +proc max {x y} \ +{ + return [mpexpr ($x > $y) ? $x : $y ] +} + +};# ixMath Namespace end + +######################################################################## +# Procedure: maxArray +# +# This command calculates the maximum value of an array. +# +# Arguments(s): +######################################################################## +proc maxArray {NumArray} \ +{ + upvar $NumArray numArray + + set maxNum 0 + for {set index 0} {$index < [llength [array names numArray]]} {incr index} { + set item [lindex [lnumsort [array names numArray]] $index] + set maxNum [ixMath::max $maxNum $numArray($item)] + } + + return $maxNum +} + + diff --git a/dep/tclclient/ixTcl1.0/Generic/chassisUtils.tcl b/dep/tclclient/ixTcl1.0/Generic/chassisUtils.tcl new file mode 100644 index 00000000..5db3a95f --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/chassisUtils.tcl @@ -0,0 +1,239 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: chassisUtils.tcl +# Copyright © IXIA. +# All Rights Reserved. +# +# Revision Log: +# 10/16/2000 DS Genesis +# +# Description: This file contains general chassis management/connection procs. +# +################################################################################## + + +######################################################################################## +# Procedure: connectToChassis +# +# Description: Attempts to connect to all chassis given in the list +# +# Arguments: chassisList - A list of chassis names +# cableLength - Optional. A corresponding list of cable lengths for the given chassis +# chassisIdList - Optional. A corresponding list of chassis id numbers for the given chassis +# chassisSeqList - Optional. A corresponding list of sequence numbers for the given chassis +# +# Returns: A return code of 0 for success and different integers representing errors +######################################################################################## +proc connectToChassis {chassisList {cableLengthList cable3feet} {chassisIdList ""} {chassisSeqList ""}} \ +{ + set retCode 0 + + # If either of the id list or sequence list contains less items than the chassis list, + # we will recreate these lists on our own starting from the number 1 until there are + # enough items in the lists. + set numChassis [llength $chassisList] + if {[llength $chassisIdList] < $numChassis} { + catch {unset chassisIdList} + for {set id 1} {$id <= $numChassis} {incr id} { + lappend chassisIdList $id + } + } + + if {[llength $chassisSeqList] < $numChassis} { + catch {unset chassisSeqList} + for {set sequence 1} {$sequence <= $numChassis} {incr sequence} { + lappend chassisSeqList $sequence + } + } + + # If the cable length list contains only one item, then that value will be used for all lengths. + # If the list is greater than one, but less than the number of chassis, then cable3feet will be used + # to complete the list. + if {([llength $cableLengthList] == 1) && ($numChassis != 1)} { + set cableLength $cableLengthList + catch {unset cableLengthList} + foreach item $chassisList { + lappend cableLengthList $cableLength + } + } elseif {[llength $cableLengthList] < $numChassis} { + while {[llength $cableLengthList] < $numChassis} { + lappend cableLengthList "cable3feet" + } + } + + foreach chassis $chassisList chassisId $chassisIdList sequence $chassisSeqList cableLength $cableLengthList { + logMsg "Connecting to Chassis $chassisId: $chassis ..." + + # Connect may not work the first time. So try a few times until a connect succeeds + set maxConnectRetries [advancedTestParameter cget -maxConnectRetries] + if {$maxConnectRetries < 1} { + set maxConnectRetries 1 + } + + set retAddCode 1 + for {set connectNum 1} {$connectNum <= $maxConnectRetries && $retAddCode != 0} {incr connectNum} { + + set connectChassisFlag [getConnectChassisFlag] + + if { ![string compare $connectChassisFlag "stop"] } { + # + # Progress dialog is canceled. Connecting to chassis should stop. + # + setConnectChassisFlag "continue" + set retAddCode 4 + break + } + + set retAddCode [chassis add $chassis] + switch $retAddCode " + $::TCL_OK { + continue + } + $::ixTcl_versionMismatch { + # if it is a version mismatch, do not bother going any further... + logMsg \"Error: Version mismatch between IxServer and Tcl Client\" + ixPuts $::ixErrorInfo + chassis del $chassis + break + } + $::ixTcl_HardwareConflict { + # if it is a serial number conflict, do not bother going any further... + chassis del $chassis + logMsg \"Error: Hardware conflict detected. Please call customer support!\" + break + } + default { + chassis del $chassis + logMsg \"Error connecting to chassis. Retrying $connectNum of $maxConnectRetries retries ..\" + after 20 + update + } + " + } + + # dump out here if there was an error connecting to one of the chassis + switch $retAddCode " + $::TCL_OK { + } + $::TCL_ERROR { + logMsg \"Error connecting to chassis $chassis\" + return $retAddCode + } + $::ixTcl_chassisTimeout { + logMsg \"Timeout connecting to chassis $chassis. Try Again!\" + return $retAddCode + } + 4 { + logMsg \"Connection was interrupted by user!\" + return $retAddCode + } + default { + return $retAddCode + } + " + + chassis config -name $chassis + chassis config -id $chassisId + chassis config -sequence $sequence + chassis config -cableLength $cableLength + + if {[chassis set $chassis]} { + errorMsg "Error setting chassis $chassis" + return $::TCL_ERROR + } + } + + # after connecting to all chassis, broadcast the topology of each chassis + # to all other chassis + chassisChain broadcastTopology + + # now we need to verify that it is a valid chain (ie., there is at least one master) + if {[chassisChain validChain]} { + errorMsg "Error: Chassis chain is not valid - check for master chassis in chain" + set retCode $::ixTcl_invalidChassisChain + } + + return $retCode +} + + +######################################################################################## +# Procedure: setConnectChassisFlag +# +# Description: Set the value that indicates whether connecting to chassis should continue. +# +# Argument(s): value - "continue" or "stop". +# +# Return: Nothing +# +######################################################################################## +proc setConnectChassisFlag {value} \ +{ + global ixgChassisContinueFlag + + switch $value { + "continue" - + "stop" { + set ixgChassisContinueFlag $value + } + default { + set ixgChassisContinueFlag "continue" + } + } +} + + +######################################################################################## +# Procedure: getConnectChassisFlag +# +# Description: Get the value that indicates whether connecting to chassis should continue. +# +# Argument(s): None. +# +# Return: "continue" or "stop". +# +######################################################################################## +proc getConnectChassisFlag {} \ +{ + global ixgChassisContinueFlag + + if { [info exists ixgChassisContinueFlag] } { + set retCode $ixgChassisContinueFlag + } else { + set retCode "continue" + } + + return $retCode +} + + diff --git a/dep/tclclient/ixTcl1.0/Generic/clientUtils.tcl b/dep/tclclient/ixTcl1.0/Generic/clientUtils.tcl new file mode 100644 index 00000000..a8be073a --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/clientUtils.tcl @@ -0,0 +1,319 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: clientUtils.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# Date Author Comments +# ----------- ------------------- -------------------------------------------- +# 10/25/2000 ds initial release +# +# Description: This file contains utilities for the client side of the Tcl proxy +# +################################################################################## + + +######################################################################################## +# Procedure: clientOpen +# +# Description: Open a connection to the ixTclServer. +# +# Input: server: host name of server +# port: port id of service +# +# Output: socket handle +# +######################################################################################## +proc clientOpen {host port} \ +{ + if [catch {socket $host $port} socketId] { + errorMsg "Error: $socketId" + set socketId {} + } + + return $socketId +} + + +######################################################################################## +# Procedure: clientClose +# +# Description: Close connection from the client side. +# +# Input: socketId: client-side socket +# +# Output: 0 if successful +# 1 if error while attempting to close socket +# +######################################################################################## +proc clientClose {socketId} \ +{ + set retCode 0 + + if [catch {close $socketId}] { + set retCode 1 + } + + return $retCode +} + + +######################################################################################## +# Procedure: clientSend +# +# Description: Send a command from the client side. +# +# Input: socketId: client-side socket +# args: TCL command to evaluate +# +# Returns: Success: TCL Return result +# Failure: {} +# +# Remarks: TCL procs can embed i/o. +# +######################################################################################## +proc clientSend {socketId args} \ +{ + set retCode 1 + set retResult 0 + + # + # send data over the socket + # + set buf [lindex $args 0] + + # + # Return buffer is formatted as follows: + # + # 0 to final lf/cr -> Tcl Standard Output + # final character -> Tcl Return Code (TCL_OK, TCL_ERROR) + # + if [catch { puts $socketId $buf ; + flush $socketId ; + # + # read the reply + # the reply may have the format + # sOutput/r/nsTclResult/r/n -- i/o output followed by TCL result + # /r/n -delimited + # sTclresult/r/nsTclResultCode -- simple TCL result + # null -- no TCL result available + + vwait tclServer::buffer + set retBuffer $tclServer::buffer + + set indexOfLastCrlf [string last "\r\n" $retBuffer] + if {$indexOfLastCrlf != -1 } { + set lenBuffer [string length $retBuffer] + set indexOfPenultimateCrlf [string last "\r\n" [string range $retBuffer 0 [expr $indexOfLastCrlf -1]]] + if {$indexOfPenultimateCrlf != -1 } { + set retResult [string range $retBuffer [expr $indexOfPenultimateCrlf + 2] $lenBuffer] + } else { + set length [string length $retBuffer] + set retCode [string index $retBuffer [incr length -1]] + set retResult [string range $retBuffer 0 [incr length -1]] + } + } else { + set length [string length $retBuffer] + set retCode [string index $retBuffer [incr length -1]] + set retResult [string range $retBuffer 0 [incr length -1]] + } + } + ] { + errorMsg $::errorInfo + tclServer::disconnectTclServer + set retResult 1 + } + + # + # Force an error if the command returned TCL_ERROR. + # Can't use the constant TCL_ERROR here since it is not defined at + # this point in execution. + # + if {$retCode == 1} { + set retCommand [list error $retResult $retResult] + } else { + set retCommand [list return $retResult] + } + + eval $retCommand +} + +######################################################################################## +# Procedure: remoteDefine +# +# Description: Create a proc to proxy over a ixTclHal command. +# +# Input: commandList +# +# Output: 0 if successful +# 1 if error +# +######################################################################################## +proc remoteDefine { commandList } \ +{ + foreach procName $commandList { + eval [format "proc %s {args} \ + {\ + global ixTclSvrHandle; \ + if \[catch { eval \"clientSend \$ixTclSvrHandle {%s \$args}\" } result\] {error \$result \$result}; \ + if {\$result != \"\"} {if \[catch { eval \"clientSend \$ixTclSvrHandle {set ixErrorInfo}\" } ::ixErrorInfo] {error \$::ixErrorInfo \$::ixErrorInfo}}; \ + return \$result \ + }" \ + $procName $procName] + } + return 0 +} + +######################################################################################## +# Procedure: getConstantsValue +# +# Description: Get the list of constants from ixTclServer. +# +# Input: serverSocket +# +# Output: 0 if successful +# 1 if error +# +######################################################################################## +proc getConstantsValue {serverSocket} \ +{ + set retCode 0 + + set constList [clientSend $serverSocket {array get ixConstants}] + if {[llength $constList] > 0} { + foreach {constName constVal} $constList { + global $constName + # The catch prevents the error message from flowing back on windows + catch {set $constName $constVal} + } + } else { + set retCode 1 + } + + return $retCode +} + + +######################################################################################## +# Procedure: ixMasterSet +# +# Description: No clue what this is here for, but I'm leaving it for backwards compatibility +# +# Input: +# +######################################################################################## +proc ixMasterSet {name element op} \ +{ + upvar ${name}($element) master + tclServer::connectToTclServer $master errMsg +} + +######################################################################################## +# Procedure: redefineCommand +# +# Description: Redefine the specified command to make its methods import and export work +# for UNIX client. +# +# Input: command - name of the command to be redefined. +# +######################################################################################## +proc redefineCommand {command} \ +{ + set commandOld ${command}Old + + if { [info command $command] != "" } { + if { [info command $commandOld] == "" } { + rename $command $commandOld + } + } + eval [format "proc %s {args} \ + { \ + set cmdLine %s ; \ + set path \[file dirname \[lindex \$args 1\]\] ;\ + set fileName \[file tail \[lindex \$args 1\]\] ;\ + if { \$path == \".\" } { \ + append cmdLine \" \$args\"; set path \$fileName } else { \ + append cmdLine \" \[lindex \$args 0\] \$fileName \[lindex \$args 2\] \[lindex \$args 3\] \[lindex \$args 4\] \" }\ + + switch \[lindex \$args 0\] { \ + import { \ + doFileTransfer \"put\" \[lindex \$args 1\] \$fileName ; \ + eval \$cmdLine; \ + } \ + export { \ + set retCode \[eval \$cmdLine\]; \ + if \{\$retCode == 0\} \{ \ + doFileTransfer \"get\" \$fileName \[lindex \$args 1\]; \ + fileTransferClient::deleteFile \[tclServer::getTclServerName\] 4500 \$fileName \ + + \} else \{ \ + return \$retCode; \ + \} \ + } \ + default { \ + eval \$cmdLine; \ + } \ + } \ + }" \ + $command $commandOld] +} + +######################################################################################## +# Procedure: doFileTransfer +# +# Description: Transfer files between client and chassis. +# +# Input: action - Has to be either put or get. +# +# Unix doesn't need the full path of the file, that is why it is based on the direction +# if it is put or get, we have to pass in two filenames, one for source the other for +# destination +# filename1 - Name of the file to be transfered. +# filename2 - Name of the file to be transfered. +# +######################################################################################## +proc doFileTransfer {action filename1 filename2 {port 4500}} \ +{ + set retCode 1 + + if [tclServer::isTclServerConnected] { + set retCode [fileTransferClient::${action}File [tclServer::getTclServerName] $port "$filename1" "$filename2"] + } + + return $retCode +} + + + diff --git a/dep/tclclient/ixTcl1.0/Generic/constants.tcl b/dep/tclclient/ixTcl1.0/Generic/constants.tcl new file mode 100644 index 00000000..d8bdc441 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/constants.tcl @@ -0,0 +1,131 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: constants.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 12-30-1998 Hardev Soor +# +# Description: This file contains common constants +# +################################################################################## + +# ----------------------------------------------------- +# Hardcoded from HAL so that the connection TclSvrConnect +# can be made after the package require -> if using UNIX +# ----------------------------------------------------- + +# protocol mapping +set kProtocol(0) mac +set kProtocol(4) ip +set kProtocol(5) udp +set kProtocol(6) tcp +set kProtocol(7) ipx +set kProtocol(31) ipV6 + +# IP address class mapping +set kIpAddrClass(0) classA +set kIpAddrClass(1) classB +set kIpAddrClass(2) classC +set kIpAddrClass(3) classD +set kIpAddrClass(4) noClass + + +# some generic frame constants +set kFirSize 6 +set kCrcSize 4 +set kUdfSize 4 + + +# duplex mode mapping +set kDuplexMode(0) half +set kDuplexMode(1) full + +# levels of storing results +set kResultLevel(test) test +set kResultLevel(iter) iter +set kResultLevel(port) port +set kResultLevel(portCat) portCat +set kResultLevel(portItem) portItem + +# IPX constants +set kSapOperation(request) 1 +set kSapOperation(response) 2 ;# General Service Response +set kSapOperation(getNearestServerRequest) 3 +set kSapOperation(getNearestServerResponse) 4 +set kSapServiceType(unknown) {00 00} +set kSapServiceType(printQueue) {00 03} +set kSapServiceType(fileServer) {00 04} +set kSapServiceType(jobServer) {00 05} +set kSapServiceType(printServer) {00 07} +set kSapServiceType(archiveServer) {00 09} +set kSapServiceType(remoteBridgeServer) {00 24} +set kSapServiceType(advertisingPrintServer) {00 47} +set kRIPXOperation(request) 1 +set kRIPXOperation(response) 2 +set kBroadcastMacAddress {ff ff ff ff ff ff} +set kSapSocket 0x452 +set kRipSocket 0x453 +set kHeaderLength(ip) 20 +set kHeaderLength(ipV6) 40 +set kHeaderLength(udp) 8 +set kHeaderLength(rip) 4 +set kHheaderLength(icmp) 8 +set kIPXHeaderLength 30 + +if {![info exists TCL_OK]} { + set TCL_OK 0 + set TCL_ERROR 1 +} + + +### Mii constants for 10ge ### +set miiPreemphasisNone 0 ;# 0x0000 +set miiPreemphasis18 1 ;# 0x4000 +set miiPreemphasis38 2 ;# 0x8000 +set miiPreemphasis75 3 ;# 0xC000 + + +set miiLossOfSignal160mv 0 ;# 0x0000 +set miiLossOfSignal240mv 1 ;# 0x0010 +set miiLossOfSignal200mv 2 ;# 0x0020 +set miiLossOfSignal120mv 3 ;# 0x0030 +set miiLossOfSignal80mv 4 ;# 0x0040 + +set miiRecoveredClock 0 +set miiLocalRefClock 1 + + + diff --git a/dep/tclclient/ixTcl1.0/Generic/conversions.tcl b/dep/tclclient/ixTcl1.0/Generic/conversions.tcl new file mode 100644 index 00000000..2e78ed98 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/conversions.tcl @@ -0,0 +1,275 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: conversions.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Description: This file contains some utility procs for running the cable modem +# and ATP tests. +# +# Revision Log: +# +# Date Author Comments +# ----------- ------------------- -------------------------------------------- +# 2000/05/01 DS Genesis (for cable modem testing) +# 2000/10/02 D. Heins-Gelder Moved rate converters to Generic/converstions.tcl +# +################################################################################## + + + +######################################################################################## +# getPercentMaxRate +# +# Description: Helper procedure which derives the percentMaxRate based on the +# user selected rate type (ie: percentMaxMate, kbps or fps) +# +# Input: chassis card port +# framesize : +# rateType : +# rate : +# loss : default is NULL +# +# Output: retCode : 0 if invalid chassis-card-port, else +# percentMaxRate +# +######################################################################################## +proc getPercentMaxRate {chassis card port framesize rateType rate {preambleSize 8}} \ +{ + set percentMaxRate 0 + + if [port get $chassis $card $port] { + errorMsg "Error getting $chassis $card $port" + } else { + + switch $rateType { + percentMaxRate { + set percentMaxRate $rate + } + kbpsRate { + set percentMaxRate [calculatePercentMaxRate $chassis $card $port [mpexpr $rate*1000./($framesize * 8.)] $framesize $preambleSize] + } + fpsRate { + set percentMaxRate [calculatePercentMaxRate $chassis $card $port $rate $framesize $preambleSize] + } + default { + set percentMaxRate 0. + } + } + } + + return [format %.6f $percentMaxRate] +} + + +######################################################################################## +######################################################################################## +# Rate converters +# Note - these things live in the cablemodem.tcl file because they are really +# only applicable to cablemodems. Also, note that the getMaxFPS +# proc will probably fall apart w/PoS implementations. +######################################################################################## +######################################################################################## +# +# +# Conversion formulae implemented in code (checked by David Selenkow 5/4/2000) +# +# Given rate=%maxRate, then: as found in: +# percentMaxRate = rate convertPercentMaxRate +# kbps = (maxFPS*(rate/100)*framesize*8) / 1000 convertKbpsRate +# fps = (rate/100)*maxFPS convertFpsRate +# +# Given rate = kpbs, then: +# percentMaxRate = ((rate*1000)/(framesize*8*maxFPS)) * 100 convertPercentMaxRate +# kpbs = rate convertKpbsRate +# fps = (rate*1000)/(framesize*8) convertFpsRate +# +# Given rate=fps, then +# percentMaxRate = (rate/maxFPS)*100 convertPercentMaxRate +# kbps = (rate*framesize*8) / 1000 convertKbpsRate +# fps = rate convertFpsRate + +######################################################################################## + + + +######################################################################################## +# Procedure: getMaxFPS +# +# Description: Get the max fps rate based on speed/framesize, assuming a 96 bit minimum +# gap (802.3) +# +######################################################################################## +proc getMaxFPS {speed framesize {preambleSize 8}} \ +{ + set expr [unixCludgeGetExpr] + + set minimumGap 96 ;# bits + if [catch {$expr round((double($speed) * 1000000.) / (double($minimumGap) + (($preambleSize + $framesize)*8.)))} maxFps] { + set maxFps 0 + } + + return $maxFps +} + + +######################################################################################## +# Procedure: convertPercentMaxRate +# +# Description: Converts to the % rate based on speed/framesize, assuming a 96 bit +# minimum gap (802.3) +# +######################################################################################## +proc convertPercentMaxRate {framesize rateType rate speed {preambleSize 8}} \ +{ + set expr [unixCludgeGetExpr] + + set maxRate [getMaxFPS $speed $framesize $preambleSize] + + if {[isValidPartialFloat $framesize] && [isValidPartialFloat $rate] && [isValidPartialFloat $speed] && \ + [isValidPartialFloat $preambleSize]} { + switch $rateType { + percentMaxRate { + set percentMaxRate $rate + } + kbpsRate { + if [catch {$expr ($rate*1000./($framesize * 8. * $maxRate))*100.} percentMaxRate] { + set percentMaxRate 1.0 + } + } + fpsRate { + if [catch {$expr (($rate*100.)/$maxRate)} percentMaxRate] { + set percentMaxRate 1.0 + } + } + default { + set percentMaxRate 1.0 + } + } + } else { + set percentMaxRate 1.0 + } + return [format %.6f $percentMaxRate] +} + + +######################################################################################## +# Procedure: convertKbpsRate +# +# Description: Converts to the kbps rate based on speed/framesize, assuming a 96 bit +# minimum gap (802.3) +# +######################################################################################## +proc convertKbpsRate {framesize rateType rate speed {preambleSize 8}} \ +{ + set expr [unixCludgeGetExpr] + + set kbpsRate 1.0 + + if {[isValidPartialFloat $framesize] && [isValidPartialFloat $rate] && [isValidPartialFloat $speed] && \ + [isValidPartialFloat $preambleSize]} { + switch $rateType { + percentMaxRate { + set maxRate [getMaxFPS $speed $framesize $preambleSize] + set kbpsRate [$expr (($maxRate*($rate/100.)) * $framesize * 8.) / 1000.] + } + kbpsRate { + set kbpsRate $rate + } + fpsRate { + set kbpsRate [$expr ($rate * $framesize * 8.)/1000.] + } + } + } + return [format %.6f $kbpsRate] +} + + +######################################################################################## +# Procedure: convertFpsRate +# +# Description: Converts to the max fps rate based on speed/framesize, assuming a 96 bit +# minimum gap (802.3) +# +######################################################################################## +proc convertFpsRate {framesize rateType rate speed {preambleSize 8}} \ +{ + set expr [unixCludgeGetExpr] + + set fpsRate 1.0 + + if {[isValidPartialFloat $framesize] && [isValidPartialFloat $rate] && [isValidPartialFloat $speed] && \ + [isValidPartialFloat $preambleSize]} { + switch $rateType { + percentMaxRate { + set maxRate [getMaxFPS $speed $framesize $preambleSize] + if {[catch {$expr ($rate/100.)*$maxRate} fpsRate]} { + set fpsRate 1.0 + } + } + kbpsRate { + if {[catch {$expr ($rate/($framesize * 8.))*1000.} fpsRate]} { + set fpsRate 1.0 + } + } + fpsRate { + set fpsRate $rate + } + } + } + return [format %.6f $fpsRate] +} + + +######################################################################## +# Procedure: generateFullList +# +# Helper proc to generate the full list for the burstsize +# +######################################################################## +proc generateFullList { originalList {burstsize 10} } \ +{ + set maxFramesize 0 + foreach item $originalList { + for {set i 1} { $i <= 10} {incr i} { + lappend fullFrameSizeList $item + } + } + + return $fullFrameSizeList +} + + + + diff --git a/dep/tclclient/ixTcl1.0/Generic/dataValidation.tcl b/dep/tclclient/ixTcl1.0/Generic/dataValidation.tcl new file mode 100644 index 00000000..9dccfdd5 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/dataValidation.tcl @@ -0,0 +1,1855 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: dataValidation.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Description: This file contains the namespace and its procedures that verify +# IXIA test parameters when a new test is started. +# +# Revision Log: +# +# Date Author Comments +# ----------- ------------------- ------------------------------------------- +# 2001/04/24 Scott Si Created +# +############################################################################### + +namespace eval dataValidation {} \ +{ + # + # All the following ARRAYs are indexed by the name of TCL variable of IXIA + # test, such as numIterations (number of iterations) and percentMaxRate + # (max percent rate). + # + variable typeArray + variable validValuesArray + variable validRangeArray + variable validateProcArray + variable helpArray + + # + # typePattern defines the valid types for all the test parameters, which + # are the following. + # {integer, integerList, double, boolean, string, stringList, ipaddress, imixlist, macaddress, portlist} + # + variable typePattern {^integer$|^integerList$|^double$|^boolean$|^string$|^stringList$|^ipaddress$|^imixlist$|^macaddress$|^portlist$} + + # + # booleanPattern defines the valid values for all the test parameters that is of + # the type boolean. The valide value must be one of the following. + # {ture false yes no 1 0} + # + variable booleanPattern {^true$|^false$|^yes$|^no$|^1$|^0$} +} + +############################################################################### +# +# dataValidation::initialize +# +# Description: Initialize rules for data validation. This function should be +# invoked when a new test is selected. +# +# Input: None +# +# Output: None. +# +############################################################################### +proc dataValidation::initialize {} \ +{ + variable typeArray + variable validValuesArray + variable validRangeArray + variable validateProcArray + variable helpArray + + variable errorString "" + + catch {unset typeArray } + catch {unset validValuesArray } + catch {unset validRangeArray } + catch {unset validateProcArray} + catch {unset helpArray } + + # + # The following parameters are kept by the global array testConf. + # + setParameter -parameter hostname -type stringList + setParameter -parameter cableLength -type stringList -validValues {cable3feet cable6feet cable9feet cable12feet cable15feet cable18feet cable21feet cable24feet} + setParameter -parameter chassisID -type integerList -validRange {0 NULL} + setParameter -parameter chassisSequence -type integerList -validRange {1 NULL} + set speedList [list 10 100 1000 usb oc3 oc12 stm1c stm4c oc48 stm16c oc192 stm64c "WAN (Sonet)" "WAN (SDH)" 10000 9294 Copper10 Copper100 Copper1000 Fiber1000] + + setParameter -parameter speed -type string -validValues $speedList -validateProc dataValidation::validatePortSpeed + setParameter -parameter RxPortSpeed -type string -validValues $speedList -validateProc dataValidation::validatePortSpeed + setParameter -parameter TxPortSpeed -type string -validValues $speedList -validateProc dataValidation::validatePortSpeed + setParameter -parameter serverSpeed -type string -validValues $speedList -validateProc dataValidation::validatePortSpeed + setParameter -parameter clientSpeed -type string -validValues $speedList -validateProc dataValidation::validatePortSpeed + setParameter -parameter PPPnegotiation -type boolean + setParameter -parameter autoMapGeneration -type boolean + setParameter -parameter autonegotiate -type boolean + setParameter -parameter duplex -type string -validValues {full half} + setParameter -parameter incrIpAddrByteNum -type integer -validValues {1 2 3 4} + setParameter -parameter mapDirection -type string -validValues {unidirectional bidirectional} + setParameter -parameter sonetRxCRC -type string -validValues {sonetCrc16 sonetCrc32} + setParameter -parameter sonetTxCRC -type string -validValues {sonetCrc16 sonetCrc32} + setParameter -parameter supportsPOS -type boolean + setParameter -parameter useMagicNumber -type boolean + setParameter -parameter useRecoveredClock -type boolean + setParameter -parameter dataScrambling -type boolean + setParameter -parameter enable802dot1qTag -type boolean + setParameter -parameter enableISLtag -type boolean + setParameter -parameter firstDestDUTIpAddress -type ipaddress + setParameter -parameter firstSrcIpxSocket -type string + setParameter -parameter ethernetType -type string -validValues {noType ethernetII} + setParameter -parameter hdlcHeader -type string -validValues {ppp cisco} + setParameter -parameter maxPercentRate -type double -validRange {0.0001 100} + setParameter -parameter responseTime -type integer -validRange {0 NULL} + setParameter -parameter protocolName -type string -validValues {mac ip ipV6 ipx} +} + +############################################################################### +# +# dataValidation::setParameter +# +# Description: Create an entry in the data validation database. +# +# Input: args - arguments that define the name, type, values, and command +# for a test parameter in the following format. +# +# -parameter $name \ +# -type $type \ +# -validValues $values \ +# -validRange $range \ +# -command $command +# +# Output: status of the procedure +# +############################################################################### +proc dataValidation::setParameter {args} \ +{ + variable typeArray + variable validValuesArray + variable validRangeArray + variable validateProcArray + variable helpArray + + variable typePattern + + set retCode $::TCL_OK + + # + # This one is different in that the args could contain multiple -validateProc. + # + set validateProcArg {} + + foreach arg $args { + + if {[regexp {^-[a-zA-Z].+} $arg]} { + # + # Current argument is preceeded by '-'. + # + + # Remove the preceeding '-'. + regsub {^-} $arg "" option + + switch $option { + parameter - + type - + validValues - + validRange - + validateProc - + help { + set currentOption $option + set currentAction getValue + } + default { + set retCode $::TCL_ERROR + break + } + } + } else { + if {[info exists currentAction]} { + switch $currentAction { + "getValue" { + if {$currentOption == "validateProc"} { + lappend ${currentOption}Arg $arg + } else { + set ${currentOption}Arg $arg + } + + unset currentOption + unset currentAction + } + default { + set retCode $::TCL_ERROR + break + } + } + } else { + set retCode $::TCL_ERROR + break + } + } + } + + if {$retCode == $::TCL_OK} { + # + # Parameter name, and parameter type are mandatory when setting a test parameter. + # + if {[info exists parameterArg] == 0} { + + set retCode $::TCL_ERROR + + } elseif {[info exists typeArg] == 0} { + + set retCode $::TCL_ERROR + + } elseif {[info exists validValuesArg] && [info exists validRangeArg]} { + + set retCode $::TCL_ERROR + + } + } + + if {$retCode == $::TCL_ERROR} { + errorMsg "Invalid arguments: \"$args\"." + return $retCode + } + + if {[info exists typeArg]} { + set typeArray($parameterArg) $typeArg + } + + if {[info exists validValuesArg] && ($validValuesArg != "")} { + set validValuesArray($parameterArg) $validValuesArg + } + + if {[info exists validRangeArg]} { + set validRangeArray($parameterArg) $validRangeArg + } + + if {[info exists validateProcArg]} { + set validateProcArray($parameterArg) $validateProcArg + } + + if {[info exists helpArg]} { + set helpArray($parameterArg) $helpArg + } + + return $retCode +} + +############################################################################### +# +# dataValidation::getParameter +# +# Description: Retrieve properties of test parameter(s). +# +# Input: args - arguments of the following format +# (-parameter parameter) (-type) (-validValues) (-validRange) (-help) +# +# Output: Prints out all the rules if no arguments specified. +# Returns the rules for a parameter if only the parameter name +# is specified. +# Retruns the property if both the parameter name and the +# property name are specified. +# +############################################################################### +proc dataValidation::getParameter {args} \ +{ + variable typeArray + variable validValuesArray + variable validRangeArray + variable validateProcArray + variable helpArray + variable command + + set retCode $::TCL_OK + + set numArgs [llength $args] + + switch $numArgs { + 0 { + # + # No arguments. Show everything. + # + showRules + } + 2 { + # + # Has to be {-parameter $name}. Will return everything in a list of the + # format {{$type} {$value} {$command}}. Primarily for find out the + # general information the parameter about a parameter. + # + if {[lindex $args 0] == "-parameter"} { + set argName [lindex $args 1] + set retCode "" + if [info exists typeArray($argName)] { + append retCode "-parameter $argName -type $typeArray($argName) " + + if [info exists validValuesArray($argName)] { + append retCode "-validValues [list $validValuesArray($argName)] " + } + if [info exists validRangeArray($argName)] { + append retCode "-validRange [list $validRangeArray($argName)] " + } + if [info exists validateProcArray($argName)] { + append retCode "-validateProc $validateProcArray($argName)" + } + if [info exists helpArray($argName)] { + append retCode "-help $helpArray($argName)" + } + } + } else { + errorMsg "Invalid format \"$args\"." + set retCode $::TCL_ERROR + } + } + 3 { + # + # Has to be "-parameter $name -{type|validValues|validRange|validateProc|help}". + # Will return exactly what the caller wants. + # + if {[lindex $args 0] == "-parameter"} { + set argName [lindex $args 1] + + if [info exists typeArray($argName)] { + # + # The specified parameter name exists. + # + set property [string trimleft [lindex $args 2] -] + + if {$property == "type"} { + + set retCode $typeArray($argName) + + } elseif {$property == "validValues"} { + + if [info exists validValuesArray($argName)] { + set retCode $validValuesArray($argName) + } else { + set retCode "" + } + } elseif {$property == "validRange"} { + + if [info exists validRangeArray($argName)] { + set retCode $validRangeArray($argName) + } else { + set retCode "" + } + } elseif {$property == "validateProc"} { + + if [info exists validateProcArray($argName)] { + set retCode $validateProcArray($argName) + } else { + set retCode "" + } + } elseif {$property == "help"} { + + if [info exists helpArray($argName)] { + set retCode $helpArray($argName) + } else { + set retCode "" + } + } else { + errorMsg "Invalid type \"$args\"." + set retCode $::TCL_ERROR + } + } else { + # + # The specified parameter name does not exist. + # + debugMsg "The parameter \"$argName\" does not exist." + } + } else { + errorMsg "Invalid format \"$args\"." + set retCode $::TCL_ERROR + } + } + default { + # + # Error. + # + errorMsg "Invalid format \"$args\"." + set retCode $::TCL_ERROR + } + } + + return $retCode +} + +############################################################################### +# +# dataValidation::showRules +# +# Description: This command displays the data validation database. +# +# Input: None +# +# Output: None +# +############################################################################### +proc dataValidation::showRules {} \ +{ + variable typeArray + variable validValuesArray + variable validRangeArray + variable validateProcArray + variable helpArray + variable command + + ixPuts "========================================================================" + ixPuts [format "%-24s %-10s %-80s %-s" "Parameter" "Type" "Values" "ValidateProc"] + ixPuts "========================================================================" + + foreach name [lsort [array name typeArray]] { + + set rule [format "%-24s %-10s" $name $typeArray($name)] + + if [info exists validValuesArray($name)] { + append rule [format " %-80s" $validValuesArray($name)] + } + if [info exists validRangeArray($name)] { + append rule [format " %-80s" $validRangeArray($name)] + } + if [info exists validateProcArray($name)] { + append rule [format " %-30s" $validateProcArray($name)] + } + if [info exists helpArray($name)] { + append rule [format " %-30s" $helpArray($name)] + } + + ixPuts $rule + } +} + +############################################################################### +# +# dataValidation::parseValues +# +# Description: This command makes sure that the validateValues or validateRange +# comply with the given type. (NEED TO BE MODIFIED) +# +# Input: +# +# Output: +# +############################################################################### +proc dataValidation::parseValues {type values} \ +{ + if {[llength $values] != 2} { + errorMsg "Wrong format for values \"$values\"." + return $::TCL_ERROR + } + + set retCode $::TCL_OK + + set valueProperty [lindex $values 0] + + switch $valueProperty { + enum { + set valueList [lindex $values 1] + set retCode [areValuesOfTheType $type $valueList] + } + range { + set valueRange [lindex $values 1] + + if {[llength $valueRange] != 2} { + errorMsg "Invalid range \"$valueRange\"." + set retCode $::TCL_ERROR + } else { + set lowerLimit [lindex $valueRange 0] + set upperLimit [lindex $valueRange 1] + + if {($type == "integer") || ($type == "double")} { + + if {($lowerLimit != "NULL") && ($upperLimit != "NULL")} { + if {([areValuesOfTheType $type $valueRange] == $::TCL_OK) && + ($lowerLimit >= $upperLimit)} { + errorMsg "Invalid range \"$valueRange\"." + set retCode $::TCL_ERROR + } + } elseif {$lowerLimit == "NULL"} { + if {[areValuesOfTheType $type $upperLimit] != $::TCL_OK} { + errorMsg "Invalid range \"$valueRange\"." + set retCode $::TCL_ERROR + } + } elseif {$upperLimit == "NULL"} { + if {[areValuesOfTheType $type $lowerLimit] != $::TCL_OK} { + errorMsg "Invalid range \"$valueRange\"." + set retCode $::TCL_ERROR + } + } + } + } + } + default { + errorMsg "Wrong value property for values \"$values\"." + set retCode $::TCL_ERROR + } + } + + return $retCode +} + + +############################################################################### +# +# dataValidation::isPortList +# +# Description: Find out if the given value is a valid port list. +# +# Input: value to be checked. +# +# Output: 1 if yes. +# 0 otherwise. +# +############################################################################### +proc dataValidation::isPortList {value} \ +{ + foreach args $value { + foreach arg $args { + if {$arg >=0 && [isInteger $arg]} { + return 1 + } else { + return 0 + } + } + } +} + +############################################################################### +# +# dataValidation::isString +# +# Description: Find out if the given value is a valid IP addresss. +# +# Input: value to be checked. +# +# Output: 1 if yes. +# 0 otherwise. +# +############################################################################### +proc dataValidation::isString {value} \ +{ +# return [regexp {^[a-zA-Z].+} $value] + return 1 +} + +############################################################################### +# +# dataValidation::isInteger +# +# Description: Find out if the given value is a valid IP addresss. +# +# Input: value to be checked. +# +# Output: 1 if yes. +# 0 otherwise. +# +############################################################################### +proc dataValidation::isInteger {value} \ +{ + return [regexp {^[-]?[0-9]+$} $value] +} + +############################################################################### +# +# dataValidation::isDouble +# +# Description: Find out if the given value is a valid IP addresss. +# +# Input: value to be checked +# +# Output: 1 if yes. +# 0 otherwise. +# +############################################################################### +proc dataValidation::isDouble {value} \ +{ + set retCode 0 + + if { [string length $value] > 0 } { + set retCode [regexp {^-?[0-9]*\.?[0-9]*$} $value] + } + return $retCode +} + +############################################################################### +# +# dataValidation::areValuesOfTheType +# +# Description: Find out if all the given values are of the given type. +# +# Input: type - integer, double, string, ipaddress, or portlist +# value - list of values to be checked +# +# Output: TCL_OK if no error +# TCL_ERROR otherwise +# +############################################################################### +proc dataValidation::areValuesOfTheType {type value} \ +{ + set retCode $::TCL_OK + + if {[llength $value] == 0} { + set retCode $::TCL_ERROR + return $retCode + } + + set flagList 0 + + switch $type { + integer { set function isInteger } + double { set function isDouble } + string { set function isString } + ipaddress { set function isIpAddressValid } + portlist { set function portlist } + integerList { + set function isInteger + set flagList 1 + } + stringList { + set function isString + set flagList 1 + } + default { + errorMsg "Invalid type \"$type\"." + return $::TCL_ERROR + } + } + + if { $flagList == 1 } { + + foreach item $value { + if {[eval {$function $item}] == 0} { + set retCode $::TCL_ERROR + break + } + } + + } else { + + if {[eval {$function $value}] == 0} { + set retCode $::TCL_ERROR + } + } + + return $retCode +} + +############################################################################### +# +# dataValidation::validateTest +# +# Description: This command is invoked to validate the test parameters of the +# given test command. +# +# Input: +# cmd - test command +# +# +# Output: TCL_OK if no error +# TCL_ERROR otherwise +# +############################################################################### +proc dataValidation::validateTest {cmd {method cget}} \ +{ + set retCode $::TCL_OK + + catch {$cmd $method} paramList + foreach param [lsort [join $paramList]] { + if {$param == "-this"} { + continue + } + if {[string index $param 0] == "-"} { + set param [string trim $param -] + if [validateCommandParameter $cmd $param] { + ixPuts "Invalid value specified for parameter -$param :[$cmd cget -$param], \ + Valid value(s):[getValidValueString $cmd $param]" + set retCode $::TCL_ERROR + } + } + } + return $retCode +} + +############################################################################### +# +# dataValidation::getValidValueString +# +# Description: This command is +# +# Input: +# cmd - test command +# param - command parameter +# +# Output: +# +############################################################################### +proc dataValidation::getValidValueString {cmd param} \ +{ + set retCode $::TCL_OK + + + return $retCode +} + +############################################################################### +# +# dataValidation::validateTestConfParameter +# +# Description: This command validate the the specified parameter of testConf. +# +# Input: parameter - name of the parameter +# value - value of the parameter +# +# Output: TCL_OK if no error. +# TCL_ERROR otherwise. +# +############################################################################### +proc dataValidation::validateTestConfParameter {parameter value} \ +{ + set retCode $::TCL_OK + + switch $parameter { + hdlcHeader - + duplex - + speed - + TxPortSpeed - + RxPortSpeed - + useMagicNumber - + sonetRxCRC - + sonetTxCRC - + dataScrambling - + useRecoveredClock - + autonegotiate - + PPPnegotiation { + + # + # The values of these parameters could be in a list of pairs (value interface). + # + + # checking speed for 10GE WAN/OC192. + if {$parameter == "speed" && (($value == "WAN (Sonet)") || ($value == "WAN (SDH)"))} { + set retCode $::TCL_OK + } elseif {$parameter == "speed" && (($value == "Copper10") || ($value == "Copper100") || \ + ($value == "Copper1000") || ($value == "Fiber1000"))} { + set retCode $::TCL_OK + } else { + foreach elemOfValue $value { + set currValue [lindex $elemOfValue 0] + set retCode [doValidateTestConfParameter $parameter $currValue] + if {$retCode != $::TCL_OK} { + break + } + } + } + } + default { + set retCode [doValidateTestConfParameter $parameter $value] + } + } + + return $retCode +} + +############################################################################### +# +# dataValidation::doValidateTestConfParameter +# +# Description: This command performs the validation of the specified parameter of testConf. +# +# Input: parameter - name of the parameter +# value - value of the parameter +# +# Output: TCL_OK if no error. +# TCL_ERROR otherwise. +# +############################################################################### +proc dataValidation::doValidateTestConfParameter {parameter value} \ +{ + variable booleanPattern + + set retCode $::TCL_OK + + set parameterType [getParameter -parameter $parameter -type] + if {$parameterType == ""} { + return $::TCL_ERROR + } + + switch $parameterType { + boolean { + if {[regexp $booleanPattern $value] == 0} { + set retCode $::TCL_ERROR + } + } + integer - + double { + set retCode [areValuesOfTheType $parameterType $value] + if {$retCode == $::TCL_OK} { + set retCode [validateNumber "testConfig" $parameter $value] + } + } + integerList { + set retCode [areValuesOfTheType $parameterType $value] + if {$retCode == $::TCL_OK} { + foreach valueOfList $value { + set retCode [validateNumber "testConfig" $parameter $valueOfList] + if { $retCode == $::TCL_ERROR } { + break + } + } + } + } + string { + set validValues [getParameter -parameter $parameter -validValues] + if {$validValues != ""} { + if {[lsearch $validValues $value] == -1} { + set retCode $::TCL_ERROR + } + } + } + stringList { + set validValues [getParameter -parameter $parameter -validValues] + if {$validValues != ""} { + foreach valueOfList $value { + if {[lsearch $validValues $valueOfList] == -1} { + set retCode $::TCL_ERROR + } + } + } + } + ipaddress { + if {[testConfig::getTestConfItem protocolName] == "ip"} { + if {![isIpAddressValid $value]} { + set retCode $::TCL_ERROR + } + } + } + portlist { + if {[isPortList $value] == 0} { + set retCode $::TCL_ERROR + } + } + default { + } + } + + if {[getParameter -parameter $parameter -validateProc] != ""} { + foreach validateProc [getParameter -parameter $parameter -validateProc] { + if {[info command "::$validateProc"] != ""} { + set cmdLine "::$validateProc $value" + } elseif {[info command "[namespace current]::$validateProc"] != ""} { + set cmdLine "$validateProc $value" + } + + if {[info exists cmdLine]} { + + set result [eval $cmdLine] + + if {$result != $::TCL_OK} { + + if { $result == $::TCL_ERROR } { + + setErrorString "Failed on $validateProc." + + } else { + + # If result is neither TCL_OK nor TCL_ERROR, it must contain a string of + # error message. + # + setErrorString $result + } + } + } + } + } + + return $retCode +} + +############################################################################### +# +# dataValidation::validateCommandParameter +# +# Description: This command validate the parameters of the specified command. +# +# Input: command - name of the test commmand +# parameter - name of the parameter +# value - value of the parameter +# +# Output: TCL_OK if no error. +# TCL_ERROR otherwise. +# +############################################################################### +proc dataValidation::validateCommandParameter {command parameter {value ""}} \ +{ + variable booleanPattern + set retCode $::TCL_OK + + set value [$command cget -$parameter] + set parameterType [$command getType -$parameter] + + if {$parameterType == ""} { + return $::TCL_ERROR + } + + switch $parameterType { + boolean { + if {[regexp -nocase $booleanPattern $value] == 0} { + set retCode $::TCL_ERROR + } + + } + integer - + double { + set retCode [areValuesOfTheType $parameterType $value] + if {$retCode == $::TCL_OK} { + set retCode [validateNumber $command $parameter $value] + } + } + integerList { + set retCode [areValuesOfTheType $parameterType $value] + if {$retCode == $::TCL_OK} { + foreach valueOfList $value { + set retCode [validateNumber $command $parameter $valueOfList] + if { $retCode == $::TCL_ERROR } { + break + } + } + } + } + string { + set validValues [$command getValidValues -$parameter] + if {$validValues != ""} { + if {[lsearch $validValues $value] == -1} { + set retCode $::TCL_ERROR + } + } + } + stringList { + set validValues [$command getValidValues -$parameter] + if {$validValues != ""} { + foreach valueOfList $value { + if {[lsearch $validValues $valueOfList] == -1} { + set retCode $::TCL_ERROR + break + } + } + } + } + ipaddress { + if {[testConfig::getTestConfItem protocolName] == "ip"} { + if {![isIpAddressValid $value]} { + set retCode $::TCL_ERROR + } + } + } + portlist { + if {[isPortList $value] == 0} { + set retCode $::TCL_ERROR + } + } + default { + } + } + + if {[$command getValidateProc -$parameter] != ""} { + foreach validateProc [$command getValidateProc -$parameter] { + if {[info command "::$validateProc"] != ""} { + set cmdLine "::$validateProc $value" + } elseif {[info command "[namespace current]::$validateProc"] != ""} { + set cmdLine "$validateProc $value" + } + + if {[info exists cmdLine]} { + + set result [eval $cmdLine] + + if {$result != $::TCL_OK} { + + if { $result == $::TCL_ERROR } { + + setErrorString "Failed on $validateProc." + + } else { + + # If result is neither TCL_OK nor TCL_ERROR, it must contain a string of + # error message. + # + setErrorString $result + set retCode $::TCL_ERROR + } + } + } + } + } + + return $retCode +} + +############################################################################### +# +# dataValidation::setErrorString +# +# Description: +# +# Input: +# +# Output: +# +############################################################################### +proc dataValidation::setErrorString {line} { + variable errorString + + set errorString $line +} + +############################################################################### +# +# dataValidation::getErrorString +# +# Description: +# +# Input: +# +# Output: +# +############################################################################### +proc dataValidation::getErrorString {} { + variable errorString + + set retVal $errorString + set errorString "" + + return $retVal +} + +############################################################################### +# +# dataValidation::isDataInRange +# +# Description: This command finds out if a given value is in the specified range. +# +# Input: data - data to be checked. +# range - the range to be checked against. Must be in the format of +# {lowerLimit, upperLimit}. +# +# Output: TCL_OK if yes. TCL_ERROR otherwise. +# +############################################################################### +proc dataValidation::isDataInRange {data range} \ +{ + set retCode $::TCL_OK + + set lowerLimit [lindex $range 0] + set upperLimit [lindex $range 1] + + if { (($lowerLimit != "NULL") && ($data < $lowerLimit)) || \ + (($upperLimit != "NULL") && ($data > $upperLimit))} { + set retCode $::TCL_ERROR + } + + return $retCode +} + +############################################################################### +# +# dataValidation::validatePortSpeed +# +# Description: +# +# Input: +# +# Output: +# +############################################################################### +proc dataValidation::validatePortSpeed {args} \ +{ + # puts "validating speed $args" + return $::TCL_OK +} + +############################################################################### +# +# dataValidation::getProtocol +# +# Description: This command finds out available protocols. +# +# Input: +# +# Output: protocol list +# +############################################################################### +proc dataValidation::getProtocol {} \ +{ + set protocolTable [imix cget -protocolTable] + set protocolList {} + foreach list $protocolTable { + lappend protocolList [lindex $list 0] + } + + return $protocolList +} + +############################################################################### +# +# dataValidation::checkIdentical +# +# Description: This command finds out if a given value is identical to others. +# If it isn't, then add it to list. +# +# Input: list - available list +# args - args to be checked. +# +# Output: list +# +############################################################################### +proc dataValidation::checkIdentical {list value} \ +{ + set length [llength $list] + set different 0 + + if {$length == 0} { + lappend list $value + incr length + } + + for {set index 0} {$index < $length} {incr index} { + if {$value != [lindex $list $index]} { + if {$index == [expr $length - 1]} { + set different 1 + } + continue + } else { + break + } + } + + if {$different == 1} { + lappend list $value + } + + return $list + +} + +############################################################################### +# +# dataValidation::checkMatched +# +# Description: This command finds out if a given value is match to list. +# +# Input: list - available list +# value - value to be checked. +# +# Output: TCL_OK if yes. TCL_ERROR otherwise. +# +############################################################################### +proc dataValidation::checkMatched {list value} \ +{ + set retCode $::TCL_OK + + foreach arg $list { + if {$arg == $value } { + break + } elseif {$arg != $value && $arg == [lindex $list end]} { + set retCode $::TCL_ERROR + } + } + + return $retCode + +} + + +############################################################################### +# +# dataValidation::frameSizeProtocolSame +# +# Description: This command finds out if both frame size and protocol are same. +# +# Input: args - args to be checked. +# +# Output: TCL_OK if no. TCL_ERROR otherwise. +# +############################################################################### +proc dataValidation::frameSizeProtocolSame {args} \ +{ + set retCode $::TCL_OK + set imixList [lindex $args 0] + debugMsg "imixList: $imixList" + set argsLength [llength $imixList] + set frameSizeProtocolList {} + + foreach arg $imixList { + set length [llength [lindex $arg 0]] + if {$length == 1} { + lappend frameSizeProtocolList [lindex $arg 0] + lappend frameSizeProtocolList UDP + } else { + lappend frameSizeProtocolList [lindex [lindex $arg 0] 0] + lappend frameSizeProtocolList [lindex [lindex $arg 0] 1] + } + } + + debugMsg "frameSizeProtocolList:$frameSizeProtocolList" + + for {set index1 0} {$index1 < [expr 2 * $argsLength]} {incr index1 0} { + set frameSize1 [lindex $frameSizeProtocolList $index1] + incr index1 + set protocol1 [lindex $frameSizeProtocolList $index1] + incr index1 + for {set index2 $index1} {$index2 < [expr 2 * $argsLength]} {incr index2 0} { + set frameSize2 [lindex $frameSizeProtocolList $index2] + incr index2 + set protocol2 [lindex $frameSizeProtocolList $index2] + incr index2 + + if {$frameSize1 == $frameSize2} { + if {[stringCompare -nocase $protocol1 $protocol2] == 0} { + logMsg "Invalid for both frame size $frameSize1 and $frameSize2 have same protocols" + set retCode $::TCL_ERROR + break + } + } + } + if {$retCode == $::TCL_ERROR} { + break + } + } + + return $retCode +} + + +############################################################################### +# +# dataValidation::validateImixList +# +# Description: This command finds out if a given imixList is valid. +# +# Input: args - args to be checked. For example: +# set list {{{80 tcp } 20} \ +# { 74 20} \ +# {{570 udp } 20} \ +# {{81 aaa } 20} \ +# {570 20}} +# +# Output: TCL_OK if yes. TCL_ERROR otherwise. +# +############################################################################### +proc dataValidation::validateImixList {args} \ +{ + set retCode $::TCL_OK + set sum 0 + set bandwidthList {} + + set imixList [lindex $args 0] + debugMsg "imixList= $imixList" + + if [frameSizeProtocolSame $imixList] { + set retCode $::TCL_ERROR + return $retCode + } + + foreach arg $imixList { + debugMsg "--------------------------------------------" + debugMsg "arg:$arg" + for {set index2 0} {$index2 < 2} {incr index2} { + switch $index2 { + 0 { + set subArg [lindex $arg 0] + set len3 [llength $subArg] + for {set index3 0} {$index3 < $len3} {incr index3} { + set parameter [lindex $subArg $index3] + switch $index3 { + 0 { + debugMsg "********** fs:$parameter ***********" + if {[isInteger $parameter] == 0} { + debugMsg "fs:$parameter invalid which isnot an integer" + set retCode $::TCL_ERROR + } elseif {$parameter < 0} { + debugMsg "fs:$parameter invalid which is less than 0" + set retCode $::TCL_ERROR + } elseif {$parameter <70} { + set protocol [lindex $subArg 1] + debugMsg "subArg=$subArg" + debugMsg "protocol:$protocol" + if {[stringCompare -nocase "Ethernet" $protocol] == 0} { + debugMsg "fs:$parameter invalid which is less than 70 while using Ethernet" + set retCode $::TCL_ERROR + } + } + } + 1 { + debugMsg "********** protocol:$parameter ***********" + set protocolList [getProtocol] + if {[checkMatched $protocolList $parameter] == 1} { + debugMsg "protocol:$parameter invalid which doesnot match your protocol list" + set retCode $::TCL_ERROR + } + } + 2 { + debugMsg "********** precedence:$parameter ***********" + if {[isInteger $parameter] == 0} { + debugMsg "precedence:$parameter invalid which isnot an integer" + set retCode $::TCL_ERROR + } elseif {$parameter < 0} { + debugMsg "precedence:$parameter invalid which is less than 0" + set retCode $::TCL_ERROR + } + } + } + } + + } + 1 { + set parameter [lindex $arg 1] + debugMsg "********** %bandwidth:$parameter ***********" + if {[isDouble $parameter] == 0} { + debugMsg "%bandwidth:$parameter invalid which isnot an integer or double" + set retCode $::TCL_ERROR + } elseif {$parameter < 0} { + debugMsg "%bandwidth:$parameter invalid which is less than 0" + set retCode $::TCL_ERROR + } elseif {[expr $sum + $parameter] > 100} { + debugMsg "%bandwidth:$parameter invalid which is more than [expr 100 - $sum]" + set retCode $::TCL_ERROR + } else { + set sum [expr $sum + $parameter] + } + } + } + } + + } + debugMsg "--------------------------------------------" + + return $retCode +} + +############################################################################### +# +# dataValidation::validateProtocolTable +# +# Description: This command finds out if a given protocolTable List is valid. +# +# Input: args: - protocolTable List. For example: +# protocolTable {{ tcp tcp 0 0 } \ +# { http tcp 1053 80 } \ +# { ftp tcp 1054 21 } \ +# { dns udp 1055 22 } \ +# { telnet tcp 1056 23 } \ +# { test1 egp 1057 24 } \ +# { test2 icmp 1058 25 }} +# +# Output: TCL_OK if yes. TCL_ERROR otherwise. +# +############################################################################### +proc dataValidation::validateProtocolTable {args} \ +{ + set retCode $::TCL_OK + debugMsg "protocol table: args= $args" + set argsList [lindex $args 0] + set len [llength $argsList] + set yourProtocolList {} + set standardProtocolList {http telnet ftp tcp udp icmp bgp ospf rsvp rvp ethernet} + + foreach arg $argsList { + debugMsg "--------------------------------------------" + debugMsg "arg:$arg" + for {set index 0} {$index < $len} {incr index} { + set parameter [lindex $arg $index] + switch $index { + 0 { + debugMsg "********** your protocol:$parameter **********" + set length [llength $yourProtocolList] + set yourProtocolList [checkIdentical $yourProtocolList $parameter] + if {$length == [llength $yourProtocolList]} { + debugMsg "yourProtocol:$parameter invalid which is identical to others" + set retCode $::TCL_ERROR + } + } + 1 { + debugMsg "********** protocol:$parameter ***********" + if {[checkMatched $standardProtocolList $parameter] == 1} { + debugMsg "Protocol:$parameter invalid which is not a standard protocol" + set retCode $::TCL_ERROR + } + } + 2 { + debugMsg "********** srcPort:$parameter ***********" + if {[isInteger $parameter] == 0} { + debugMsg "srcPort:$parameter invalid which isnot an integer" + set retCode $::TCL_ERROR + } elseif {$parameter < 0} { + debugMsg "srcPort:$parameter invalid which is less than 0" + set retCode $::TCL_ERROR + } + + + } + 3 { + debugMsg "********** destPort:$parameter **********" + if {[isInteger $parameter] == 0} { + debugMsg "destPort:$parameter invalid which isnot an integer" + set retCode $::TCL_ERROR + } elseif {$parameter < 0} { + debugMsg "destPort:$parameter invalid which is less than 0" + set retCode $::TCL_ERROR + } + } + } + + } + } + debugMsg "--------------------------------------------" + + return $retCode +} + +############################################################################### +# +# dataValidation::validateCmFlowMix +# +# Description: +# +# Input: +# +# Output: +# +############################################################################### +proc dataValidation::validateCmFlowMix {args} \ +{ +} + +############################################################################### +# +# dataValidation::validateRunType +# +# Description: +# +# Input: +# +# Output: +# +############################################################################### +proc dataValidation::validateRunType {args} \ +{ +} + + +############################################################################### +# +# dataValidation::validateNumber +# +# Description: Validates a number that can be either integer or double. +# +# Input: command - name of the test commmand +# parameter - name of the parameter +# value - value of the parameter +# +# Output: TCL_OK if yes. TCL_ERROR otherwise. +# +############################################################################### +proc dataValidation::validateNumber {command parameter value} \ +{ + set retCode $::TCL_OK + + set enumOrRange "" + if { $command == "testConfig" } { + set validValue [getParameter -parameter $parameter -validValues] + } else { + set validValue [$command getValidValues -$parameter] + } + + if {$validValue != ""} { + set enumOrRange enum + } else { + if { $command == "testConfig" } { + set validValue [getParameter -parameter $parameter -validValues] + } else { + set validValue [$command getValidRange -$parameter] + } + + if {$validValue != ""} { + set enumOrRange range + } + } + + if {$enumOrRange == "enum"} { + foreach elemOfValue $value { + if {[lsearch $validValue $elemOfValue] == -1} { + set retCode $::TCL_ERROR + break + } + } + } elseif {$enumOrRange == "range"} { + foreach elemOfValue $value { + set retCode [isDataInRange $elemOfValue $validValue] + if {$retCode == $::TCL_ERROR} { + break + } + } + } + + return $retCode +} + + +############################################################################### +# +# dataValidation::checkErrFrameFrameErrorList +# +# Description: This command validates frame error list for 2889 error frame test. +# +# Input: framesize +# +# Output: TCL_OK, if no error +# Error string, otherwise +# +############################################################################### +proc dataValidation::checkErrFrameFrameErrorList { frameErrorList } \ +{ + + set retCode $::TCL_OK + + if { [llength $frameErrorList] == 0 } { + + set retCode "frameErrorList must not be empty.\nAt least one type of the frame sizes\nhave to be selected." + + } else { + + set frameSizeCount 0 + + foreach item $frameErrorList { + switch $item { + undersize { + incr frameSizeCount [llength [errframe cget -undersizeList]] + } + oversize { + incr frameSizeCount [llength [errframe cget -oversizeList]] + } + default { + incr frameSizeCount [llength [errframe cget -framesizeList]] + } + } + } + + if { $frameSizeCount == 0 } { + set retCode "No valid frame sizes were selected\n\ + for current framesize option(s)" + } + } + + return $retCode +} + + +############################################################################### +# +# dataValidation::checkCmatsIpAndMacAddress +# +# Description: This command validates address of Cmats test. +# The address of cableModem command can be either IP address and +# MAC address. The last bit of the first byte has to be on if it +# is MAC address. +# +# Input: address +# +# Output: TCL_OK, if no error +# Error string, otherwise +# +############################################################################### +proc dataValidation::checkCmatsIpAndMacAddress { address } \ +{ + global currContext + + set retCode $::TCL_OK + + if {![isIpAddressValid $address]} { + set retCode $::TCL_ERROR + } + + if { $retCode == $::TCL_ERROR } { + + # The address is not IP address. Let's see if it is MAC address. + + if { [isMacAddressValid $address] == $::TCL_OK } { + + if { $currContext(testSubCat) == "eth01McastUpstream" || \ + $currContext(testSubCat) == "eth01McastDownstream" } { + + # The last bit of the first byte has to be on. + + set firstByte [lindex [split $address] 0] + if { [expr 0x$firstByte & 01] == 1 } { + set retCode $::TCL_OK + } + + } else { + set retCode $::TCL_OK + } + } + } + + if { $retCode == $::TCL_ERROR } { + set retCode "Valid IP or MAC address." + + if { $currContext(testSubCat) == "eth01McastUpstream" || \ + $currContext(testSubCat) == "eth01McastDownstream" } { + append retCode "The last bit of the first byte has to be on for MAC address." + } + } + + return $retCode +} + + +################################################################################## +# Procedure: dataValidation::isValidMulticastIp +# +# Description: +# Helper proc to check if ipAddress is a valid IP by making sure it falls in the +# range (224.0.0.0 - 239.255.255.255). +# +# Arguments(s): +# ipAddress - IP address +# +# Returns: +# $::TCL_OK, if is valid unicast IP address +# Valid range, otherwise +# +################################################################################## +proc dataValidation::isValidMulticastIp {ipAddress} \ +{ + set retCode "\(224.0.0.0 - 239.255.255.255\)" + + if {[isIpAddressValid $ipAddress]} { + + set multicastIpStartNum [ip2num "224.0.0.0"] + set multicastIpEndNum [ip2num "239.255.255.255"] + set ipNum [ip2num $ipAddress] + + if { ($ipNum >= $multicastIpStartNum) && ($ipNum <= $multicastIpEndNum) } { + set retCode $::TCL_OK + } + } + + return $retCode +} + + +################################################################################## +# Procedure: dataValidation::isValidUnicastIp +# +# Description: +# Helper proc to check if ipAddress accomplied with the following +# 1) it is not 0.x.x.x +# 2) it is not 255.255.255.255 +# 3) it is not loopback address (127.x.x.x) +# 4) it is not multicast address (224.0.0.0 - 239.255.255.255, i.e first 4 bits not 1110) +# 5) it is not reserved for future use (240.0.0.0 - 247.255.255.255) +# 6) it is invalid when it is < 1.0.0.0 or >= 224.0.0.0 on port +# +# Arguments(s): +# ipAddress - IP address +# +# Returns: +# $::true if is valid unicast IP address +# $::false if is not valid unicast IP address +# +################################################################################## +proc dataValidation::isValidUnicastIp {ipAddress} \ +{ + set valid $::false + + if {[isIpAddressValid $ipAddress]} { + set valid $::true + + set multicastIpStartNum [ip2num "224.0.0.0"] + set ipNum [ip2num $ipAddress] + + if {([lindex [split $ipAddress .] 0] == 127) || ([lindex [split $ipAddress .] 0] == 255) || \ + ([lindex [split $ipAddress .] 0] == 0)} { + set valid $::false + } + + if { [mpexpr $ipNum >= $multicastIpStartNum] } { + set valid $::false + } + } + + return $valid +} + +################################################################################## +# Procedure: dataValidation::isOverlappingIpAddress +# +# Description: +# Helper proc to check if IP addresses are overlapping +# +# Arguments(s): +# ipAddress1 - ipAddress to compare to +# mask1 - net mask +# ipAddress2 - ipAddress to compare to +# mask2 - net mask +# +# Returns: +# $::true - if overlapping +# $::false - if not overlapping +# +################################################################################## +proc dataValidation::isOverlappingIpAddress {ipAddress1 count1 ipAddress2 count2} \ +{ + set overlap $::false + + set firstIp1 [ip2num $ipAddress1] + set lastIp1 [mpexpr $firstIp1 + $count1 - 1] + + set firstIp2 [ip2num $ipAddress2] + set lastIp2 [mpexpr $firstIp2 + $count2 - 1] + + if {($firstIp1 <= $firstIp2 && $firstIp2 <= $lastIp1) || \ + ($firstIp1 <= $lastIp2 && $lastIp2 <= $lastIp1)} { + set overlap $::true + } + + if {($firstIp2 <= $firstIp1 && $firstIp1 <= $lastIp2) || \ + ($firstIp2 <= $lastIp1 && $lastIp1 <= $lastIp2)} { + set overlap $::true + } + + return $overlap +} + +################################################################################## +# Procedure: dataValidation::isSameSubnet +# +# Description: +# Helper proc to check if ports in two different user profiles are in the same subnet. +# Note that all ports in one user porfile are in the same subnet. +# +# Arguments(s): +# ipAddr1 - ipAddress to compare to +# mask1 - net mask +# ipAddr2 - ipAddress to compare to +# mask2 - net mask +# +# Returns: +# $::true if in same subnet +# $::false if not in same subnet +# +################################################################################## +proc dataValidation::isSameSubnet {ipAddr1 mask1 ipAddr2 mask2} \ +{ + set retCode $::true + + set range1Subnet [num2ip [expr [ip2num $ipAddr1] & [ip2num $mask1]]] + set range2Subnet [num2ip [expr [ip2num $ipAddr2] & [ip2num $mask2]]] + + if {[string compare $range1Subnet $range2Subnet] != 0} { + set retCode $::false + } + + return $retCode +} + + +################################################################################## +# Procedure: dataValidation::isValidNetMask +# +# Description: +# Helper proc to check if net mask is valid. i.e. In binary form, the mask must +# have consecutive 1's followed by consecutive 0's +# +# Arguments(s): +# mask - net mask +# +# Returns: +# $::true - if valid mask +# $::false - if not valid mask +# +################################################################################## +proc dataValidation::isValidNetMask {mask} \ +{ + set valid $::false + + if {[isIpAddressValid $mask]} { + set numMask [ip2num $mask] + + if {![catch {expr fmod(0x80000000, ($numMask ^ 0xffffffff) +1)} value] && $value == 0} { + set valid $::true + } + } + + return $valid +} + +################################################################################## +# Procedure: dataValidation::isValidHostPart +# +# Description: +# Helper proc to check if ipAddress is valid within the net mask +# The host bits cannot be all 0s or 1s +# +# Arguments(s): +# ipAddress - IP address, net mask +# +# Returns: +# $::true if is valid IP address within net mask +# $::false if is not valid IP address within net mask +# +################################################################################## +proc dataValidation::isValidHostPart {ipAddress mask} \ +{ + set valid $::false + + if {[isValidUnicastIp $ipAddress] && [isValidNetMask $mask]} { + + set ipNum [ip2num $ipAddress] + set maskNum [ip2num $mask] + + set valid [expr ((1 + $ipNum) & (~$maskNum)) > 1] + } + + return $valid +} + + + + diff --git a/dep/tclclient/ixTcl1.0/Generic/defineCommand.tcl b/dep/tclclient/ixTcl1.0/Generic/defineCommand.tcl new file mode 100644 index 00000000..43732bad --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/defineCommand.tcl @@ -0,0 +1,441 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: defineCommand.tcl +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 12-13-1999 DS Genesis +# +# Description: This file contains the commands for all the tests. When a new test +# is added, create a new proc for that test here. +# +################################################################################## + + + +######################################################################################## +# Namespace: defineCommand +# +# Description: This namespace is used to initialize the defineCommand commands +# +######################################################################################## +namespace eval defineCommand {} \ +{ + variable commands + variable commandList + + set commandList {} + set commands(config) {} + set commands(cget) {} + set commands(show) {} + set commands(setDefault) {} + + set commands(getType) {} + set commands(getValidRange) {} + set commands(getValidValues) {} + set commands(getValidateProc) {} + set commands(getHelp) {} +} + +######################################################################################## +# Procedure: initializeDefineCommand +# +# Description: This command is used to initialize the defineCommand commands +# +######################################################################################## +proc initializeDefineCommand {} \ +{ + defineCommand::initialize +} + +######################################################################################## +# Procedure: defineCommand::initialize +# +# Description: This command is used to initialize the defineCommand commands +# +######################################################################################## +proc defineCommand::initialize {} \ +{ + set retCode 0 + + # once we get this one registered, then we can use its methods + registerCommand defineCommand + registerMethod defineCommand registerCommand + registerMethod defineCommand registerMethod + + # now that we have registered the registerMethod, we can use it for the other methods + defineCommand registerMethod defineCommand isRegistered + defineCommand registerMethod defineCommand registerParameter + + return $retCode +} + + +######################################################################################## +# Procedure: defineCommand::isRegistered +# +# Description: This command checks to see if a command is registered +# +######################################################################################## +proc defineCommand::isRegistered {testCmd} \ +{ + set retCode 0 + + if [catch {$testCmd exists} retCode] { + set retCode 0 + } + return [expr $retCode] +} + +######################################################################################## +# Procedure: defineCommand::getAllRegisteredCommands +# +# Description: This command gets the Tcl command list +# +######################################################################################## +proc defineCommand::getAllRegisteredCommands { } \ +{ + set retCode 0 + + variable commandList + + return $commandList +} + +######################################################################################## +# Procedure: defineCommand::registerCommand +# +# Description: This command registers a new Tcl command +# +######################################################################################## +proc defineCommand::registerCommand {testCmd} \ +{ + set retCode 0 + + global defineCommandParms + variable commands + variable commandList + + + if [isRegistered $testCmd] { + debugMsg "registerCommand: $testCmd is already registered" + set retCode 1 + } else { + + lappend commandList $testCmd + + set parmArray [format %sParms $testCmd] + set methodArray [format %sMethods $testCmd] + global $parmArray $methodArray + + #define namespace for testCmd + #NOTE: Not defined variable here, may define them in proc testCmd::setDefault later. + # Defined results and dataVerify namespaces twice in two files. + # Defined tclClient and advancedTestParameter namespaces which we may not need. + # We need modify those later. + namespace eval ::$testCmd {} { + } + + # assign global parms automatically on registration + foreach parm [array names defineCommandParms] { + set ${parmArray}($parm) $defineCommandParms($parm) + } + foreach command [array names commands] { + set ${methodArray}($command) $commands($command) + } + set cmdLine "proc ::$testCmd \{\{method_name \{\}\} \{args \{\}\}\} \{return \[parseCmd $testCmd \$method_name args\]\}" + eval $cmdLine + + # create the default set of methods + foreach command [array names commands] { + registerMethod $testCmd $command + } + } + + return $retCode +} + + +######################################################################################## +# Procedure: defineCommand::registerMethod +# +# Description: This command registers a new method for this Tcl command +# +# Arguments: +# +# Results: 0 if succeeded. 1 otherwise. +# +######################################################################################## +proc defineCommand::registerMethod {testCmd method {parameterList {}}} \ +{ + set retCode 0 + + if {![isRegistered $testCmd]} { + puts "$testCmd is not yet registered, registering method failed." + set retCode 1 + } else { + set methodArray [format %sMethods $testCmd] + + global $methodArray + + set method_name [format "%s_%s" $testCmd $method] + + if [info exists ${methodArray}($method)] { + foreach parm $parameterList { + if {[lsearch [eval set ${methodArray}($method)] $parm] == -1} { + lappend ${methodArray}($method) $parm + } + } + } else { + set ${methodArray}($method) $parameterList + } + + if [catch {info body $method_name}] { + if {$method == "setDefault"} { + set defaultArray [format %sDefaultVals $testCmd] + set cmdLine "proc \:\:$testCmd\:\:$method \{\{args \{\}\}\} \{global $defaultArray; \ + foreach name \[array names ${defaultArray}\] { \ + $testCmd config -\$name \$${defaultArray}(\$name); \ + }; \ + return 0\}" + eval $cmdLine + } + } + } + return $retCode +} + + +######################################################################################## +# Procedure: defineCommand::registerParameter +# +# Description: This command registers a new parameter for this Tcl command +# +# Arguments: args - must be in the following format. +# -command command \ +# -parameter parameter \ +# -type type \ +# -defaultValue defaultValue \ +# -validValues validValues \ +# -validRange validRange \ +# -validateProc validateProc \ +# -access access +# where -command, -parameter, and -type are mandatory. +# +# Results: 0 if succeeded. 1 otherwise. +# +######################################################################################## +proc defineCommand::registerParameter {args} \ +{ + set retCode 0 + + # + # If the argument comes in as a list. Take away the curly braces around it. + # + if {[regexp {^\{} $args]} { + regsub -all {(^{|}$)} $args "" args + } + + # + # This one is different in that the args could contain multiple -validateProc. + # + set validateProcArg {} + + foreach arg $args { + + if {[regexp {^-[a-zA-Z].+} $arg]} { + # + # Current argument is preceeded by '-'. + # + switch -- $arg { + "-command" - + "-parameter" - + "-type" - + "-defaultValue" - + "-validValues" - + "-validRange" - + "-validateProc" - + "-access" - + "-help" { + set currentOption [string trimleft $arg -] + set currentAction getValue + } + default { + set retCode 1 + break + } + } + } else { + if {[info exists currentAction]} { + switch $currentAction { + "getValue" { + if {$currentOption == "validateProc"} { + lappend ${currentOption}Arg $arg + } else { + set ${currentOption}Arg $arg + } + + unset currentOption + unset currentAction + } + default { + set retCode 1 + break + } + } + } else { + set retCode 1 + break + } + } + } + + if {$retCode == 0} { + # + # Test command name, parameter name, and parameter type are mandatory when + # registring a test parameter. + # + if {[info exists commandArg] == 0} { + + set retCode 1 + + } elseif {[info exists parameterArg] == 0} { + + set retCode 1 + + } elseif {[info exists typeArg] == 0} { + + set retCode 1 + + } elseif {[info exists validValuesArg] && [info exists validRangeArg]} { + + set retCode 1 + + } + } + + if {$retCode == 1} { + puts "Invalid arguments: \"$args\"." + return $retCode + } + + if {![isRegistered $commandArg]} { + puts "$commandArg is not yet registered, registering parameter failed." + set retCode 1 + } else { + + if {[info exists defaultValueArg] == 0} { + set defaultValueArg 0 + } + + # + # $parmArray - contains the values defined by the option -parameter + # $methodArray - contains the values defined by the option -command + # $validateArray - contains the values defined by the option -validValues + # $defaultArray - contains the values defined by the option -defaultValue + # $validRangeArray - contains the values defined by the option -validRange + # $validateProcArray - contains the values defined by the option -validateProc + # $typeArray - contains the values defined by the option -type + # $helpArray - contains the values defined by the option -help + # + set parmArray ${commandArg}Parms + set methodArray ${commandArg}Methods + set validateArray ${commandArg}ConfigVals + set defaultArray ${commandArg}DefaultVals + set validRangeArray ${commandArg}ValidRange + set validateProcArray ${commandArg}ValidateProc + set typeArray ${commandArg}Type + set helpArray ${commandArg}Help + + global $parmArray $methodArray $validateArray $defaultArray + global $validRangeArray $validateProcArray $typeArray $helpArray + + foreach parm $parameterArg { + set ${parmArray}($parm) $defaultValueArg + set ${defaultArray}($parm) $defaultValueArg + } + + if {[info exists access] == 0} { + set access rw + } + + switch $access { + r { + defineCommand registerMethod $commandArg cget $parameterArg + } + w { + defineCommand registerMethod $commandArg config $parameterArg + } + wr - + default { + defineCommand registerMethod $commandArg cget $parameterArg + defineCommand registerMethod $commandArg config $parameterArg + } + } + + if {[info exists validValuesArg] && ($validValuesArg != "")} { + foreach parm $parameterArg { + set ${validateArray}($parm) $validValuesArg + } + } + + if {[info exists validRangeArg]} { + foreach parm $parameterArg { + set ${validRangeArray}($parm) $validRangeArg + } + } + + if {[info exists validateProcArg]} { + foreach parm $parameterArg { + set ${validateProcArray}($parm) $validateProcArg + } + } + + if {[info exists typeArg]} { + foreach parm $parameterArg { + set ${typeArray}($parm) $typeArg + } + } + + if {[info exists helpArg]} { + foreach parm $parameterArg { + set ${helpArray}($parm) $helpArg + } + } + } + + return $retCode +} + + diff --git a/dep/tclclient/ixTcl1.0/Generic/defineTest.tcl b/dep/tclclient/ixTcl1.0/Generic/defineTest.tcl new file mode 100644 index 00000000..bb1db43f --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/defineTest.tcl @@ -0,0 +1,161 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: defineTest.tcl +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 12-13-1999 DS Genesis +# +# Description: This file contains the commands for all the tests. When a new test +# is added, create a new proc for that test here. +# +################################################################################## + +namespace eval defineTest {} \ +{ + variable defaultParms + variable defaultParmType + variable defaultCommands + +# moved parameter:numtrials to proc defineTest::registerCommand. +# set defaultParms(numtrials) 1 + set defaultParms(duration) 20 + set defaultParms(waitResidual) 2 + set defaultParms(testName) "" + set defaultParms(framesize) 64 + set defaultParms(framesizeList) {} + + +# set defaultParmType(numtrials) integer + set defaultParmType(duration) integer + set defaultParmType(waitResidual) integer + set defaultParmType(testName) string + set defaultParmType(framesize) integer + set defaultParmType(framesizeList) integerList + + set defaultCommands(config) [array names defaultParms] + set defaultCommands(cget) [array names defaultParms] + set defaultCommands(registerResultVars) {} + set defaultCommands(start) {} +} + +######################################################################################## +# Procedure: initializeDefineTest +# +# Description: This command is used to initialize the defineTest commands +# +######################################################################################## +proc initializeDefineTest {} \ +{ + defineTest::initialize +} + +######################################################################################## +# Procedure: defineTest::initialize +# +# Description: This command is used to initialize the defineTest commands +# +######################################################################################## +proc defineTest::initialize {} \ +{ + set retCode 0 + + defineCommand registerCommand defineTest + defineCommand registerMethod defineTest registerCommand + defineCommand registerMethod defineTest registerParameter + defineCommand registerMethod defineTest registerTest + + return $retCode +} + + +######################################################################################## +# Procedure: defineTest::registerCommand +# +# Description: This command registers a new Tcl test command +# +######################################################################################## +proc defineTest::registerCommand {testCmd} \ +{ + variable defaultParms + variable defaultParmType + variable defaultCommands + + set retCode 0 + + defineCommand registerCommand $testCmd + + # assign global parms automatically on registration + foreach parm [array names defaultParms] { + defineCommand registerParameter -command $testCmd -parameter $parm -defaultValue $defaultParms($parm) -type $defaultParmType($parm) + } + + defineTest registerParameter -command $testCmd -parameter numtrials -defaultValue 1 -type integer -validRange {1 NULL} + defineTest registerParameter -command $testCmd -parameter staggeredStart -defaultValue notStaggeredStart -type string -validValues {staggeredStart notStaggeredStart true false} + defineTest registerParameter -command $testCmd -parameter framesize -defaultValue 64 -type integer + defineTest registerParameter -command $testCmd -parameter framesizeList -defaultValue {} -type integerList + + foreach method [array names defaultCommands] { + defineCommand registerMethod $testCmd $method $defaultCommands($method) + } + + return $retCode +} + + +######################################################################################## +# Procedure: defineTest::registerParameter +# +# Description: This command registers a new parameter for this Tcl test command +# +######################################################################################## +proc defineTest::registerParameter {args} \ +{ + return [defineCommand registerParameter $args] +} + + +######################################################################################## +# Procedure: defineTest::registerTest +# +# Description: This command registers a new test (start command) for this Tcl command +# +######################################################################################## +proc defineTest::registerTest {testCmd testSubCmd {testSubCmdParameterList ""}} \ +{ + return [defineCommand registerMethod $testCmd start $testSubCmd] +} + + + diff --git a/dep/tclclient/ixTcl1.0/Generic/dhcpExchange.tcl b/dep/tclclient/ixTcl1.0/Generic/dhcpExchange.tcl new file mode 100644 index 00000000..2b52ea7c --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/dhcpExchange.tcl @@ -0,0 +1,2605 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: dhcpExchange.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 12-02-1998 Debby Stopp +# +# Description: This file contains procedures that send out DHCP exchange messages and +# conforms to the DHCP specifications RFC 2131 and RFC 1533 with the following +# exceptions: +# +# Timers 1 & 2 not implemented in a port by port fashion +# Lease expiration not handled in a port by port fashion +# Release not implemented. +# +######################################################################################## + +set nullIP "0.0.0.0" +set broadcastIP "255.255.255.255" + +# +# Wrappers routines for dhcpClient namespace procedures +# +proc dhcpSetState {chassis card port newState} {dhcpClient::SetState $chassis $card $port $newState} +proc dhcpGetState {chassis card port} {dhcpClient::GetState $chassis $card $port} +proc dhcpSetLease {chassis card port lease} {dhcpClient::SetLease $chassis $card $port $lease} +proc dhcpGetLease {chassis card port} {dhcpClient::GetLease $chassis $card $port} +proc dhcpSetIP {chassis card port ip} {dhcpClient::SetIP $chassis $card $port $ip} +proc dhcpGetIP {chassis card port} {dhcpClient::GetIP $chassis $card $port} +proc dhcpSetServer {chassis card port server} {dhcpClient::SetServer $chassis $card $port $server} +proc dhcpGetServer {chassis card port} {dhcpClient::GetServer $chassis $card $port} +proc dhcpGetTimer {timer} {dhcpClient::GetTimer $timer} +proc dhcpStartTimers {lease {timer1 0} {timer2 0}} {dhcpClient::StartTimers $lease $timer1 $timer2} +proc dhcpStartTimer {timer lease} {dhcpClient::StartTimer $timer $lease} +proc dhcpStopTimers {} {dhcpClient::StopTimers} +proc dhcpStopTimer {timer} {dhcpClient::StopTimer $timer} +proc dhcpSetStreamRegion {region} {dhcpClient::SetStreamRegion $region} +proc dhcpGetStreamRegion {} {dhcpClient::GetStreamRegion} +proc dhcpSetPortList {portList} {dhcpClient::SetPortList $portList} +proc dhcpGetPortList {} {dhcpClient::GetPortList} +proc dhcpStop {} {dhcpClient::Stop} +proc dhcpStopPort {chassis card port {release false}} {dhcpClient::StopPort $chassis $card $port $release} +proc dhcpEnableStateMachine {{stateList dhcpClient::stateList} \ + {eventList dhcpClient::eventList} \ + {actionList dhcpClient::actionList}} \ + {dhcpClient::Initialize $stateList $eventList $actionList} +proc dhcpDisableStateMachine {{stateList dhcpClient::stateList} \ + {eventList dhcpClient::eventList} \ + {actionList {}}} {dhcpClient::Initialize $stateList $eventList $actionList} + + +namespace eval dhcpClient { +variable debugLevel +#set debugLevel [list default state] +#set debugLevel [list default] +set debugLevel [list] +# +# Public Procedures: dhcpEnableStateMachine (dhcpClient::Initialize) +# dhcpDisableStateMachine (dhcpClient::Initialize) +# dhcpStop (dhcpClient::Stop) +# dhcpSetState (dhcpClient::SetState) +# dhcpGetState (dhcpClient::GetState) +# dhcpSetLease (dhcpClient::SetLease) +# dhcpGetLease (dhcpClient::GetLease) +# dhcpGetTimer (dhcpClient::GetTimer) +# dhcpStartTimers (dhcpClient::StartTimers) +# dhcpStartTimer (dhcpClient::StartTimer) +# dhcpStopTimers (dhcpClient::StopTimers) +# dhcpStopTimer (dhcpClient::StopTimer) +# dhcpSetIP (dhcpClient::SetIP) +# dhcpGetIP (dhcpClient::GetIP) +# dhcpSetServer (dhcpClient::SetServer) +# dhcpGetServer (dhcpClient::GetServer) +# dhcpSetStreamRegion (dhcpClient::SetStreamRegion) +# dhcpGetStreamRegion (dhcpClient::GetStreamRegion) +# dhcpSetPortList (dhcpClient::SetPortList) +# dhcpGetPortList (dhcpClient::GetPortList) +# dhcpClient::GetStateNames +# dhcpClient::GetStateCodes +# dhcpClient::GetStateName +# dhcpClient::ValidState +# dhcpClient::GetEventCodes +# dhcpClient::GetEventName +# dhcpClient::ValidEvent +# dhcpClient::debug + +# +# Private Procedures: dhcpClient::StateLookup +# dhcpClient::InitStateTable +# dhcpClient::ActionNull +# dhcpClient::ActionRenew +# dhcpClient::ActionRebound +# +# Private Variables: dhcpClient::dhcpLeaseRecord +# dhcpClient::stateList +# dhcpClient::eventList +# dhcpClient::actionList +# dhcpClient::streamRegion +# dhcpClient::portList +# +# +# DHCP Stream Building. +# +# When a DHCP lease needs to renew, the client must interrupt it's current +# activities and send a renewal request. This disrrupts the current +# stream construct in region 0. Rather than use region 0, DHCP streams +# are constructed in region 1 and an asynchonous transmission is used for +# the renewal process. +# +variable streamRegion +set streamRegionMax 7 +set streamRegionMin 0 + +variable portList +# +# DHCP timers. +# +# Following the DHCP negotiation, the client has (hopefully) been supplied +# with: IP address, lease duration, and a DHCP server ID. Each of +# these items are necessary for renewal of the IP lease. +# +# Three time periods are associated with any given lease: +# Timer1 = lease * .50 +# Timer2 = lease * .875 +# Expiration = lease +# +# At the expiry of timer1, a dhcp Client tries to renew from current server +# At the expiry of timer2, a dhcp Client tries to rebind from any server +# At the expiry of lease, a dhcp Client returns to the Init state and starts discovery. +# +# However, IXIA is not attempting to implement a DHCP client state +# machine, and simply needs the insure that a renewal or rebind +# request maintains an IP address so that performance testing may +# resume. A single timer, rather than a timer for each port, initiates +# the renewal process for ALL ports. +# +variable dhcpLeaseRecord +# dhcpLeaseRecord(timer1) = identification of TCL timer (after), timer 1 +# dhcpLeaseRecord(timer2) = identification of TCL timer (after), timer 2 +# dhcpLeaseRecord(leaseExpire) = identification of TCL timer (after), lease expiration +# dhcpLeaseRecord(c,l,p,state) = dhcp state for this port +# dhcpLeaseRecord(c,l,p,lease) = duration of lease +# dhcpLeaseRecord(c,l,p,ip) = leased IP address +# dhcpLeaseRecord(c,l,p,server) = dhcp server for leased IP address +# +# +# DHCP States: A state table allows the dhcpClient to respond to +# background events (ie timers), the following are valid states: +# +# Valid states: +# idle: Entered after client initially boots or lease expires +# select: Entered when client broadcasts for an IP address +# request: Entered when client has accepted an IP lease offer +# bound: Entered when client & server have agreed upon lease +# renew: Entered when timer1 expires +# rebind: Entered when/if timer2 expires +# +variable state +set state(idle) 0 +set state(select) 1 +set state(request) 2 +set state(bound) 3 +set state(renew) 4 +set state(rebind) 5 + +# +# +# DHCP State Table. +# +# The table is organized by state/event. For each state, and for each +# potential event, there is a corresponding action. If no action +# is desired for a particular event, the table element should +# contain a referance to the NULL routine. +# +# Currently this table is only being used with the renew & rebind +# states. +# +# Example: State/ +# Event Time Out SomeEvent +# +# idle dhcpActionNull dhcpActionNull +# select dhcpActionNull dhcpDoSomething +# request dhcpActionNull dhcpDoSomething +# bound dhcpActionRenew dhcpDoSomething +# renew dhcpActionRebind dhcpDoSomething +# rebind dhcpActionInit dhcpDoSomething +# +# The stateList contains a list of all known states. +# The eventList contains a list of all potential events. +# The actionList contains a list of all possible actions for each +# state. Note that an action *must* be listed for each event. +# +# +# IXIA's DHCP Client: +# +# This is not a true DHCP client implementation (the DHCP Client state +# machine belongs in the protocol server). IXIA requires +# DHCP only for the establishment of IP address so that performance +# testing may ensue. Therefore, when timer1 expires, for any port, +# the assumption is made that all port timers are about to expire and +# all ports are 'renewed' at that time. +# +variable stateTable +variable stateList +set stateList [list $state(idle) $state(select) $state(request) \ + $state(bound) $state(renew) $state(rebind)] +# +# Known events +variable event +set event(timeOut) 1 +# +variable eventList +set eventList [list $event(timeOut)] +# +# Action list by state. +# The # of actions per state must equal the size of the eventList. +# Set actionList [list] to disable the state machine. +# +# TimeOut +# ------------ +variable action +set action(idle) [list ActionNull ] +set action(select) [list ActionNull ] +set action(request) [list ActionNull ] +set action(bound) [list ActionRenew ] +set action(renew) [list ActionRebind] +set action(rebind) [list ActionInit ] + +variable actionList +set actionList [list $action(idle) $action(select) $action(request) \ + $action(bound) $action(renew) $action(rebind)] + +# DHCP Option Parameter List. +# +# When the dhcp client issues a message, it has the option of specifying a +# list of configuration parameters it is interested in recieving from the +# dhcp server. The parameter list must be identical in all commands, except +# dhcpRelease where no parameter list is specified. Refer to section 9.6 of +# RFC 1533. +variable paramRequestList + + +# DHCP Magic Cookie +# As defined in RFC 2131 (section 3), the first 4 bytes of option data +# are always the dhcp magic cookie: 99,130, 83, 99. +variable magicCookie +set magicCookie {63 82 53 63} + +# DHCP Start Time +# The time that the dhcp process was started. +variable startTime + +} +## End of DhcpClient namespace + +######################################################################################## +# Procedure: DHCPdiscoverIP +# +# Description: This procedure uses the DHCP protocol to get an IP address for each +# client +# +# Argument(s): +# PortList list of ports, ie, ixgSortMap +# startState: The state to start in, default is state(discover): +# 0 = discover (issue discover message) +# 1 = offer (rx offer) +# 2 = request (issue request) +# 3 = ack (rx ack) +# 4 = done (dhcp exchange complete) +# +######################################################################################## +proc DHCPdiscoverIP {PortList {startState 0} {stateMachine disable}} \ +{ + global dhcpAck dhcpNak dhcpOffer + + upvar $PortList portList + + set retCode 0 + + # Initialize state variables + set state(discover) 0 + set state(offer) 1 + set state(request) 2 + set state(ack) 3 + set state(done) 4 + + + # Save the port list. + set discoverList [getAllPorts portList] + dhcpSetPortList $discoverList + + # Enable/Disable state machine for IP renewal + if {$startState == $state(discover)} { + if {$stateMachine == "enable"} { + dhcpEnableStateMachine + } else { + dhcpDisableStateMachine + } + } + + set discoverRetries 1 + set maxRetries 3 + set NAKCounter 0 + set currState $startState + + # Start DHCP State Machine. + while {$currState != $state(done)} { + switch $currState { + 0 { + if [send_DHCP_discover $discoverList] { + errorMsg "Error sending discover frames. Discover failed." + set currState $state(done) + set retCode 1 + } else { + incr currState + } + } + + 1 { + if [get_DHCP_offer discoverList] { + if {$discoverRetries < $maxRetries} { + incr discoverRetries + set currState $state(discover) + errorMsg "Discover failed on one or more ports, retrying discover $discoverRetries of $maxRetries times..." + } else { + errorMsg "Error receiving offer frames. Discover failed." + set currState $state(done) + set retCode 1 + } + } else { + incr currState + } + } + + 2 { + set requestList [getAllPorts portList] + if [send_DHCP_request $requestList] { + errorMsg "Error sending request frames. Discover/Renew failed." + set currState $state(done) + set retCode 1 + } else { + set currState $state(ack) + } + } + + 3 { + if [get_DHCP_ack $requestList command] { + errorMsg "Error receiving ack frames. Discover failed.\n" + set currState $state(done) + set retCode 1 + } else { + if {$command == $dhcpAck} { + set NAKCounter 0 + set currState $state(done) + } + if {$command == $dhcpNak} { + set discoverList $requestList + incr NAKCounter + if {$NAKCounter >= 3} { + set currState $state(done) + } else { + set currState $state(discover) + } + } + } + } + + default { + errorMsg "Error - invalid state $currState" + set retCode 1 + break + } + } + } + + return $retCode +} + + +######################################################################################## +# Procedure: send_DHCP_discover +# +# Description: This command sends a DHCP discover frame (client broadcast to locate +# available servers) +# +# Argument(s): +# portList list of ports to tx dhcp discover frames to +# +######################################################################################## +proc send_DHCP_discover {portList} \ +{ + return [sendDhcpPacket $portList dhcpDiscover] +} + + +######################################################################################## +# Procedure: get_DHCP_offer +# +# Description: This command gets a DHCP offer frame sent from the DUT +# +# Argument(s): +# PortList list of ports to tx dhcp discover frames to, removes ports from the +# portList if they received an offer. +# +######################################################################################## +proc get_DHCP_offer {PortList} \ +{ + upvar $PortList portList + + global dhcpOffer + + set retCode $::TCL_OK + + set wait [learn cget -dhcpWaitTime] + + foreach portMap [lnumsort $portList] { + scan $portMap "%d %d %d" chassis lm port + + if {[get_DHCP_packet $chassis $lm $port dhcpOffer $wait] == $dhcpOffer} { + if [ip get $chassis $lm $port] { + errorMsg "Error getting ip on port $chassis $lm $port" + continue + } + ip config -sourceIpAddr [dhcp cget -yourIpAddr] + + set msg "DHCP Gateway" + if {![dhcp getOption dhcpGateways]} { + if {[dhcp cget -optionDataLength] != 4} { + errorMsg "Invalid IP address in $msg option" + continue + } + + ip config -destDutIpAddr [dhcp cget -optionData] + } + + set msg "DHCP Server Identifer" + if {![dhcp getOption dhcpSvrIdentifier]} { + if {[dhcp cget -optionDataLength] != 4} { + errorMsg "Invalid IP address in $msg option" + continue + } + + dhcp config -serverIpAddr [dhcp cget -optionData] + if [dhcp set $chassis $lm $port] { + errorMsg "Error setting dhcp on port $chassis $lm $port" + continue + } + } + + ip config -ipProtocol udp + if [ip set $chassis $lm $port] { + errorMsg "Error setting ip on port $chassis $lm $port" + continue + } + + set indx [lsearch $portList [list $chassis $lm $port]] + if {$indx != -1} { + set portList [lreplace $portList $indx $indx] + } + logMsg "Got DHCP OFFER on $chassis,$lm,$port, offered IP: [dhcp cget -yourIpAddr]" + } + } + logMsg "" + + if {[llength $portList] > 0} { + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################################## +# Procedure: send_DHCP_request +# +# Description: This command sends a DHCP request frame to the DUT +# +# Argument(s): +# portList list of ports to tx dhcp request frames to +# +######################################################################################## +proc send_DHCP_request {portList} \ +{ + return [sendDhcpPacket $portList dhcpRequest] +} + + +######################################################################################## +# Procedure: get_DHCP_ack +# +# Description: This command gets a DHCP ack frame sent from the DUT +# +# Argument(s): +# portList list of ports to tx dhcp request frames to +# +######################################################################################## +proc get_DHCP_ack {portList command} \ +{ + global dhcpAck dhcpNak + variable dhcpClient::state + + upvar $command Command + + set retCode 0 + + ipAddressSetDefault + + foreach portMap [lnumsort $portList] { + scan $portMap "%d %d %d" chassis lm port + + # Get the dhcp packet. + set messageType [get_DHCP_packet $chassis $lm $port [list dhcpAck dhcpNak]] + if {$messageType != $dhcpAck && \ + $messageType != $dhcpNak} { + set retCode 1 + continue + } + + + # Reset the timers. + dhcpStopTimer timer1 + dhcpStopTimer timer2 + dhcpStopTimer leaseExpire + + # Handle DHCP ACK. + if {$messageType == $dhcpAck} { + + set Command $dhcpAck + + # Collect & validate the dhcp server id. + if [dhcp getOption dhcpSvrIdentifier] { + errorMsg "DHCP Server Identifier option not found in dhcpAck frame" + set retCode 1 + continue + } + set dhcpSvrIdentifier [dhcp cget -optionData] + + if {[dhcp cget -optionDataLength] != 4} { + errorMsg "Invalid IP address in DHCP Server ID option" + set retCode 1 + continue + } + + + # Collect and validate the lease duration. + if [dhcp getOption dhcpIPAddrLeaseTime] { + errorMsg "DHCP IP Lease Duration option not found in dhcpAck frame" + set retCode 1 + continue + } + set dhcpIPAddrLeaseTime [dhcp cget -optionData] + + set dhcpRenewalTimeValue 0 + if {![dhcp getOption dhcpRenewalTimeValue]} { + set dhcpRenewalTimeValue [dhcp cget -optionData] + } + set dhcpRebindingTimeValue 0 + if {![dhcp getOption dhcpRebindingTimeValue]} { + set dhcpRebindingTimeValue [dhcp cget -optionData] + } + + + # Set IP address into ip configuration. + if [ip get $chassis $lm $port] { + errorMsg "Error getting ip on port $chassis $lm $port" + set retCode 1 + continue + } + ip config -sourceIpAddr [dhcp cget -yourIpAddr] + + + set msg "DHCP Gateway" + if {![dhcp getOption dhcpGateways]} { + if {[dhcp cget -optionDataLength] != 4} { + errorMsg "Invalid IP address in $msg option" + set retCode 1 + continue + } + + ip config -destDutIpAddr [dhcp cget -optionData] + } + + set msg "DHCP Server Identifer" + if {![dhcp getOption dhcpSvrIdentifier]} { + if {[dhcp cget -optionDataLength] != 4} { + errorMsg "Invalid IP address in $msg option" + set retCode 1 + continue + } + set dhcpSvrIdentifier [dhcp cget -optionData] + dhcp config -serverIpAddr $dhcpSvrIdentifier + if [dhcp set $chassis $lm $port] { + errorMsg "Error setting dhcp on port $chassis $lm $port" + set retCode 1 + continue + } + } + + ip config -ipProtocol udp + if [ip set $chassis $lm $port] { + errorMsg "Error setting ip on port $chassis $lm $port" + set retCode 1 + continue + } + logMsg "Got DHCP ACK on $chassis,$lm,$port, allocated IP: [ip cget -sourceIpAddr] \ + from server $dhcpSvrIdentifier" + + # add the new ip address to the ipAddressTable + ipAddressSetDefault + if [updateIpAddressTable $chassis $lm $port] { + set retCode 1 + continue + } + +# TBD. +# interfaceTable::setDefault +# set protocolName [getProtocolName [protocol cget -name]] +# switch $protocolName { +# ip { +# set protocolList $::ipV4 +# if {[interfaceTable::configurePort $chassis $lm $port $protocolList]} { +# set retCode $::TCL_ERROR +# continue +# } +# } +# ipV6 { +# set protocolList $::ipV6 +# if {[interfaceTable::configurePort $chassis $lm $port $protocolList]} { +# set retCode $::TCL_ERROR +# continue +# } +# } +# } +# + # + # This could be a renewal of this IP address, if so, it will + # already have a lease record with timers running. Stop any + # associated timers. + # + + + # Create/Update dhcpLeaseRecord and start lease timers. + dhcpSetIP $chassis $lm $port [dhcp cget -yourIpAddr] + dhcpSetServer $chassis $lm $port $dhcpSvrIdentifier + dhcpSetLease $chassis $lm $port $dhcpIPAddrLeaseTime + dhcpSetState $chassis $lm $port $dhcpClient::state(bound) + + } + + # Handle dhcpNAK. + if {$messageType == $dhcpNak} { + set dhcpError "" + if ![dhcp getOption dhcpMessage] { + set dhcpError [dhcp cget -optionData] + } + logMsg "Got DHCP NAK on $chassis,$lm,$port, allocated. $dhcpError" + + dhcpStopPort $chassis $lm $port + set Command $dhcpNak + } + } + + if {$messageType == $dhcpAck} { + dhcpStartTimers \ + $dhcpIPAddrLeaseTime $dhcpRenewalTimeValue $dhcpRebindingTimeValue + } + + + logMsg "" + + return $retCode +} + + +######################################################################################## +# Procedure: send_DHCP_release +# +# Description: Given a portMap, sends a dhcpRelease to the DUT. +# +# Argument(s): +# chassis +# lm +# port +# +# Output: retCode : 0 if okay, else +# 1 if failure +# +######################################################################################## +proc send_DHCP_release {portList} \ +{ + set retCode [sendDhcpPacket $portList dhcpRelease] + + return $retCode +} + + + +######################################################################################## +############ DHCP Utility Procedures ########### +######################################################################################## + + +######################################################################################## +# Procedure: setupUDPbootp +# +# Description: This procedure sets up the UDP bootp ports +# +# Argument(s): +# chassis +# lm +# port +# +######################################################################################## +proc setupUDPbootp {chassis lm port} \ +{ + set retCode 0 + + udp setDefault + udp config -sourcePort bootpClientPort + udp config -destPort bootpServerPort + if [udp set $chassis $lm $port] { + errorMsg "Error setting udp on port $chassis $lm $port" + set retCode 1 + } + + return $retCode +} + + +######################################################################################## +# Procedure: setupDhcpBroadcastIP +# +# Description: This procedure sets up broadcast IP addr for destination & null IP for +# source (don't know what our IP address is at this point). +# +# Argument(s): +# chassis +# lm +# port +# address : defaults to broadcast, +# +######################################################################################## +proc setupDhcpBroadcastIP {chassis lm port} \ +{ + global nullIP + global broadcastIP + + set retCode 0 + + #ip setDefault + if [ip get $chassis $lm $port] { + errorMsg "Error getting ip on port $chassis $lm $port" + set retCode 1 + } + ip config -sourceIpAddr $nullIP + ip config -destIpAddr $broadcastIP + ip config -ipProtocol udp + ip config -ttl 128 + ip config -identifier 512 + if [ip set $chassis $lm $port] { + errorMsg "Error setting ip on port $chassis $lm $port" + set retCode 1 + } + + return $retCode +} + +######################################################################################## +# Procedure: setupDhcpUnicastIP +# +# Description: This procedure sets up a unicast IP addr for destination +# +# Argument(s): +# chassis +# lm +# port +# sourceIpAddr: source Ip Address +# destIpAddr: destination Ip Address +# +######################################################################################## +proc setupDhcpUnicastIP {chassis lm port sourceIpAddr destIpAddr} \ +{ + + set retCode 0 + + #ip setDefault + if [ip get $chassis $lm $port] { + errorMsg "Error getting ip on port $chassis $lm $port" + set retCode 1 + } + ip config -sourceIpAddr $sourceIpAddr + ip config -destIpAddr $destIpAddr + ip config -ipProtocol udp + ip config -ttl 128 + ip config -identifier 512 + if [ip set $chassis $lm $port] { + errorMsg "Error setting ip on port $chassis $lm $port" + set retCode 1 + } + + return $retCode +} + + + + +######################################################################################## +# Procedure: setupDefaultDhcpParameters +# +# Description: This procedure sets up the default DHCP parameters for discover & request +# +# Argument(s): +# chassis +# lm +# port +# transactionID - transaction ID +# txSA - mac address of the chassis lm port +# +######################################################################################## +proc setupDefaultDhcpParameters {chassis lm port transactionID txSA {clientIpAddr "0.0.0.0"}} \ +{ + global nullIP + + dhcp setDefault + dhcp config -opCode $::dhcpBootRequest + dhcp config -clientIpAddr $clientIpAddr + dhcp config -yourIpAddr $nullIP + dhcp config -serverIpAddr $nullIP + dhcp config -relayAgentIpAddr $nullIP + dhcp config -clientHwAddr $txSA + dhcp config -transactionID $transactionID + dhcp config -seconds [dhcpClient::getStartTime] +} + + +######################################################################################## +# Procedure: setDhcpOptions +# +# Description: This procedure sets the options specified in the optionList +# +# Argument(s): +# OptionList - list of options & data to set using dhcp setOption; data must be in +# in the appropriate byte format before passing through to this proc +# +######################################################################################## +proc setDhcpOptions {OptionList} \ +{ + upvar $OptionList optionList + set retCode 0 + + # make sure there's an 'end' at the back end of each set of options + lappend optionList dhcpEnd dhcpEnd + + foreach {option data} $optionList { + dhcp config -optionData $data + if [dhcp setOption $option] { + errorMsg "Error setting DHCP option <$option>" + set retCode 1 + continue + } + } + + return $retCode +} + + +######################################################################################## +# Procedure: sendDhcpPacket +# +# Description: This procedure sends a Dhcp packet to each Tx port in the map +# +# Argument(s): portList: list of ports to send DHCP packets: {{c l p} {c l p}} +# opcode: operation to perform: dhcpDiscover +# dhcpRequest +# dhcpRelease +# +# Returns: 0 if successful, else +# 1 if failure +# +######################################################################################## +proc sendDhcpPacket {portList opcode} \ +{ + set retCode 0 + + # save original protocol config + set tempApp [protocol cget -appName] + set tempName [protocol cget -name] + protocol config -appName Dhcp + protocol config -name ip + + foreach portMap [lnumsort $portList] { + scan $portMap "%d %d %d" c l p + + # Temporary workaround (DHCP was consumed by Protocol Server) + if [protocolServer get $c $l $p] { + errorMsg "Error getting protocolServer on port [getPortId $c $l $p]" + set retCode 1 + continue + } + + set enableArp [protocolServer cget -enableArpResponse] + if {$enableArp} { + protocolServer config -enableArpResponse false + if [protocolServer set $c $l $p] { + errorMsg "Error setting protocolServer on port [getPortId $c $l $p]" + set retCode 1 + } + } + + if [buildDhcpPacket $c $l $p $opcode] { + errorMsg "Error building dhcp packet for port [getPortId $c $l $p]" + set retCode 1 + continue + } + } + + # now put back original protocol config + protocol config -appName $tempApp + protocol config -name $tempName + + # if this port was *ever* in packetGroup mode, change it or we won't be able to capture... + if [setCaptureMode portList write] { + return -code error + } + + if {![writeConfigToHardware portList] && ![zeroStats portList] && ![startCapture portList]} { + logMsg "" + foreach portMap [lnumsort $portList] { + scan $portMap "%d %d %d" c l p + + logMsg "----->Sending $opcode frame from [getPortId $c $l $p] to DHCP server." + + if [startPortTx $c $l $p] { + errorMsg "Error starting Tx on port [getPortId $c $l $p]" + set retCode 1 + } + } + logMsg "" + if {[learn cget -waitTime] > 1000} { + logMsg "Waiting on DHCP response for [expr [learn cget -waitTime]/1000] second(s)..." + } + after [learn cget -waitTime] + + } else { + set retCode 1 + } + + # Temporary workaround (DHCP was consumed by Protocol Server) + # If arp was enabled in the first place, reenable it here... + if {$enableArp} { + foreach portMap [lnumsort $portList] { + scan $portMap "%d %d %d" c l p + + if [protocolServer get $c $l $p] { + errorMsg "Error getting protocolServer on port [getPortId $c $l $p]" + set retCode 1 + continue + } + + protocolServer config -enableArpResponse true + if [protocolServer set $c $l $p] { + errorMsg "Error setting protocolServer on port [getPortId $c $l $p]" + set retCode 1 + } + } + } + + return $retCode +} + + +######################################################################################## +# Procedure: buildDhcpPacket +# +# Description: This procedure builds a Dhcp packet +# +# Argument(s): +# chassis +# lm +# port +# opcode opcode, ie, dhcpDiscover, dhcpRequest, dhcpRelease +# } else if {$state == $dhcpClient::state(rebind)} { +# if {$state == $dhcpClient::state(renew)} { +# +######################################################################################## +proc buildDhcpPacket {chassis lm port opcode} \ +{ + + global nullIP broadcastIP + variable dhcpClient::state + + set retCode 0 + + set clientIpAddr $nullIP + set txAddress $nullIP + + set ethernetType [protocol cget -ethernetType] + protocol config -ethernetType $::noType + + set destMacAddress $::kBroadcastMacAddress + + udf setDefault + dhcp setDefault + filter setDefault + stream setDefault + filterPallette setDefault + + dhcpSetStreamRegion 0 + + # delete any existing streams first + if [port reset $chassis $lm $port] { + errorMsg "Error deleting streams on port $chassis $lm $port" + set retCode 1 + } + + # set the stream parameters. + set preambleSize 8 + set framesize [learn cget -framesize] + + set streamName $opcode + append streamName "Stream" + + disableUdfs {1 2 3 4} + + stream config -region [dhcpGetStreamRegion] + stream config -rateMode usePercentRate + stream config -name $streamName + stream config -framesize $framesize + stream config -dma stopStream + stream config -numFrames [learn cget -numDHCPframes] + stream config -fcs good + stream config -enableIbg false + stream config -enableIsg false + stream config -gapUnit gapNanoSeconds + + filter config -captureFilterError errGoodFrame + filter config -captureTriggerError errGoodFrame + + if [port get $chassis $lm $port] { + errorMsg "port $chassis $lm $port has not been configured yet" + set retCode 1 + } + + set txSA [port cget -MacAddress] + set transactionID [dhcpClient::getTransactionID $port] + + set optionList [list dhcpHostName [format "IxiaHost_%u.%u.%u" $chassis $lm $port]] + + + switch $opcode { + dhcpDiscover { +# set txAddress $broadcastIP + lappend optionList dhcpMessageType dhcpDiscover \ + dhcpClientId $txSA \ + dhcpParamRequestList [dhcpClient::getParameterRequestList] + + + # Capture Boot Replies with the dhcpMagicCookie +# filter config -captureTriggerPattern pattern1 +# filter config -captureFilterPattern pattern1 +# filterPallette config -pattern1 [dhcpClient::getMagicCookie] +# filterPallette config -patternOffset1 {278} + + filter config -captureTriggerPattern pattern1AndPattern2 + filter config -captureFilterPattern pattern1AndPattern2 + filterPallette config -pattern1 [format "%02x" $::dhcpBootReply] + filterPallette config -pattern2 [dhcpClient::getMagicCookie] + filterPallette config -patternOffset1 {42} + filterPallette config -patternOffset2 {278} + + if [filterPallette set $chassis $lm $port] { + errorMsg "Error setting filter pallette on $chassis,$lm,$port" + } + } + + dhcpRequest { + if [ip get $chassis $lm $port] { + errorMsg "port $chassis $lm $port has not been configured for IP yet" + set retCode 1 + } + set txIP [ip cget -sourceIpAddr] + + # we need to use the same transaction ID for the complete dhcp transaction... if we don't have a dhcp object + # for this port yet, setup a transaction ID for it using a random number, otherwise use the existing transactionID + # in the dhcp object. + if [dhcp get $chassis $lm $port] { + errorMsg "Error getting dhcp configuration for port $chassis $lm $port" + set retCode 1 + } else { + set transactionID [dhcp cget -transactionID] + } + + # + # Build option list based upon state (these are + # common to all dhcpRequest messages). + # + lappend optionList dhcpMessageType dhcpRequest \ + dhcpParamRequestList [dhcpClient::getParameterRequestList] + + + set portState [dhcpGetState $chassis $lm $port] + if ![dhcpClient::ValidState $portState] { + set portState 0 + } + + switch [dhcpClient::GetStateName $portState] { + + renew { + + # + # For state Renew, the following are required: + # - Server Id is not set + # - Requested IP is not set + # - ciaddr is set to Client's IP address + # - giaddr (dhcpRouter) is not set + # + set clientIpAddr [dhcpGetIP $chassis $lm $port] + set txAddress [dhcp cget -serverIpAddr] + set destMacAddress [port cget -DestMacAddress] + + lappend optionList dhcpClientId $txSA + } + + + rebind { + # + # For state Rebind, the following are required: + # - Server Id is not set + # - Requested IP is not set + # - ciaddr is set to Client's IP address + # + set clientIpAddr [dhcpGetIP $chassis $lm $port] + lappend optionList dhcpClientId $txSA \ + dhcpRouter [ip cget -destDutIpAddr] \ + dhcpSvrIdentifier $nullIP \ + dhcpRequestedIPAddr $nullIP + + } + + default { + # + # For states Select, the following are required: + # - Server Id is set to address supplied in offer + # - Requested IP is set to address supplied in offer + # - ciaddr is not set + # - giaddr is set to relay agent + # + lappend optionList dhcpClientId $txSA \ + dhcpRequestedIPAddr $txIP \ + dhcpRouter [ip cget -destDutIpAddr] \ + dhcpSvrIdentifier [dhcp cget -serverIpAddr] + } + } + # Capture Boot Replies + filter config -captureTriggerPattern pattern1 + filter config -captureFilterPattern pattern1 + filterPallette config -pattern1 [format "%02x" $::dhcpBootReply] + filterPallette config -patternOffset1 {42} + + if [filterPallette set $chassis $lm $port] { + errorMsg "Error setting filter pallette on $chassis,$lm,$port" + } + + } + dhcpRelease { + + # Collect the IP configuration on this port. + if [ip get $chassis $lm $port] { + errorMsg "port $chassis $lm $port has not been configured for IP yet" + set retCode 1 + } + set txIP [ip cget -sourceIpAddr] + # Collect the current DHCP configuration on this port. + if [dhcp get $chassis $lm $port] { + errorMsg "Error getting dhcp configuration for port $chassis $lm $port" + set retCode 1 + } + + + # Build DHCP packet. + set clientIpAddr [dhcpGetIP $chassis $lm $port] + set serverIpAddr [dhcp cget -serverIpAddr] + set txAddress $serverIpAddr + set destMacAddress [port cget -DestMacAddress] + + + + lappend optionList dhcpMessageType dhcpRelease \ + dhcpClientId $txSA \ + dhcpSvrIdentifier $serverIpAddr + } + default { + errorMsg "Unsupported packet type" + return 1 + } + } + + if {$txAddress != $nullIP} { + setupDhcpUnicastIP $chassis $lm $port $txIP $txAddress + } else { + setupDhcpBroadcastIP $chassis $lm $port + } + + setupUDPbootp $chassis $lm $port + setupDefaultDhcpParameters $chassis $lm $port $transactionID $txSA $clientIpAddr + + setDhcpOptions optionList + + if [dhcp set $chassis $lm $port] { + errorMsg "Error setting DHCP on port $chassis $lm $port" + set retCode 1 + } + + set speed [port cget -speed] + + stream config -da $destMacAddress + stream config -numDA 1 + + stream config -sa $txSA + stream config -numSA 1 + + stream config -frameSizeType sizeAuto + if [stream set $chassis $lm $port 1] { + errorMsg "Error setting stream 1, port $chassis,$lm,$port for $opcode frames" + set retCode 1 + } + + # since we set this for autoframe size calc, get the calculated framesize to use + # in calculating the IFG. + set framesize [stream cget -framesize] + set learnPercentRate [expr double([learn cget -rate])/[calculateMaxRate $chassis $lm $port $framesize]*100.] + stream config -percentPacketRate $learnPercentRate + + if [stream set $chassis $lm $port 1] { + errorMsg "Error setting stream 1, port $chassis,$lm,$port for $opcode frames" + set retCode 1 + } + + # set the filter parameters for receiving the DHCP response + filter config -captureTriggerDA any + filter config -captureFilterDA any + filter config -captureFilterEnable true + filter config -captureTriggerEnable true + if [filter set $chassis $lm $port] { + errorMsg "Error setting filters on $chassis,$lm,$port" + set retCode 1 + } + + protocol config -ethernetType $ethernetType + + return $retCode +} + + + +######################################################################################## +# Procedure: get_DHCP_packet +# +# Description: This command gets a DHCP packet sent from the DUT & fills the dhcp command +# memory using dhcp decode $packet +# +# Argument(s): +# chassis +# lm +# port +# messageType - name of DHCP message type to search. Note that this can be a list +# of message types, ie [list dhcpAck dhcpNak] +# +# Output: First message type (of messageType list) found in buffer: +# 0 if no match, else +# dhcpMessageType (as defined in Dhcp.hpp) +# +######################################################################################## +proc get_DHCP_packet {chassis lm port messageType {wait 0}} \ +{ + set retCode 0 + + # Wait till data in capture buffer, or timer expires. + set expire [expr [clock seconds] + $wait] + while {$expire > [clock seconds]} { + if {![stat get statCaptureFilter $chassis $lm $port]} { + if {[stat cget -counterVal] > 0} { + break + } + } + } + + # make sure there's something there to look at first! + if {[captureBuffer get $chassis $lm $port 1 1000]} { + errorMsg "Error getting capture data on $chassis,$lm,$port" + return 0 + } + + # You have to have the transactionID before we can process any further... + if [dhcp get $chassis $lm $port] { + errorMsg "Error getting DHCP on port $chassis $lm $port" + return 0 + } + set transactionID [dhcp cget -transactionID] + + set found 0 + set nPackets [captureBuffer cget -numFrames] + for {set nFrame 1} {$nFrame <= $nPackets} {incr nFrame} { + if [captureBuffer getframe $nFrame] { + errorMsg "Error getting frame $nFrame from capture buffer for $chassis,$lm,$port" + continue + } + set capframe [captureBuffer cget -frame] + if [dhcp decode $capframe $chassis $lm $port] { + continue + } + + # What is the message type? + if {![dhcp getOption dhcpMessageType]} { + set dhcpMessageType [dhcp cget -optionData] + } + + # The transaction ID must match. + if {[dhcp cget -transactionID] == $transactionID} { + + # The message type must match one in the list of message types. + foreach item $messageType { + global $item + upvar $item MessageType + if {$dhcpMessageType == $MessageType} { + set found 1 + set retCode $MessageType + break + } + } + } + # Break out of packet search loop. + if $found { + break + } + + } + + if {$found == 0} { + logMsg "No $messageType frames received on $chassis $lm $port" + } else { + set destinationAddr [lrange $capframe 6 11] + if [port get $chassis $lm $port] { + errorMsg "Error getting port $chassis,$lm,$port" + set retCode 0 + } + port config -DestMacAddress $destinationAddr + if [port set $chassis $lm $port] { + errorMsg "Error setting port on $chassis,$lm,$port" + set retCode 0 + } + } + + return $retCode +} + + + +######################################################################################## +# Procedure: dhcpInitialize +# +# Description: Setup dhcpClient initial settings. +# +# Input: eventList: list of potential events +# stateList: list of all known states +# actionList: list of list of actions per state per event (can be an +# empty list if state machine is to be disabled. +# +# Output: None +# +######################################################################################## +proc dhcpClient::Initialize { {stateList dhcpClient::stateList} \ + {eventList dhcpClient::eventList} \ + {actionList dhcpClient::actionList}} \ +{ + debug + variable dhcpLeaseRecord + + if [info exists dhcpLeaseRecord] { + unset dhcpLeaseRecord + } + + # Initialize state table. + InitStateTable $stateList $eventList $actionList + + # Set default stream region. + SetStreamRegion 0 + + # Create the default parameter request list used by dhcpDiscover & dhcpRequest. + clearParameterRequestList + setParameterRequestList dhcpSubnetMask + setParameterRequestList dhcpDomainName + setParameterRequestList dhcpGateways + setParameterRequestList dhcpNetBIOSNameSvr + setParameterRequestList dhcpNetBIOSNodeType + setParameterRequestList dhcpNetBIOSScope + setParameterRequestList dhcpDomainNameServer + + # Record dhcp start-time. + setStartTime [clock seconds] + +} + +######################################################################################## +# Procedure: dhcpStop +# +# Description: Cancel any outstanding timers, remove dhcpLeaseRecord, release all +# IP addresses that are currently leased. +# +# Input: None +# +# Output: None +# +######################################################################################## +proc dhcpClient::Stop {} \ +{ + debug + variable dhcpLeaseRecord + + foreach port [GetPortList] { + scan $port "%d %d %d" c l p + StopPort $c $l $p true + } + SetPortList {} + catch {unset dhcpLeaseRecord} + + StopTimer timer1 + StopTimer timer2 + StopTimer leaseExpire +} + +######################################################################################## +# Procedure: dhcpStopPort +# +# Description: Release IP address, unset dhcpLeaseRecord for a single port. +# +# Input: chassis +# card +# port +# release: true if IP address should be released, default is false +# +# Output: None +# +######################################################################################## +proc dhcpClient::StopPort {chassis card port {release false}} \ +{ + debug + variable dhcpLeaseRecord + + if {$release == "true"} { + set portList [list [list $chassis $card $port]] + send_DHCP_release $portList + } + + catch {unset dhcpLeaseRecord($chassis,$card,$port,ip)} + catch {unset dhcpLeaseRecord($chassis,$card,$port,server)} + catch {unset dhcpLeaseRecord($chassis,$card,$port,lease)} + catch {unset dhcpLeaseRecord($chassis,$card,$port,state)} + +} + +######################################################################################## +# Procedure: dhcpSetState +# +# Description: Store the current state of dhcp process. +# +# Input: chassis-card-port +# state: discover, offer, request, ack, bound, renew, rebind, idle +# +# Output: 0, success, else +# 1, failure (invalid state) +# +######################################################################################## +proc dhcpClient::SetState {chassis card port newState} \ +{ + + debug + + variable dhcpLeaseRecord + + # Valid state? + if ![ValidState $newState] { + return 1 + } + + set dhcpLeaseRecord($chassis,$card,$port,state) $newState + + return 0 +} + + +######################################################################################## +# Procedure: dhcpGetState +# +# Description: Get the current state for the given port. +# +# Input: chassis +# card: +# port: +# lease: duration of lease +# +# Output: state (possible states are: idle, discover, offer, request, ack, +# done, renew, rebind) +# +######################################################################################## +proc dhcpClient::GetState {chassis card port} \ +{ + variable dhcpLeaseRecord + + if [info exists dhcpLeaseRecord($chassis,$card,$port,state)] { + return $dhcpLeaseRecord($chassis,$card,$port,state) + } + +} + +######################################################################################## +# Procedure: dhcpSetLease +# +# Description: Set the DHCP lease. +# +# Input: chassis +# card: +# port: +# lease: duration of lease +# +# Output: None +# +######################################################################################## +proc dhcpClient::SetLease {chassis card port lease} \ +{ + debug + variable dhcpLeaseRecord + + # Store the lease + set dhcpLeaseRecord($chassis,$card,$port,lease) $lease +} + +######################################################################################## +# Procedure: dhcpGetLease +# +# Description: Return the value of the lease for a given port +# +# Input: chassis +# card: +# port: +# +# Output: lease +# +######################################################################################## +proc dhcpClient::GetLease {chassis card port} \ +{ + debug + variable dhcpLeaseRecord + + if [info exists dhcpLeaseRecord($chassis,$card,$port,lease)] { + return $dhcpLeaseRecord($chassis,$card,$port,lease) + } + +} + + +######################################################################################## +# Procedure: dhcpStartTimers +# +# Description: Start DHCP timers. +# +# Input: lease: duration of lease +# +# Output: 0 if successful, else +# 1 +# +######################################################################################## +proc dhcpClient::StartTimers {lease {timer1 0 } {timer2 0}} \ +{ + debug + variable dhcpLeaseRecord + + set returnCode 0 + + # If timers already running, don't restart. + if [info exist dhcpLeaseRecord(timer1)] { + if {$dhcpLeaseRecord(timer1) != ""} { + return 1 + } + } + + # Start timer 1 + if [StartTimer timer1 $lease $timer1] { + set returnCode 1 + } + + # Start timer 2 + if [StartTimer timer2 $lease $timer2] { + set returnCode 1 + } + + # Start lease timer + if [StartTimer leaseExpire $lease] { + set returnCode 1 + } + + + return $returnCode +} + + +######################################################################################## +# Procedure: dhcpStopTimers +# +# Description: Stop DHCP timers. +# +# Input: lease: duration of lease +# +# Output: 0 if successful, else +# 1 +# +######################################################################################## +proc dhcpClient::StopTimers {} \ +{ + debug + set returnCode 0 + + # Stop timer 1 + if [StopTimer timer1] { + set returnCode 1 + } + + # Stop timer 2 + if [StopTimer timer2] { + set returnCode 1 + } + + # Stop lease timer + if [StopTimer leaseExpire] { + set returnCode 1 + } + + return $returnCode +} + + +######################################################################################## +# Procedure: dhcpStartTimer +# +# Description: Initiate DHCP timers based upon the value supplied by the server. +# TIMER 1 will expire at (lease * .50) +# TIMER 2 will expire at (lease * .875) +# +# Input: timer: timer1, timer2 or leaseExpire +# +# Output: 0 if successful, else +# 1 +# +######################################################################################## +proc dhcpClient::StartTimer {timer lease {value 0}} \ +{ + debug + variable dhcpLeaseRecord + variable event + + # Initiate timers + switch $timer { + + timer1 { + if $value { + set timerValue $value + } else { + set timerValue [expr round([mpexpr $lease * .50])] + } + } + + timer2 { + if $value { + set timerValue $value + } else { + set timerValue [expr round([mpexpr $lease * .875])] + } + } + + leaseExpire { + set timerValue $lease + } + + default { + return 1 + } + + } + + # Start the timer, set the timer Id into the lease record. + set portList [lindex [GetPortList] 0] + scan $portList "%d %d %d" c l p + set dhcpLeaseRecord($timer) \ + [after [expr 1000 * $timerValue] \ + [namespace current]::StateLookup $c $l $p $event(timeOut)] + + + return 0 + +} + +######################################################################################## +# Procedure: dhcpStopTimer +# +# Description: Cancels a given DHCP timer for a given port. +# +# Input: timer: timer1, timer2 or leaseExpire +# +# +# Output: 0 if successful, else +# 1 +# +######################################################################################## +proc dhcpClient::StopTimer {timer} \ +{ + debug + variable dhcpLeaseRecord + + switch $timer { + + timer1 - + timer2 - + leaseExpire { + if {[dhcpGetTimer $timer] != ""} { + after cancel [dhcpGetTimer $timer] + set dhcpLeaseRecord($timer) "" + } + } + + default { + return 1 + } + } + + return 0 + +} + + +######################################################################################## +# Procedure: dhcpGetTimer +# +# Description: Return the value of the timer id for a given port +# +# Input: timer: timer1, timer2 or leaseExpire +# +# Output: timer Id +# +######################################################################################## +proc dhcpClient::GetTimer {timer} \ +{ + debug + variable dhcpLeaseRecord + set returnValue 0 + + switch $timer { + + timer1 - + timer2 - + leaseExpire { + if [info exists dhcpLeaseRecord($timer)] { + set returnValue $dhcpLeaseRecord($timer) + } + } + + default { + set returnValue 0 + } + } + + return $returnValue + +} + + +######################################################################################## +# Procedure: dhcpSetIP +# +# Description: Set the DHCP IP address as offered by the dhcp server for a given +# port. +# +# Input: chassis +# card: +# port: +# ip: IP address +# +# Output: None +# +######################################################################################## +proc dhcpClient::SetIP {chassis card port ip} \ +{ + debug + variable dhcpLeaseRecord + + # Store the IP address + set dhcpLeaseRecord($chassis,$card,$port,ip) $ip +} + +######################################################################################## +# Procedure: dhcpGetIP +# +# Description: Return the leased IP address for a given port +# +# Input: chassis +# card: +# port: +# +# Output: lease +# +######################################################################################## +proc dhcpClient::GetIP {chassis card port} \ +{ + debug + variable dhcpLeaseRecord + + if [info exists dhcpLeaseRecord($chassis,$card,$port,ip)] { + return $dhcpLeaseRecord($chassis,$card,$port,ip) + } + +} + + +######################################################################################## +# Procedure: dhcpSetServer +# +# Description: Set the DHCP Server address for a given port. +# +# Input: chassis +# card: +# port: +# server: server address (that which offered the lease) +# +# Output: None +# +######################################################################################## +proc dhcpClient::SetServer {chassis card port server} \ +{ + debug + variable dhcpLeaseRecord + + # Store the server address + set dhcpLeaseRecord($chassis,$card,$port,server) $server +} + +######################################################################################## +# Procedure: dhcpGetServer +# +# Description: Return the address of the dhcp server which leased this port an +# IP address. +# +# Input: chassis +# card: +# port: +# +# Output: server address +# +######################################################################################## +proc dhcpClient::GetServer {chassis card port} \ +{ + debug + variable dhcpLeaseRecord + + if [info exists dhcpLeaseRecord($chassis,$card,$port,server)] { + return $dhcpLeaseRecord($chassis,$card,$port,server) + } + +} + +######################################################################################## +# Procedure: dhcpSetStreamRegion +# +# Description: Set the region that DHCP streams are build within. +# +# Input: Region: 0 is default, else 1-7 +# +# Output: None +# +######################################################################################## +proc dhcpClient::SetStreamRegion {{region 0}} \ +{ + debug + + variable streamRegion + variable streamRegionMax + variable streamRegionMin + + if {$region <= $streamRegionMax && \ + $region >= $streamRegionMin} { + set streamRegion $region + } + +} + +######################################################################################## +# Procedure: dhcpGetStreamRegion +# +# Description: Returns the region that DHCP streams are build within. +# +# Input: None +# +# Output: region # +# +######################################################################################## +proc dhcpClient::GetStreamRegion {} \ +{ + debug + variable streamRegion + + if [info exists streamRegion] { + return $streamRegion + } + +} + +######################################################################################## +# Procedure: dhcpSetPortList +# +# Description: Store the list of ports working with DHCP addresses +# +# Input: ports: {{c l p} {c l p} ...} +# +# Output: None +# +######################################################################################## +proc dhcpClient::SetPortList {ports} \ +{ + debug + variable portList + set portList $ports +} + +######################################################################################## +# Procedure: dhcpGetPortList +# +# Description: Returns the list of ports using DHCP addresses. +# +# Input: None +# +# Output: region # +# +######################################################################################## +proc dhcpClient::GetPortList {} \ +{ + variable portList + + if [info exists portList] { + return $portList + } + +} + + +######################################################################################## +# Procedure: dhcpInitStateTable +# +# Description: Set the DHCP state table to it's initial value (all events in all +# state are set to the NULL routine). +# +# Input: eventList: list of potential events +# stateList: list of all known states +# actionList: list of list of actions per state per event (can be an +# empty list if state machine is to be disabled. +# +# Output: None +# +######################################################################################## +proc dhcpClient::InitStateTable { stateList eventList {actionList {}} } \ +{ + debug + variable stateTable + + upvar $stateList StateList + upvar $eventList EventList + if [catch "upvar $actionList ActionList"] { + set ActionList [list] + } + + # Empty state list? If so, exit. + if {[llength $StateList] == 0} { + return + } + + # Empty event list? If so, exit. + if {[llength $EventList] == 0} { + return + } + + if [info exists stateTable] { + unset stateTable + } + + # Empty Action List? If so, fill stateTable with the NULL routine. + if {[llength $ActionList] == 0} { + foreach state $StateList { + foreach event $EventList { + set stateTable($state,$event) [namespace current]::ActionNull + } + } + + # Otherwise, fill the stateTable w/given list of action routines. + } else { + set ActionList [join $ActionList] + set i 0 + + foreach state $StateList { + foreach event $EventList { + set stateTable($state,$event) [lindex $ActionList $i] + incr i + } + } + } +} + +######################################################################################## +# Procedure: dhcpClient::StateLookup +# +# Description: Perform state table lookup, execute action routine. +# +# Input: event: event that resulted in this procedure call +# port: port that event occurred on +# +# Output: None +# +######################################################################################## +proc dhcpClient::StateLookup {chassis card port event} \ +{ + debug + variable stateTable + + if ![ValidEvent $event] { + return + } + + set state [GetState $chassis $card $port] + if [ValidState $state] { + eval $stateTable($state,$event) [list [list $chassis $card $port]] + } +} + + + +######################################################################################## +# +# DHCP State Machine - Action Routines +# +# The following routines perform actions as a result of a background +# event. Based on the state and the event, and action is looked-up and +# executed. +# +######################################################################################## + +######################################################################################## +# Procedure: dhcpClient::ActionNull +# +# Description: Place holder for events which don't require handling. +# +# Input: None +# +# Output: None +# +######################################################################################## +proc dhcpClient::ActionNull {port} \ +{ + debug +} + +######################################################################################## +# Procedure: dhcpClient::ActionRenew +# +# Description: Perform 'renew' behavior as defined in the DHCP RFC for all +# ports in the port list. +# - Set state = renew +# - Request lease extension from the current dhcp server +# +# The occurance of this action indicates that in a bound state +# a timeOut occurred indicating that it is time to renew the lease. +# +# Input: None +# +# Output: None +# +######################################################################################## +proc dhcpClient::ActionRenew {port} \ +{ + debug + variable state + + set portList [GetPortList] + foreach item [lnumsort $portList] { + scan $item "%d %d %d" c l p + SetState $c $l $p $state(renew) + } + + dhcpStopTimer timer1 + DHCPdiscoverIP portList 2 enable + +} + +######################################################################################## +# Procedure: dhcpClient::ActionRebind +# +# Description: Perform 'rebind' behavior as defined in the DHCP RFC. +# - Set state = rebind +# - Request lease extension from the any dhcp server +# +# The occurance of this action routine indicates that in state(renew) +# a timeout occurred indicated that no IP addresses were offered for +# renewal from the current dhcp server. +# +# Input: None +# +# Output: None +# +######################################################################################## +proc dhcpClient::ActionRebind {port} \ +{ + debug $port + variable state + + set portList [GetPortList] + foreach item [lnumsort $portList] { + scan $item "%d %d %d" c l p + SetState $c $l $p $state(rebind) + } + + dhcpStopTimer timer2 + DHCPdiscoverIP portList 2 enable + +} + +######################################################################################## +# Procedure: dhcpClient::ActionInit +# +# Description: Perform 'discovery' behavior as defined in the DHCP RFC. +# - Set state = idle +# - Send discover message +# +# The occurance of this action routine indicates that in state(rebind) +# a timeout occurred indicating that the IP lease expired. +# +# Input: None +# +# Output: None +# +######################################################################################## +proc dhcpClient::ActionInit {port} \ +{ + + debug $port + + # Release all ports, turn off timers. + set portList [GetPortList] + dhcpStop + + # Restart IP acquisition with Discovery. + DHCPdiscoverIP portList 0 enable +} + + +######################################################################################## +# Procedure: dhcpClient::GetStateNames +# +# Description: Returns a list of textual state names. +# +# Input: None +# +# Output: List of state names +# +######################################################################################## +proc dhcpClient::GetStateNames {} \ +{ + variable state + return [array names state] +} + +######################################################################################## +# Procedure: dhcpClient::GetStateName +# +# Description: Given a state code, return a textual state name. +# +# Input: stateCode: state code (refer to dhcpClient::state) +# +# Output: state name +# +######################################################################################## +proc dhcpClient::GetStateName {stateCode} \ +{ + variable state + set retValue "" + + foreach {name value} [array get state] { + set stateNames($value) $name + } + + if [info exists stateNames($stateCode)] { + set retValue $stateNames($stateCode) + } + + return $retValue +} + +######################################################################################## +# Procedure: dhcpClient::GetStateCodes +# +# Description: Returns a list of state codes. +# +# Input: None +# +# Output: List of state codes +# +######################################################################################## +proc dhcpClient::GetStateCodes {} \ +{ + variable state + + set states [list] + foreach {name value} [array get state] { + lappend states $value + } + + return $states +} + +######################################################################################## +# Procedure: dhcpClient::ValidState +# +# Description: True/False: Is the given state code a valid member of the list +# of state codes (dhcpClient::state) +# +# Input: stateCode: state code +# +# Output: 1 if valid, else +# 0 +# +######################################################################################## +proc dhcpClient::ValidState {stateCode} \ +{ + if {[lsearch [GetStateCodes] $stateCode] < 0} { + return 0 + } + return 1 +} + + +######################################################################################## +# Procedure: dhcpClient::GetEventName +# +# Description: Given a event code, return a textual event name. +# +# Input: eventCode: event code (refer to dhcpClient::event) +# +# Output: event name +# +######################################################################################## +proc dhcpClient::GetEventName {eventCode} \ +{ + variable event + set retValue "" + + foreach {name value} [array get event] { + set eventNames($value) $name + } + + if [info exists eventNames($eventCode)] { + set retValue $eventNames($eventCode) + } + + return $retValue +} + +######################################################################################## +# Procedure: dhcpClient::GetEventCodes +# +# Description: Returns a list of event codes (dhcpClient::event). +# +# Input: None +# +# Output: List of event codes +# +######################################################################################## +proc dhcpClient::GetEventCodes {} \ +{ + variable event + + set events [list] + foreach {name value} [array get event] { + lappend events $value + } + + return $events +} + +######################################################################################## +# Procedure: dhcpClient::ValidEvent +# +# Description: True/False: Is the given event code a valid member of the list +# of event codes (dhcpClient::event) +# +# Input: eventCode: event code +# +# Output: 1 if valid, else +# 0 +# +######################################################################################## +proc dhcpClient::ValidEvent {eventCode} \ +{ + if {[lsearch [GetEventCodes] $eventCode] < 0} { + return 0 + } + return 1 +} + +######################################################################################## +# Procedure: dhcpClient::getTransactionID +# +# Description: Derive a unique transaction id, given a port id by adding the +# the port # to the current system time in 'clicks'. +# +# Input: port: port id +# +# Output: transaction id +# +######################################################################################## +proc dhcpClient::getTransactionID {port} \ +{ + return [expr [clock clicks] + $port] +} + +######################################################################################## +# Procedure: dhcpClient::clearParameterRequestList +# +# Description: Empty parameter request list. +# +# Input: None +# +# Output: None +# +######################################################################################## +proc dhcpClient::clearParameterRequestList {} \ +{ + variable paramRequestList + set paramRequestList [list] +} + +######################################################################################## +# Procedure: dhcpClient::getParameterRequestList +# +# Description: Return the values currently set into the list of requested DHCP +# options. DHCP options are described in RFC 1533. +# +# Input: None +# +# Output: dhcpClient::paramRequestList +# +######################################################################################## +proc dhcpClient::getParameterRequestList {} \ +{ + variable paramRequestList + return $paramRequestList +} + +######################################################################################## +# Procedure: dhcpClient::setParameterRequestList +# +# Description: Set the list of requested DHCP options. DHCP options are described +# in RFC 1533, the parameter request list is described in section +# 9.6 of RFC 1533. +# +# Input: option: name of dhcp option (refer to Tcl Development Guide - DHCP, +# for a list of valid options or RFC 1533). +# +# Output: 0 if okay, else +# 1 +# +######################################################################################## +proc dhcpClient::setParameterRequestList {option} \ +{ + variable paramRequestList + set retCode 1 + + switch $option { + dhcpPad - + dhcpEnd - + dhcpSubnetMask - + dhcpTimeOffset - + dhcpGateways - + dhcpTimeServer - + dhcpNameServer - + dhcpDomainNameServer - + dhcpLogServer - + dhcpCookieServer - + dhcpLPRServer - + dhcpImpressServer - + dhcpResourceLocationServer - + dhcpHostName - + dhcpBootFileSize - + dhcpMeritDumpFile - + dhcpDomainName - + dhcpSwapServer - + dhcpRootPath - + dhcpExtensionPath - + dhcpIpForwardingEnable - + dhcpPolicyFilter - + dhcpMaxDatagramReassemblySize - + dhcpDefaultIpTTL - + dhcpPathMTUAgingTimeout - + dhcpPathMTUPlateauTable - + dhcpInterfaceMTU - + dhcpAllSubnetsAreLocal - + dhcpBroadcastAddress - + dhcpPerformMaskDiscovery - + dhcpMaskSupplier - + dhcpPerformRouterDiscovery - + dhcpRouterSolicitAddr - + dhcpStaticRoute - + dhcpTrailerEncapsulation - + dhcpARPCacheTimeout - + dhcpEthernetEncapsulation - + dhcpTCPDefaultTTL - + dhcpTCPKeepAliveInterval - + dhcpTCPKeepGarbage - + dhcpNISDomain - + dhcpNISServer - + dhcpMTPServer - + dhcpVendorSpecificInfo - + dhcpNetBIOSNameSvr - + dhcpNetBIOSDatagramDistSvr - + dhcpNetBiosNodeType - + dhcpNetBIOSScope - + dhcpXWINSysFontSvr - + dhcpRequestedIPAddr - + dhcpIPAddrLeaseTime - + dhcpOptionOverload - + dhcpTFTPSvrName - + dhcpBOOTFileName - + dhcpMessageType - + dhcpSvrIdentifier - + dhcpParamRequestList - + dhcpMessage - + dhcpMaxMessageSize - + dhcpRenerwalTimeValue - + dhcpRebindingTimeValue - + dhcpVendorClassId - + dhcpClientId - + dhcpXWinSysDisplayMgr - + dhcpNISplusDomain - + dhcpNISplusServer - + dhcpMobileIPHomeAgent - + dhcpSMTPSvr - + dhcpPOP3Svr - + dhcpNNTPSvr - + dhcpWWWSvr - + dhcpDefaultFingerSvr - + dhcpDefaulttIRCSvr - + dhcpStreetTalkSvr - + dhcpSTDASvr { + if {[lsearch $paramRequestList $option] < 0} { + set paramRequestList [lappend paramRequestList $option] + } + set retCode 0 + } + } + + return $retCode +} + +######################################################################################## +# Procedure: dhcpClient::getMagicCookie +# +# Description: Return the dhcp magic cookie (defined in dhcpClient namespace) +# +# Input: None +# +# Output: magic cookie (hex value) +# +######################################################################################## +proc dhcpClient::getMagicCookie {} \ +{ + variable magicCookie + return $magicCookie +} + +######################################################################################## +# Procedure: dhcpClient::setStartTime +# +# Description: Record the start of dhcp processing. +# +# Input: seconds: start time in seconds +# +# Output: None +# +######################################################################################## +proc dhcpClient::setStartTime {seconds} \ +{ + variable startTime + set startTime $seconds +} + +######################################################################################## +# Procedure: dhcpClient::getStartTime +# +# Description: Return the start time of dhcp processing in seconds. +# +# Input: None +# +# Output: start time. +# +######################################################################################## +proc dhcpClient::getStartTime {} \ +{ + variable startTime + return $startTime +} + + + + +######################################################################################## +# Procedure: dhcpClient::debug +# +# Description: Depending upon the list of items contained in variable +# dhcpClient::debugLevel, debug information is built and +# printed. +# +# Input: args: list of arguments, these are added to debug output +# +# Output: Printable debug information +# +######################################################################################## +proc dhcpClient::debug {args} \ +{ + variable debugLevel + if {[string length $debugLevel] == 0} { + return + } + + set output "" + foreach item $debugLevel { + + switch $item { + + state { + set stateName "" + set port [lindex [GetPortList] 0] + if {[string length $port] > 0} { + scan $port "%d %d %d" c l p + set stateName [GetStateName [GetState $c $l $p]] + } + append output "State: $stateName " + } + + default { + set procedure [info level [expr [info level]-1]] + set arguments [info args [lindex $procedure 0]] + set defaultInfo [string toupper [lindex $procedure 0]] + set procedure [lreplace $procedure 0 0] + + for {set i 0} {$i < [llength $procedure]} {incr i} { + set defaultInfo [lappend defaultInfo [lindex $arguments $i] \ + [lindex $procedure $i]] + } + append output "$defaultInfo " + } + } + } + append output "$args" + + errorMsg "$output" +} + + + diff --git a/dep/tclclient/ixTcl1.0/Generic/dialogUtils.tcl b/dep/tclclient/ixTcl1.0/Generic/dialogUtils.tcl new file mode 100644 index 00000000..6458219b --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/dialogUtils.tcl @@ -0,0 +1,230 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: dialogUtils.tcl +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 03-12-2001 DS Genesis +# +# Description: Contains miscellaneous dialog for transmit/pause dialog procedures. +# +################################################################################## + +proc createDialog {dialogName {window textDialog}} \ +{ + dialogUtils::create $dialogName $window +} + +proc writeDialog {dialogText {window textDialog}} \ +{ + dialogUtils::writeText $dialogText $window +} + +proc destroyDialog {{window textDialog}} \ +{ + dialogUtils::destroy $window +} + + +####################################################################### +# +# Procedure: dialogUtils +# Description: +# Argument(s): +# Results: +# +####################################################################### +namespace eval dialogUtils {} \ +{ + variable state [advancedTestParameter cget -dialogState] +} + +######################################################################## +# Procedure: dialogUtils::init +# +# Description: This command doesn't do anything, just want to source this +# file when it is called. +# +######################################################################## +proc dialogUtils::init {} \ +{ +} + +######################################################################## +# Procedure: dialogUtils::create +# +# This command creates a dialog box of name "dialogName" +# +######################################################################## +proc dialogUtils::create {dialogName window} \ +{ + variable state + + set retCode $::TCL_OK + + set window .$window + + if {$state != "destroyedByUser"} { + if [regexp -nocase wish [file tail [info nameofexecutable]]] { + + if {[winfo exists $window] == 0} { + toplevel $window -class Dialog + wm resizable $window 0 1 + wm withdraw $window + wm title $window $dialogName + + text $window.text -width 40 -wrap word -yscrollcommand "$window.textScroll set" + + # unix didn't look nice, so just set a different font here.. + if {[isUNIX]} { + createNamedFont application arial 7 + $window.text config -font application + } + + scrollbar $window.textScroll -command "$window.text yview" + + grid $window.text -row 0 -column 0 -sticky snew + grid $window.textScroll -row 0 -column 1 -sticky sn + grid rowconfigure $window 0 -weight 1 + grid columnconfigure $window 0 -weight 1 + + wm geometry $window 270x100+100+100 + wm protocol $window WM_DELETE_WINDOW "dialogUtils::destroyedByUser $window" + + if {$state == "normal"} { + wm deiconify $window + } else { + wm iconify $window + } + } else { + $window.text delete 0.0 end + } + } + } + return $retCode +} + + +######################################################################## +# Procedure: dialogUtils::writeText +# +# This command writes to a dialog box of name "window" +# +######################################################################## +proc dialogUtils::writeText {dialogText window} \ +{ + set window .$window + + if [catch { + $window.text insert end "$dialogText\n" + $window.text see end + update + } err] { + ixPuts $dialogText + } +} + +######################################################################## +# Procedure: dialogUtils::getWindowState +# +# This command gets the state the dialog box of name "window" +# +######################################################################## +proc dialogUtils::getWindowState {window} \ +{ + set windowState .$window + + if [catch {wm state $windowState} state] { + set state destroyedByUser + } + + return $state +} + +######################################################################## +# Procedure: dialogUtils::setWindowState +# +# This command sets the state variable for the dialog box +# +######################################################################## +proc dialogUtils::setWindowState {newState} \ +{ + variable state + + set state $newState + + return $state +} + + +######################################################################## +# Procedure: dialogUtils::saveWindowState +# +# This command save the static state to bring the dialog back up in next time +# +######################################################################## +proc dialogUtils::saveWindowState {window} \ +{ + setWindowState [getWindowState $window] +} + + +######################################################################## +# Procedure: dialogUtils::destroyedByUser +# +# This command destroys the dialog box of name "window" +# +######################################################################## +proc dialogUtils::destroyedByUser {window} \ +{ + catch {::destroy $window} + setWindowState destroyedByUser +} + + +######################################################################## +# Procedure: dialogUtils::destroy +# +# This command destroys the dialog box of name "dialogName" +# +######################################################################## +proc dialogUtils::destroy {window} \ +{ + saveWindowState $window + + set window .$window + catch {::destroy $window} +} + + diff --git a/dep/tclclient/ixTcl1.0/Generic/eventHandler.tcl b/dep/tclclient/ixTcl1.0/Generic/eventHandler.tcl new file mode 100644 index 00000000..9a7dc1a1 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/eventHandler.tcl @@ -0,0 +1,124 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: eventHandler.tcl +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 04-25-2000 DS Genesis +# +# Description: The procedures in this file are used for handling events +# +# NOTE: THIS FILE NEEDS TO BE MOVED INTO SM LAND AS IT'S NOT USED +# BY IXTCLHAL GENERIC PROCS!!! +# +################################################################################## + + +################################################################################# +# Procedure: setStopTestFlag +# +# Description: Set the global variable ixStopTest to the given value. +# +# Argument(s): The new value for ixStopTest. +# +# Output: +################################################################################# +proc setStopTestFlag { value } \ +{ + global ixStopTest + + set ixStopTest $value +} + + +################################################################################# +# Procedure: stopTest +# +# Description: Handle the message stopTest that is sent from Scriptmate. +# +# Argument(s): +# +# Output: +################################################################################# + +proc stopTest {} \ +{ + global ixStopTest ixStopAction + + set ixStopTest 1 + switch $ixStopAction { + 0 { + # close pipe & socket + catch { puts "closePipe" } + catch { putsServer "closeSocket" } + destroyDialog + exit + } + default { + } + } + + debugMsg "The test is being stopped!" +} + + +################################################################################# +# Procedure: isTestStopped +# +# Description: Find out if the test is being stopped by looking at the flag ixStopTest. +# +# Argument(s): +# +# Output: +################################################################################# +proc isTestStopped {} { + global ixStopTest + return $ixStopTest +} + + +################################################################################# +# Procedure: informServerCurrentTestStopped +# +# Description: Send the message "runningStatus:currentTestStoppedByUser" to Scriptmate. +# +# Argument(s): +# +# Output: +################################################################################# +proc informServerCurrentTestStopped {} \ +{ + logMsg "runningStatus:currentTestStoppedByUser" +} + diff --git a/dep/tclclient/ixTcl1.0/Generic/featureUtils.tcl b/dep/tclclient/ixTcl1.0/Generic/featureUtils.tcl new file mode 100644 index 00000000..496fc32e --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/featureUtils.tcl @@ -0,0 +1,324 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: featureUtils.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 07-31-2002 DS Genesis +# +# Description: This file contains helper utilities that check for +# specific features on a port or portArray. +# +######################################################################## + + + +######################################################################## +# Procedure: IsPOSPort +# +# This command checks the port if it is a POS port +# +######################################################################## +proc IsPOSPort {c l p} \ +{ + return [port isActiveFeature $c $l $p $::portFeaturePos] +} + + +######################################################################## +# Procedure: Is10GigEPort +# +# This command checks the port if it is a 10GigE port +# +######################################################################## +proc Is10GigEPort {c l p} \ +{ + set retCode 0 + + set interfaceType [port getInterface $c $l $p] + + if {$interfaceType == $::interface10GigE } { + set retCode 1 + } + + + return $retCode +} + + +######################################################################## +# Procedure: IsGigabitPort +# +# This command checks card type against Gigabit card types +# +######################################################################## +proc IsGigabitPort {c l p} \ +{ + set retCode 0 + + set interfaceType [port getInterface $c $l $p] + if {$interfaceType == $::interfaceGigabit} { + set retCode 1 + } + + return $retCode +} + + +######################################################################## +# Procedure: any10100Ports +# +# This command check if there are any 10/100 cards in the map. +# Returns 1 if there are 10/100 cards in the map. +# +# Argument(s): +# +######################################################################## +proc any10100Ports {TxRxArray} \ +{ + upvar $TxRxArray txRxArray + + return [anyPortsBySpeed txRxArray {10 100}] +} + + +######################################################################## +# Procedure: anyGigPorts +# +# This command checks if there are any gig cards in the map +# +# Argument(s): +# +######################################################################## +proc anyGigPorts {TxRxArray} \ +{ + upvar $TxRxArray txRxArray + + return [anyPortsBySpeed txRxArray 1000] +} + + +######################################################################## +# Procedure: anyOc48Ports +# +# This command checks if there are any oc48 cards in the map +# +# Argument(s): +# +######################################################################## +proc anyOc48Ports {TxRxArray} \ +{ + upvar $TxRxArray txRxArray + + return [anyPortsByInterface txRxArray interfaceOc48] +} + + +######################################################################## +# Procedure: anyOc192Ports +# +# This command checks if there are any oc192 cards in the map +# +# Argument(s): +# +######################################################################## +proc anyOc192Ports {TxRxArray} \ +{ + upvar $TxRxArray txRxArray + + return [anyPortsByInterface txRxArray interfaceOc192] +} + + +######################################################################## +# Procedure: anyPortsByInterface +# +# This command checks if there are any ports of that interface type +# +# Argument(s): +# +######################################################################## +proc anyPortsByInterface {TxRxArray interface} \ +{ + upvar $TxRxArray txRxArray + + global $interface + + set retCode 0 + + foreach portMap [getAllPorts txRxArray] { + scan $portMap "%d %d %d" c l p + if {[port getInterface $c $l $p] == [set $interface]} { + set retCode 1 + break + } + } + + return $retCode +} + + +######################################################################## +# Procedure: anyPortsBySpeed +# +# This command checks if there are any ports of that SPEED(list) +# +# Argument(s): +# +######################################################################## +proc anyPortsBySpeed {TxRxArray speed} \ +{ + upvar $TxRxArray txRxArray + + set retCode 0 + + foreach portMap [getAllPorts txRxArray] { + scan $portMap "%d %d %d" c l p + + if {[lsearch $speed [stat getLineSpeed $c $l $p]] >= 0} { + set retCode 1 + break + } + } + + return $retCode +} + + +######################################################################## +# Procedure: supportsProtocolServer +# +# This command checks if all the ports support protocol server +# +# Argument(s): +# TxRxArray - port array or list +# +# Return: +# TRUE if all ports support protocol server +# +######################################################################## +proc supportsProtocolServer {TxRxArray} \ +{ + upvar $TxRxArray txRxArray + + return [isValidFeature txRxArray $::portFeatureProtocols] +} + + +######################################################################## +# Procedure: supportsPortCPU +# +# This command checks if all the ports are portCPU-based +# +# Argument(s): +# TxRxArray - port array or list +# +# Return: +# TRUE if all ports are portCPU-based +# +######################################################################## +proc supportsPortCPU {TxRxArray} \ +{ + upvar $TxRxArray txRxArray + + return [isValidFeature txRxArray $::portFeatureLocalCPU] +} + + +######################################################################## +# Procedure: isValidFeature +# +# Description: TRUE/FALSE: Is a feature valid for the ports in the given +# port array? +# +# Arguments: PortArray array, map or list of ports to check feature on +# featureList one or more features to validate port against +# +# Returns: $::true is valid, else $::false +# +######################################################################## +proc isValidFeature {PortArray featureList} \ +{ + upvar $PortArray portArray + + set retCode $::true + + foreach portMap [getAllPorts portArray] { + scan $portMap "%d %d %d" c l p + + foreach feature $featureList { + if {![port isValidFeature $c $l $p $feature]} { + set retCode $::false + break + } + } + } + + return $retCode +} + + +######################################################################## +# Procedure: isPacketFlowMode +# +# This command checks if the port is in packet flow mode +# +# Argument(s): +# +######################################################################## +proc isPacketFlowMode {c l p} \ +{ + return [port isActiveFeature $c $l $p $::portFeaturePacketFlows] +} + + +######################################################################## +# Procedure: isAdvancedStreamSchedulerMode +# +# This command checks if the port is in advanced streams mode +# +# Argument(s): +# +######################################################################## +proc isAdvancedStreamSchedulerMode {c l p} \ +{ + if {[port isValidFeature $c $l $p $::portTxModeAdvancedScheduler] || \ + [port isValidFeature $c $l $p $::portTxModeAdvancedSchedulerCoarse]} { + return 1; + } else { + return 0; + } +} + diff --git a/dep/tclclient/ixTcl1.0/Generic/fileTransferClient.tcl b/dep/tclclient/ixTcl1.0/Generic/fileTransferClient.tcl new file mode 100644 index 00000000..f98b72a8 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/fileTransferClient.tcl @@ -0,0 +1,538 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: fileTransferClient.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 5-14-2001 Andy Balogh created +# +# Description: +# commands to transfer files using ixTclServer as the server +# +# NOTE: We use this mechanism because the ixTclServer is not setup +# to be a 'real' fully-functioning FTP server; it really only +# does some simple file transfer stuff. +# +######################################################################## + + +######################################################################## +# Protocol Description +# +# bytes reqd description +# ----- ---- ---------------------------------------- +# 20 Y length of transaction +# 1 Y command 0=success, 1=error, 2=getFile, 3=putFile, 4=listDirectory, 5=deleteFile +# 20 Y length of command details +# above Y command details, error=errorCode +# getFile=fileName +# putFile=fileName +# listDirectory=directory +# deleteFile=fileName +# 20 Y length of data +# above N data for the command being executed, +# error=text description of the error, +# getFile=the file data, +# putFile=the file Data, +# listDirectory=the directory listing, +# deleteFile=null +# +######################################################################## +namespace eval fileTransferClient {} \ +{ + variable dataSocket + variable sourceFile + variable destinationFile + + variable transactionSize + variable commandId + variable commandDetailsSize + variable commandDetails + variable dataSize + + variable totalBytesRead + variable buffer + variable destinationFileId + variable transferOutcome + variable transferOutcomeDetails + + ### name of the log file in case we read more bytes then expected, used below in the method debug + variable debug_file_name +} + +proc ::fileTransferClient::debug { mesg } { + variable debug_file_name + + # Print only explicit messages with debug "message" + if {[info exists debug_file_name]} { + set fid [open $debug_file_name "a+"] + puts $fid "$mesg" + close $fid + } else { + puts "$mesg" + } +} + +set fileTransferClient::debug_file_name "/tmp/ixialog.log" + + +######################################################################## +# Procedure: fileTransferClient::openConnectionToTclServer +# +# Description: This command connects to an ixTclServer +# +# Argument(s): +# hostname - hostname of the ixTclServer +# port - port number, should be 4500 +# +# Return Values: returns 0-success, 1-error +######################################################################## +proc fileTransferClient::openConnectionToTclServer { hostname {port 4500} } \ +{ + variable dataSocket + variable commandId + + set returnValue 1 + + catch { set dataSocket [socket $hostname $port] } + + if { $dataSocket != "" } { + + fconfigure $dataSocket -blocking 0 + fconfigure $dataSocket -buffering none + fconfigure $dataSocket -buffersize 8192 + + if { $commandId == 2 } { + + fconfigure $dataSocket -translation {binary binary} + fconfigure $dataSocket -encoding binary + + } else { + + fconfigure $dataSocket -encoding binary + fconfigure $dataSocket -translation {binary binary} + + } + + set returnValue 0 + + } + + return $returnValue +} + + +######################################################################## +# Procedure: fileTransferClient::closeTclServerConnection +# +# Description: This command closes the connection to an ixTclServer +# and optionally closes the destinationFileId if the +# command is a getFile +# +# Argument(s): +# +# Return Values: always returns 0 +######################################################################## +proc fileTransferClient::closeTclServerConnection {} \ +{ + variable dataSocket + variable destinationFileId + + close $dataSocket + + if { $destinationFileId != "" } { + + close $destinationFileId + + } + + return 0 +} + + +######################################################################## +# Procedure: fileTransferClient::sendTransactionToTclServer +# +# Description: This command sends a formatted transaction to an ixTclServer +# +# Argument(s): +# +# Return Values: returns 0-success, 1-error +######################################################################## +proc fileTransferClient::sendTransactionToTclServer { transaction } \ +{ + variable dataSocket + variable totalBytesRead 0 + variable buffer "" + variable transferOutcomeDetails + variable transferOutcome + variable destinationFile + variable transactionSize 0 + variable commandSize 0 + variable commandDetails "" + variable commandDetailsSize 0 + variable dataSize -1 + + set returnValue 1 + + if { $dataSocket != "" } { + + fconfigure $dataSocket -blocking 1 + set line [puts $dataSocket $transaction] + flush $dataSocket + + set totalBytesRead [expr $totalBytesRead + [string length $line] ] + set buffer $line + + set returnValue [fileTransferClient::getTransferOutcome] + } + + return $returnValue +} + + +######################################################################## +# Procedure: fileTransferClient::sendTransactionToTclServer_ForDelete +# +# Description: This command sends a formatted transaction to an ixTclServer +# +# Argument(s): +# +# Return Values: returns 0-success, 1-error +######################################################################## +proc fileTransferClient::sendTransactionToTclServer_ForDelete { transaction } \ +{ + variable dataSocket + variable totalBytesRead 0 + variable buffer "" + variable transferOutcomeDetails + variable transferOutcome + variable destinationFile + variable transactionSize 0 + variable commandSize 0 + variable commandDetails "" + variable commandDetailsSize 0 + variable dataSize -1 + + set returnValue 0 + + if { $dataSocket != "" } { + + fconfigure $dataSocket -blocking 1 + set line [puts $dataSocket $transaction] + flush $dataSocket + + set totalBytesRead [expr $totalBytesRead + [string length $line] ] + set buffer $line + closeTclServerConnection + } + + return $returnValue +} + +######################################################################## +# Procedure: fileTransferClient::getFile +# +# Description: This command gets a file using ixTclServer as the server +# +# Argument(s): +# hostname - hostname of the ixTclServer +# port - port number, should be 4500 +# sourceFile - name of the file to get, can be absolute or relative +# destinationFile - name of the file that the contents will be saved as +# +# Return Values: returns 0-success, 1-error +######################################################################## +proc fileTransferClient::getFile { hostname port sourceFile destinationFile } \ +{ + variable totalBytesRead 0 + variable transactionSize 0 + variable commandSize 0 + variable commandDetails 0 + variable destinationFileId "" + variable transferOutcomeDetails 0 + variable commandId 2 + + set returnValue 1 + + if { [openConnectionToTclServer $hostname $port] == 0 } { + + set commandDetails $sourceFile + set commandDetailsSize [format "%.20d" [string length $commandDetails]] + set transactionSize [format "%.20d" [expr 20 + [string length $commandId] + [string length $commandDetailsSize] + [string length $commandDetails]]] + set transaction $transactionSize$commandId$commandDetailsSize$commandDetails + + set destinationFileId [ixFileUtils::open $destinationFile w] + if { $destinationFileId != "" } { + + fconfigure $destinationFileId -encoding binary + fconfigure $destinationFileId -eofchar { "" "" } + fconfigure $destinationFileId -translation { lf lf } + + set returnValue [sendTransactionToTclServer $transaction] + + } else { + + closeTclServerConnection + + set transferOutcomeDetails "Unable to open source file" + } + + } else { + + set transferOutcomeDetails "Unable to open socket" + } + + return $returnValue +} + +######################################################################## +# Procedure: fileTransferClient::deleteFile +# +# Description: This command deletes a file using ixTclServer as the server +# +# Argument(s): +# hostname - hostname of the ixTclServer +# port - port number, should be 4500 +# sourceFile - name of the file to delete, can be absolute or relative +# +# Return Values: returns 0-success, 1-error +######################################################################## +proc fileTransferClient::deleteFile { hostname port sourceFile } \ +{ + variable totalBytesRead 0 + variable transactionSize 0 + variable commandSize 0 + variable commandDetails "" + variable transferOutcomeDetails 0 + variable destinationFileId "" + variable commandId 5 + + set returnValue 1 + + if { [openConnectionToTclServer $hostname $port] == 0 } { + + set commandDetails $sourceFile + set commandDetailsSize [format "%.20d" [string length $commandDetails]] + set transactionSize [format "%.20d" [expr 20 + [string length $commandId] + [string length $commandDetailsSize] + [string length $commandDetails]]] + set transaction $transactionSize$commandId$commandDetailsSize$commandDetails + + set returnValue [sendTransactionToTclServer_ForDelete $transaction] + + } else { + + set transferOutcomeDetails "Unable to open socket" + } + + + return $returnValue +} + + +######################################################################## +# Procedure: fileTransferClient::putFile +# +# Description: This commands puts a file using ixTclServer as the server +# +# Argument(s): +# hostname - hostname of the ixTclServer +# port - port number, should be 4500 +# sourceFile - name of the file to get, can be absolute or relative +# destinationFile - name of the file that the contents will be saved as +# +# Return Values: always returns 0 +######################################################################## +proc fileTransferClient::putFile { hostname port sourceFile destinationFile } \ +{ + variable totalBytesRead 0 + variable transactionSize 0 + variable commandSize 0 + variable commandDetails "" + variable dataSize -1 + variable buffer + variable destinationFileId "" + variable transferOutcomeDetails 0 + variable commandId 3 + + set returnValue 1 + + if { [openConnectionToTclServer $hostname $port] == 0 } { + + set commandDetails $destinationFile + set commandDetailsSize [format "%.20d" [string length $commandDetails]] + set dataSize [format "%.20d" [file size $sourceFile]] + + set sourceFileId [ixFileUtils::open $sourceFile r] + + if { $sourceFileId != "" } { + + fconfigure $sourceFileId -translation { binary binary } + fconfigure $sourceFileId -encoding binary + set fileData [read -nonewline $sourceFileId] + close $sourceFileId + + set transactionSize [format "%.20d" [expr 20 + [string length $commandId] + [string length $commandDetailsSize] + [string length $commandDetails] + [string length $dataSize] + [string length $fileData]]] + set transaction $transactionSize$commandId$commandDetailsSize$commandDetails$dataSize$fileData + + set returnValue [sendTransactionToTclServer $transaction] + + } else { + + closeTclServerConnection + + set transferOutcomeDetails "Unable to open source file" + } + + } else { + + set transferOutcomeDetails "Unable to open socket" + } + + return $returnValue +} + + +######################################################################## +# Procedure: fileTransferClient::getTransferOutcome +# +# Description: This command reads the response from the ixTclServer +# +# Argument(s): +# dataSocket - a connected socket +# +# Return Values: always returns 0 +######################################################################## +proc fileTransferClient::getTransferOutcome {} \ +{ + variable totalBytesRead 0 + variable transactionSize 0 + variable commandId -1 + variable commandDetailsSize -1 + variable commandDetails "" + variable dataSize -1 + variable buffer "" + variable destinationFileId + variable transferOutcome + variable transferOutcomeDetails + variable dataSocket + variable hitTime "" + + fconfigure $dataSocket -blocking 0 + + set returnValue 1 + set endSocketRead 0 + + while { $endSocketRead == 0 } { + + set line [read $dataSocket 2048] + set totalBytesRead [expr $totalBytesRead + [string length $line]] + append buffer $line + + if { ( $transactionSize == 0 ) && ( [string length $buffer] >= 40 ) } { + + set transactionSize [string range $buffer 0 19] + set transactionSize [string trimleft $transactionSize "0"] + set transactionSize [expr int( $transactionSize )] + + set commandId [string range $buffer 20 20] + + set commandDetailsSize [string range $buffer 21 40] + set commandDetailsSize [string trimleft $commandDetailsSize "0"] + set commandDetailsSize [expr int( $commandDetailsSize )] + + set buffer [string range $buffer 41 [string length $buffer]] + + } + + if { $commandDetailsSize > 0 && [string length $commandDetails] == 0 && $totalBytesRead >= [expr 41 + $commandDetailsSize] } { + set commandDetails [string range $buffer 0 [expr $commandDetailsSize - 1]] + + set buffer [string range $buffer $commandDetailsSize [string length $buffer]] + } + + if { $commandId == 0 && $dataSize == -1 && $commandDetailsSize > 0 } { + set dataSize [string range $buffer 0 19] + + if { [string length $dataSize] > 0 } { + + set dataSize [string trimleft $dataSize "0"] + set dataSize [expr int( $dataSize )] + set buffer [string range $buffer 20 [string length $buffer]] + + } else { + set dataSize 0 + } + } + + if { $transactionSize > 0 && $totalBytesRead >= $transactionSize } { + set endSocketRead 1 + if { $totalBytesRead > $transactionSize } { + file delete -- $fileTransferClient::debug_file_name + set hitTime [clock format [clock seconds]] + debug "In PROC getTransferOutcome : hit endSocketRead" + debug "In PROC getTransferOutcome : the time is $hitTime" + debug "In PROC getTransferOutcome : totalBytesRead = $totalBytesRead, transactionSize = $transactionSize" + debug "In PROC getTransferOutcome : commandId = $commandId, commandDetailsSize = $commandDetailsSize" + debug "In PROC getTransferOutcome : commandDetails = $commandDetails" + debug "In PROC getTransferOutcome : bytes read are '$buffer'" + } + } + + if { $destinationFileId != "" && $dataSize != -1 } { + puts -nonewline $destinationFileId $buffer + flush $destinationFileId + set buffer "" + } + } + + closeTclServerConnection + + if { $totalBytesRead - [expr 41 + $commandDetailsSize] >= $dataSize && $commandId == 0 } { + + set transferOutcomeDetails "SUCCESS" + set returnValue 0 + + } else { + set transferOutcomeDetails $commandDetails + } + + return $returnValue +} + + + + + + diff --git a/dep/tclclient/ixTcl1.0/Generic/ftp.tcl b/dep/tclclient/ixTcl1.0/Generic/ftp.tcl new file mode 100644 index 00000000..2241df90 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/ftp.tcl @@ -0,0 +1,154 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: ftp.tcl +# Copyright © IXIA +# All Rights Reserved. +# +# Description: This file contains utilities FileTransfer +# +# Revision Log: +# Date Author Comments +# ----------- ------------------- -------------------------------------------- +# 10/25/2000 ds initial release +# +################################################################################## + +package require FTP + +######################################################################################## +# Procedure: putFtpLogWindow +# +# Description: Write a message to the ftp log window +# +# Input: textEntry +# +######################################################################################## +proc putFtpLogWindow {textEntry} \ +{ + logMsg $textEntry +} + + +######################################################################################## +# Procedure: ixFileSend +# +# Description: FTP protocol stuff... +# +# Input: +# +######################################################################################## +proc ixFileSend {ipAddress filename username password sourceDirectory destDirectory} \ +{ + set retCode 0 + + global ftpDone + + putFtpLogWindow "ipAddress=$ipAddress\nfilename=$filename\nusername=$username\npassword=$password\nsourceDirectory=$sourceDirectory\ndestDirectory=$destDirectory" + + set retCode 1 + if {[file isdirectory $destDirectory]} { + if {![FTP::Open $ipAddress $username $password -mode passive]} { + putFtpLogWindow"Error opening FTP session" + set retCode 2 + } else { + if {![FTP::Get $sourceDirectory$filename $destDirectory$filename]} { + putFtpLogWindow "Error getting file $sourceDirectory$filename" + set retCode 3 + } + } + FTP::Close + } else { + putFtpLogWindow "Error - invalid destination directory $destDirectory" + set retCode 4 + } + + set ftpDone $retCode + + if {$ftpDone == 1} { + set retCode 0 + } else { + set retCode 1 + } + + return $retCode +} + + +######################################################################################## +# Procedure: ixFileTransferStart +# +# Description: FTP protocol stuff... +# +# Input: +# +######################################################################################## +proc ixFileTransferStart { ipAddress filename username password sourceDirectory destDirectory } \ +{ + global ixTclSvrHandle ftpDone + + set retCode 0 + + ixFileSend $ipAddress $filename $username $password $sourceDirectory $destDirectory + + set ftpDone 0 + while {$ftpDone==0} { + ;# Make a time out counter here + ;# probably count to about 100 then + ;# a failure should be returned + set x 0 + after 1000 {set x 1} + vwait x + } + + switch $ftpDone { + 1 { + ixPuts "File transfer complete." + } + 2 { + ixPuts "Error opening FTP session on $ipAddress, username:$username." + set retCode 1 + } + 3 { + ixPuts "Error transferring file $sourceDirectory$filename to $destDirectory$filename." + set retCode 1 + } + 4 { + ixPuts "Error - invalid destination directory $destDirectory" + set retCode 1 + } + } + + return $retCode +} + + diff --git a/dep/tclclient/ixTcl1.0/Generic/highLevelAPI.tcl b/dep/tclclient/ixTcl1.0/Generic/highLevelAPI.tcl new file mode 100644 index 00000000..3e822dc6 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/highLevelAPI.tcl @@ -0,0 +1,2642 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: highLevelAPI.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 04-28-1998 HS Genesis +# +# Description: +# This file contains high level Tcl API commands. +# +################################################################################### + + +############################################################################### +# Procedure: ixInitialize +# +# Description: This procedure loads the IXIA Tcl package, sets the name of log +# and debug files, and performs any other initializations, if necessary. +# +# Argument(s): +# chassisList - List of hostnames or IP addresses of chassis in a chain. +# cableLen - Optional. The cable length choice. If not passed then cable3feet. +# logfilename - Optional. The name of the file for the test logs output. +# If not passed, then is blank. +# client - Optional. The name of the client. Defaults to local. Currently unused +# +# Returns: +# 0 : if no error +# 1 : if error connecting to chassis +# 2 : if version mismatch +# 3 : if chassis timeout occurred +# 4 : if connection interrupted by user +# 5 : if error connecting to Tcl server +############################################################################### +proc ixInitialize {chassisList {cableLen cable3feet} {logfilename ""} {client local}} \ +{ + set tclServer [lindex $chassisList 0] + set retCode [ixProxyConnect $tclServer $chassisList $cableLen $logfilename] + + return $retCode +} + + +######################################################################################## +# Procedure: ixConnectToChassis +# +# Description: This procedure connects to a list of chassis. An ID number is assigned to +# the chassis in sequence starting from 1 in the order that the list is passed. +# The first chassis in the list is assigned as the master chassis. +# +# Argument(s): +# chassisList - The list of hostnames or IP addresses of chassis in a chain +# cableLength - Optional. The length of the cables between the chassis. If not passed in, +# then uses cable3feet. Note - may be a list of lengths, one for each chassis +# in the chassisList. +# +# Returns: +# 0 : if no error +# 1 : if error connecting to chassis +# 2 : if version mismatch, ixTcl_versionMismatch +# 3 : if chassis timeout occurred, ixTcl_chassisTimeout +# 4 : if connection interrupted by user +######################################################################################## +proc ixConnectToChassis {chassisList {cableLength cable3feet}} \ +{ + return [connectToChassis $chassisList $cableLength] +} + + +######################################################################################## +# Procedure: ixConnectToTclServer +# +# Description: This procedure connects a unix +# +# Argument(s): +# serverName - Ip addr/name of TclServer +# +# Returns: +# 0 - if no error +# 1 - if any error found +######################################################################################## +proc ixConnectToTclServer {serverName} \ +{ + return [tclServer::connectToTclServer $serverName errMsg] +} + + +######################################################################################## +# Procedure: ixDisconnectTclServer +# +# Description: This procedure disconnects from the TclServer +# +# Argument(s): +# serverName - UNUSED. Still exists for backwards script support +# +# Returns: +######################################################################################## +proc ixDisconnectTclServer {{serverName ""}} \ +{ + return [tclServer::disconnectTclServer] +} + + +######################################################################################## +# Procedure: ixGetChassisID +# +# Description: This procedure gets the chassis ID of the specified chassis name. It is +# useful when multiple chassis are chained together. +# +# Argument(s): +# chassisName - chassis name for which ID is to be obtained +# +# Returns: +# -1 - if error found +# chasissID - ID number of the chassis +######################################################################################## +proc ixGetChassisID {chassisName} \ +{ + if [chassis get $chassisName] { + ixPuts "Error getting parameters for chassis $chassisName" + return -1 + } + set chassisID [chassis cget -id] + return $chassisID +} + + +######################################################################################## +# Procedure: ixDisconnectFromChassis +# +# Description: Disconnects from input chassis or list of chassis; if no arg given, then +# will removeAll. +# +# Argument(s): +# args - list of chassis to del; if empty, removeAll +# +# Returns: +# 0 - if no error +# +######################################################################################## +proc ixDisconnectFromChassis {args} \ +{ + if {[llength $args] == 0} { + chassisChain removeAll + } else { + foreach host $args { + chassis del $host + } + } + return 0 +} + + +######################################################################## +# Procedure: ixGlobalSetDefault +# +# Description: This command calls the setDefault for all the stream/port related +# commands as a form of initialization. +# +# Arguments: None +# +# Returns: Nothing +######################################################################## +proc ixGlobalSetDefault {} \ +{ + globalSetDefault +} + + +######################################################################################## +# Procedure: ixStartTransmit +# +# Description: Starts transmission on the specific ports +# +# Arguments: +# PortList - Represented in Chassis Card Port and can be a list also, example {1,1,1 1,1,2} +# +# Returns: +######################################################################################## +proc ixStartTransmit {PortList} \ +{ + upvar $PortList portList + + return [startTx portList] +} + + +######################################################################## +# Procedure: ixStartPortTransmit +# +# Description: This command starts Transmit on a single port; it will also stop transmit & +# zero stats on this port before transmitting. +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +######################################################################## +proc ixStartPortTransmit {chassis lm port} \ +{ + return [startPortTx $chassis $lm $port] +} + + +######################################################################## +# Procedure: ixStartStaggeredTransmit +# +# Description: This command arms each Tx port & then sends out a pulse to the master +# to begin transmitting +# +# Arguments: +# PortList - Represented in Chassis Card Port and can be a list also, for ex. {1,1,1 1,1,2} +# +# Returns: +######################################################################## +proc ixStartStaggeredTransmit {PortList} \ +{ + upvar $PortList portList + + return [startStaggeredTx portList] +} + + +######################################################################################## +# Procedure: ixStopTransmit +# +# Description: Stops transmission on the specific ports +# +# Arguments: +# PortList - Represented in Chassis Card Port and can be a list also, for ex. {1,1,1 1,1,2} +# +# Returns: +######################################################################################## +proc ixStopTransmit {PortList} \ +{ + upvar $PortList portList + + return [stopTx portList] +} + + +######################################################################## +# Procedure: ixStopPortTransmit +# +# Description: This command stops Tx on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: 0 if OK, 1 if port not configured +######################################################################## +proc ixStopPortTransmit {chassis lm port} \ +{ + return [stopPortTx $chassis $lm $port] +} + + +######################################################################## +# Procedure: ixStartCapture +# +# Description: This command turns on capture for each Rx port +# +# Arguments: +# PortList - Represented in Chassis Card Port and can be a list also for ex. {1,1,1 1,1,2} +# +# Returns: +######################################################################## +proc ixStartCapture {PortList} \ +{ + upvar $PortList portList + + return [startCapture portList] +} + + +######################################################################## +# Procedure: ixStartPrbsCapture +# +# Description: This command turns on capture and Packet Groups for each Rx port +# +# Arguments: +# PortList - Represented in Chassis Card Port and can be a list also for ex. {1,1,1 1,1,2} +# +# Returns: +######################################################################## +proc ixStartPrbsCapture {PortList} \ +{ + upvar $PortList portList + + return [startPrbsCapture portList] +} + + +######################################################################## +# Procedure: ixStopCapture +# +# This command stops capture for each Rx port +# +# Arguments: +# PortList - Represented in Chassis Card Port and can be a list also +# for ex. {1,1,1 1,1,2} +# +######################################################################## +proc ixStopCapture {PortList} \ +{ + upvar $PortList portList + + return [stopCapture portList] +} + + +######################################################################## +# Procedure: ixStopPrbsCapture +# +# This command stops capture and Packet Groups for each Rx port +# +# Arguments: +# PortList - Represented in Chassis Card Port and can be a list also +# for ex. {1,1,1 1,1,2} +# +######################################################################## +proc ixStopPrbsCapture {PortList} \ +{ + upvar $PortList portList + + return [stopPrbsCapture portList] +} + +######################################################################## +# Procedure: ixClearPrbsCapture +# +# This command clears PRBS capture for each Rx port +# +# Arguments: +# PortList - Represented in Chassis Card Port and can be a list also +# for ex. {1,1,1 1,1,2} +# +######################################################################## +proc ixClearPrbsCapture {PortList} \ +{ + upvar $PortList portList + + return [clearPrbsCapture portList] +} + + +######################################################################## +# Procedure: ixStartPortCapture +# +# This command starts capture on a single port; +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixStartPortCapture {chassis lm port} \ +{ + return [startPortCapture $chassis $lm $port] +} + + + +######################################################################## +# Procedure: ixStartPortPrbsCapture +# +# This command starts capture and Packet Groups on a single port; +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixStartPortPrbsCapture {chassis lm port} \ +{ + return [startPortPrbsCapture $chassis $lm $port] +} + + + +######################################################################## +# Procedure: ixStopPortCapture +# +# This command stops capture on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 1 if OK, 0 if port not configured +# +######################################################################## +proc ixStopPortCapture {chassis lm port {groupId 710} {create create} {destroy destroy}} \ +{ + return [stopPortCapture $chassis $lm $port] +} + + +######################################################################## +# Procedure: ixStopPortPrbsCapture +# +# This command stops capture and Packet Groups on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 1 if OK, 0 if port not configured +# +######################################################################## +proc ixStopPortPrbsCapture {chassis lm port {groupId 710} {create create} {destroy destroy}} \ +{ + return [stopPortPrbsCapture $chassis $lm $port] +} + +######################################################################## +# Procedure: ixClearPortPrbsCapture +# +# This command clears PRBS capture on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 1 if OK, 0 if port not configured +# +######################################################################## +proc ixClearPortPrbsCapture {chassis lm port {groupId 710} {create create} {destroy destroy}} \ +{ + return [clearPortPrbsCapture $chassis $lm $port] +} + + +######################################################################################## +# Procedure : ixClearStats +# +# Description: Clear statistics counters on the specific ports +# +# Arguments : +# ports - Represented in Chassis Card Port and can be a list also +# for ex. {1,1,1 1,1,2} +# +######################################################################################## +proc ixClearStats {PortList} \ +{ + upvar $PortList portList + + return [zeroStats portList] +} + + +######################################################################## +# Procedure: ixClearPortStats +# +# This command zeros all stats & stops the specified port if transmitting +# +# Argument(s): +# chassis chassis ID +# lm Load Module number +# port port number +# +######################################################################## +proc ixClearPortStats {chassis lm port} \ +{ + return [zeroPortStats $chassis $lm $port] +} + +######################################################################################## +# Procedure : ixClearPerStreamTxStats +# +# Description: Clear per stream Tx statistics counters on the portList +# +# Arguments : +# PortList - Represented in Chassis Card Port and can be a list also +# for ex. {1,1,1 1,1,2} +# +######################################################################################## +proc ixClearPerStreamTxStats {PortList} \ +{ + upvar $PortList portList + + return [clearPerStreamTxStats portList] +} + + +######################################################################## +# Procedure: ixClearPerStreamTxPortStats +# +# This command zeros all stream Tx stats on this port +# +# Argument(s): +# chassis chassis ID +# lm Load Module number +# port port number +# +######################################################################## +proc ixClearPerStreamTxPortStats {chassis lm port} \ +{ + return [clearPerStreamTxPortStats $chassis $lm $port] +} + + +######################################################################################## +# Procedure: ixRequestStats +# +# Description: This command combines the statGroup w/a portList or map array to request +# stats for a list of ports. statList command must be used to retrieve stats +# after call completion. +# +# Arguments: +# TxRxArray - either list of ports or array of ports +# +# Returns: +# TCL_OK or TCL_ERROR +# +######################################################################################## +proc ixRequestStats {TxRxArray} \ +{ + upvar $TxRxArray txRxArray + + return [requestStats txRxArray] +} + + +######################################################################## +# Procedure: ixClearTimeStamp +# +# This command synchronizes the timestamp value among all chassis +# +# Arguments: +# PortList - Represented in Chassis Card Port and can be a list also +# for ex. {1,1,1 1,1,2} +# +######################################################################## +proc ixClearTimeStamp {PortList} \ +{ + upvar $PortList portList + + return [clearTimeStamp portList] +} + + +######################################################################## +# Procedure: ixStartPacketGroups +# +# This command arms each Rx port & then sends out a pulse to the master +# to initiate packetGroup stats +# +# Arguments: +# PortList - Represented in Chassis Card Port and can be a list also +# for ex. {1,1,1 1,1,2} +# +######################################################################## +proc ixStartPacketGroups {PortList} \ +{ + upvar $PortList portList + + return [startPacketGroups portList] +} + + +######################################################################## +# Procedure: ixStartPortPacketGroups +# +# This command starts packetGroup stats on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixStartPortPacketGroups {chassis lm port} \ +{ + return [startPortPacketGroups $chassis $lm $port] +} + + +######################################################################## +# Procedure: ixStopPacketGroups +# +# This command arms each Rx port & then sends out a pulse to the master +# to stop packetGroup stats +# +# Arguments: +# PortList - Represented in Chassis Card Port and can be a list also +# for ex. {1,1,1 1,1,2} +# +######################################################################## +proc ixStopPacketGroups {PortList} \ +{ + upvar $PortList portList + + return [stopPacketGroups portList] +} + + +######################################################################## +# Procedure: ixClearPacketGroups +# +# This command arms each Rx port & then sends out a pulse to the master +# to clear packetGroup stats +# +# Arguments: +# PortList - Represented in Chassis Card Port and can be a list also +# for ex. {1,1,1 1,1,2} +# +######################################################################## +proc ixClearPacketGroups {PortList} \ +{ + upvar $PortList portList + + return [clearPacketGroups portList] +} + + +######################################################################## +# Procedure: ixClearPortPacketGroups +# +# This command clears packetGroup stats on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixClearPortPacketGroups {chassis lm port} \ +{ + return [clearPortPacketGroups $chassis $lm $port] +} + +######################################################################## +# Procedure: ixSetScheduledTransmitTime +# +# Description: This command sets scheduled transmit time on given port list +# +# Arguments: +# PortList - List of ports; for ex. {{1 1 1} {1 1 2}} +# +# Returns: +######################################################################## +proc ixSetScheduledTransmitTime {PortList duration } \ +{ + upvar $PortList portList + + return [setScheduledTransmitTime portList $duration] +} + +######################################################################## +# Procedure: ixClearScheduledTransmitTime +# +# Description: This command clears/resets scheduled transmit time on given +# port list +# +# Arguments: +# PortList - List of ports; for ex. {{1 1 1} {1 1 2}} +# +# Returns: +######################################################################## +proc ixClearScheduledTransmitTime {PortList} \ +{ + upvar $PortList portList + + set duration 0 + return [setScheduledTransmitTime portList $duration] +} + +######################################################################## +# Procedure: ixStopPortPacketGroups +# +# This command stops packetGroup stats on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixStopPortPacketGroups {chassis lm port} \ +{ + return [stopPortPacketGroups $chassis $lm $port] +} + + +######################################################################## +# Procedure: ixStartCollisions +# +# This command arms each Rx port & then sends out a pulse to the master +# to initiate collisions +# +# Arguments: +# PortList - Represented in Chassis Card Port and can be a list also +# for ex. {1,1,1 1,1,2} +# +######################################################################## +proc ixStartCollisions {PortList} \ +{ + upvar $PortList portList + + return [startCollisions portList] +} + + +######################################################################## +# Procedure: ixStartPortCollisions +# +# This command starts packetGroup stats on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixStartPortCollisions {chassis lm port} \ +{ + return [startPortCollisions $chassis $lm $port] +} + + +######################################################################## +# Procedure: ixStopCollisions +# +# This command arms each Rx port & then sends out a pulse to the master +# to stop collisions +# +# Arguments: +# PortList - Represented in Chassis Card Port and can be a list also +# for ex. {1,1,1 1,1,2} +# +######################################################################## +proc ixStopCollisions {PortList} \ +{ + upvar $PortList portList + + return [stopCollisions portList] +} + + +######################################################################## +# Procedure: ixStopPortCollisions +# +# This command stops collisions on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixStopPortCollisions {chassis lm port} \ +{ + return [stopPortCollisions $chassis $lm $port] +} + + +######################################################################## +# Procedure: ixLoadPoePulse +# +# This command loads the poe pulse +# +# Argument(s): +# PortArray either list of ports or array of ports +# +######################################################################## +proc ixLoadPoePulse {PortList} \ +{ + upvar $PortList portList + + return [loadPoePulse portList] +} + + +######################################################################## +# Procedure: ixLoadPortPoePulse +# +# This command loads the poe pulse on a single port. +# +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixLoadPortPoePulse {chassis lm port} \ +{ + return [loadPortPoePulse $chassis $lm $port] +} + + +######################################################################## +# Procedure: ixArmPoeTrigger +# +# This command arms the poe trigger +# +# Argument(s): +# PortArray either list of ports or array of ports +# +######################################################################## +proc ixArmPoeTrigger {PortList} \ +{ + upvar $PortList portList + + return [armPoeTrigger portList] +} + + +######################################################################## +# Procedure: ixArmPortPoeTrigger +# +# This command arms the poe trigger on a single port. +# +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixArmPortPoeTrigger {chassis lm port} \ +{ + return [armPortPoeTrigger $chassis $lm $port] +} + + +######################################################################## +# Procedure: ixAbortPoeArm +# +# This command aborts the poe trigger +# +# Argument(s): +# PortArray either list of ports or array of ports +# +######################################################################## +proc ixAbortPoeArm {PortList} \ +{ + upvar $PortList portList + + return [abortPoeArm portList] +} + + +######################################################################## +# Procedure: ixAbortPortPoeArm +# +# This command aborts the poe trigger on a single port. +# +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixAbortPortPoeArm {chassis lm port} \ +{ + return [abortPortPoeArm $chassis $lm $port] +} + + +######################################################################## +# Procedure: ixStartAtmOamTransmit +# +# This command starts the atm oam transmit +# +# Argument(s): +# PortArray either list of ports or array of ports +# +######################################################################## +proc ixStartAtmOamTransmit {PortList} \ +{ + upvar $PortList portList + + return [startAtmOamTransmit portList] +} + + +######################################################################## +# Procedure: ixStartPortAtmOamTransmit +# +# This command starts the atm oam transmit on a single port. +# +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixStartPortAtmOamTransmit {chassis lm port} \ +{ + return [startPortAtmOamTransmit $chassis $lm $port] +} + +######################################################################## +# Procedure: ixStopAtmOamTransmit +# +# This command starts the atm oam transmit +# +# Argument(s): +# PortArray either list of ports or array of ports +# +######################################################################## +proc ixStopAtmOamTransmit {PortList} \ +{ + upvar $PortList portList + + return [stopAtmOamTransmit portList] +} + + +######################################################################## +# Procedure: ixStopPortAtmOamTransmit +# +# This command starts the atm oam transmit on a single port. +# +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixStopPortAtmOamTransmit {chassis lm port} \ +{ + return [stopPortAtmOamTransmit $chassis $lm $port] +} + + + + +######################################################################################## +# Procedure : ixCreatePortListWildCard +# +# Description: This commands creates a list of ports in a sorted order based on the +# physical slots. It accepts * as a wild card to indicate all cards or all ports on a +# card. A wild card cannot be used for chassis ID. Also, if a combination of a list +# element containing wild cards and port numbers are passed, then the port list passed +# MUST be in a sorted order, otherwise the some of those ports might not make it in the +# list. For example, +# ixCreatePortListWildCard {1 * *} - all cards and all ports on chassis 1 +# ixCreatePortListWildCard {{1 1 *} {1 2 1} { 1 2 2}} - all ports on card 1 and +# ports 1 and 2 on card 2. +# +# Arguments : +# portList - Represented in Chassis Card Port and can be a list also +# excludePorts - exclude these ports from the sorted port list +# +######################################################################################## +proc ixCreatePortListWildCard {portList {excludePorts {}}} \ +{ + set retList {} + + # If excludePorts is passed as a single list, then put braces around it + if {[llength $excludePorts] == 3 && [llength [lindex $excludePorts 0]] == 1} { + set excludePorts [list $excludePorts] + } + + foreach portItem $portList { + scan [join [split $portItem ,]] "%s %s %s" ch fromCard fromPort + + set origFromPort $fromPort + + if { $ch == "*"} { + errorMsg "Chassis ID cannot be a wildcard. Enter a valid number" + return $retList + } + + if [chassis getFromID $ch] { + errorMsg "Chassis ID $ch has not been added to chassis chain." + continue + } + + set maxCardsInChassis [chassis cget -maxCardCount] + if { $fromCard == "*"} { + set fromCard 1 + set toCard $maxCardsInChassis + } else { + set toCard $fromCard + } + + for {set l $fromCard} {$l <= $toCard} {incr l} { + if [card get $ch $l] { + #errorMsg "Error getting card $ch $l" + continue + } + + set maxPorts [card cget -portCount] + + if { $origFromPort == "*"} { + set fromPort 1 + set toPort $maxPorts + } else { + set toPort $fromPort + } + + for {set p $fromPort} {$p <= $toPort} {incr p} { + if [port get $ch $l $p] { + continue + } + if {[lsearch $excludePorts "$ch $l $p"] == -1 && [lsearch $retList "$ch $l $p"] == -1} { + lappend retList [list $ch $l $p] + } + + } + } + } + + if {[llength $retList] == 0} { + errorMsg "Port list wild card is empty" + } + + return [lnumsort $retList] +} + + +######################################################################################## +# Procedure : ixCreateSortedPortList +# +# Description: This commands creates a list of ports in a sorted order based on the +# range of ports passed. +# +# For example - to add all ports on cards 1 through 5: +# ixCreateSortedPortList {{1 1 1} {1 5 4}} +# +# +# Arguments : +# portListFrom - First port +# portListTo - Last port +# +######################################################################################## +proc ixCreateSortedPortList {portListFrom portListTo excludePortList} \ +{ + scan $portListFrom "%d %d %d" fromChassis fromCard fromPort + scan $portListTo "%d %d %d" toChassis toCard toPort + set sortMap {} + + for {set c $fromChassis} {$c <= $toChassis} {incr c} { + if [chassis get $c] { + errorMsg "Chassis ID $c has not been added to chassis chain." + continue + } + + set maxCardsInChassis [chassis cget -maxCardCount] + + if {$c == $fromChassis} { + set firstCard $fromCard + } else { + set firstCard 1 + } + if {$c == $toChassis} { + if {$maxCardsInChassis < $toCard} { + set currLastCard $maxCardsInChassis + } else { + set currLastCard $toCard + } + } else { + set currLastCard $maxCardsInChassis + } + + for {set l $firstCard} {$l <= $currLastCard} {incr l} { + if [card get $c $l] { + ixPuts "Error getting card $l on chassis $c" + return 1 + } + set numports [card cget -portCount] + if {$numports == 0} { + continue + } + + if {($c == $fromChassis) && ($l == $fromCard)} { + set firstPort $fromPort + } else { + set firstPort 1 + } + + if {($c == $toChassis) && ($l == $toCard)} { + if {$numports < $toPort} { + set currLastPort $numports + } else { + set currLastPort $toPort + } + } else { + set currLastPort [card cget -portCount] + } + + for {set p $firstPort} {$p <= $currLastPort} {incr p} { + if {[lsearch $excludePortList "$c $l $p"] != -1} { + continue + } + set sortMap [lappend sortMap [list $c $l $p]] + } + } + } + return $sortMap +} + + +######################################################################################## +# Procedure : ixPuts +# +# Description: This command is similar to "puts" except that it has an update command +# so that the output queue gets flushed and the message gets printed immediately. In +# Window 95/NT platform, the "puts" command does not print the messages rightaway. +# +# Arguments : +# args - Message to display +# +######################################################################################## +proc ixPuts {args} \ +{ + catch { + if {[lindex $args 0] == "-nonewline"} { + set args [lreplace $args 0 0] + puts -nonewline [logger cget -ioHandle] [join $args " "] + } else { + puts [logger cget -ioHandle] [join $args " "] + } + + # Reported that the update blocks the display on Solaris using wish8.0 + # so use update idletasks instead + update + } +} + + +######################################################################################## +# Procedure : ixiaPortSetParms +# +# Description: This procedure sets specific port parameters +# +# Arguments : chassis - Chassis ID +# card - Card Number +# port - Port Number +# parm - Parameter to be set +# value - Value to set +######################################################################################## +proc ixiaPortSetParms {chassis card port parm value} \ +{ + if [port get $chassis $card $port] { + ixPuts "Error getting port $chassis $card $port from HAL" + return 1 + } + + puts "port config -$parm $value" + + port config -$parm $value + + if [port set $chassis $card $port] { + ixPuts "Error setting port $chassis $card $port in HAL" + return 1 + } + if [port write $chassis $card $port] { + ixPuts "Error writing port $chassis $card $port in Hardware" + return 1 + } + + return 0 +} + + +######################################################################################## +# Procedure : ixiaReadWriteMII +# +# Description: This procedure will read/write values from/to the MII +# on . 4 character hex string and Action will be READ or +# WRITE. register will be a value between 0 and 31 +# +# Arguments : +# ports - Represented in Chassis Card Port and can be a list also +# for ex. {1,1,1 1,1,2} +# action - READ or WRITE +# register - Value between 0 and 31 +# code - 4 character hex string +# +######################################################################################## + +proc ixiaReadWriteMII {ports action register code} \ +{ + + if {($action != "READ") && ($action != "WRITE")} { + ixPuts "Error: Action parameter error use READ or WRITE" + return 1 + } + if {($register < 0) || ($register > 31)} { + ixPuts "Error: register parameter error - valid range between 0 - 31" + return 1 + } + + set retValues {} + + foreach prt $ports { + scan $prt "%d %d %d" c l p + mii get $c $l $p + + if {$action == "READ"} { + set val [mii cget -registerValue] + lappend retValues $val + } + + mii configure -miiRegister $register + mii configure -registerValue $code + if {$action == "WRITE"} { + if [mii set $c $l $p] { + errorMsg "Error setting Mii [getPortId $c $l $p] in HAL." + return 1 + } + if [mii write $c $l $p] { + errorMsg "Error setting Mii [getPortId $c $l $p] in HAL." + return 1 + } + } + } + +# +# This procedure is set up to return a list of MII register values for multiple ports +# however if only one port is fed to it then one value will be return. It works with +# either multiple ports or a single port in the $ports list +# + if {$action == "READ"} { + return $retValues + } else { + return 0 + } +} + + +######################################################################################## +# Procedure: ixTclSvrConnect +# +# Description: This procedure launches the Tcl Server on the chassis for multi-users. +# +# Argument(s): +# serverName name of hostnames or IP address of chassis to connect to +# +######################################################################################## +proc ixTclSvrConnect { serverName } \ +{ + return [tclServer::connectToTclServer $serverName errMsg] +} + + +######################################################################################## +# Procedure: ixTclSvrDisconnect +# +# Description: This procedure disconnects from the TclServer socket. +# +# +######################################################################################## +proc ixTclSvrDisconnect {} \ +{ + return [tclServer::disconnectTclServer] +} + + +######################################################################## +# Procedure: ixEnableArpResponse +# +# This command gets the MAC & IP addresses for that port, sets up the +# address table and enables the arp response engine for all ports in +# the portlist +# +# Arguments(s): +# mapType - either oneIpToOneMAC or manyIpToOneMAC +# PortMap - list or array of ports, ie. ixgSortMap +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixEnableArpResponse { mapType PortMap } \ +{ + upvar $PortMap portMap + return [enableArpResponse $mapType portMap write] +} + + +######################################################################## +# Procedure: ixEnablePortArpResponse +# +# This command gets the MAC & IP addresses for that port, sets up the +# address table and enables the arp response engine for the specified port +# +# Arguments(s): +# mapType - either oneIpToOneMAC or manyIpToOneMAC +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixEnablePortArpResponse { mapType chassis lm port {write write}} \ +{ + return [enablePortArpResponse $mapType $chassis $lm $port $write] +} + + +######################################################################## +# Procedure: ixDisableArpResponse +# +# This command disables the arp response engine for all ports in +# the portlist +# +# Arguments(s): +# PortMap - list or array of ports, ie. ixgSortMap +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixDisableArpResponse { PortMap } \ +{ + upvar $PortMap portMap + + return [disableArpResponse portMap write] +} + + +######################################################################## +# Procedure: ixTransmitArpRequest +# +# This command transmits an Arp request via the protocol server. +# +# Arguments: +# TxRxArray - either array or list containing ports to transmit +# arp request on +# +######################################################################## +proc ixTransmitArpRequest {TxRxArray} \ +{ + upvar $TxRxArray txRxArray + + return [transmitArpRequest txRxArray] +} + + +######################################################################## +# Procedure: ixClearArpTable +# +# This command clears the arp table via the protocol server. +# +# Arguments: +# TxRxArray - either array or list containing ports to clear +# arp table on +# +######################################################################## +proc ixClearArpTable {TxRxArray} \ +{ + upvar $TxRxArray txRxArray + + return [clearArpTable txRxArray] +} + + +######################################################################## +# Procedure: ixDisablePortArpResponse +# +# This command disables the arp response engine for the specified port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixDisablePortArpResponse {chassis lm port {write write}} \ +{ + return [disablePortArpResponse $chassis $lm $port $write] +} + + +######################################################################## +# Procedure: ixTransmitPortArpRequest +# +# This command transmits an Arp request via the protocol server on a +# single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixTransmitPortArpRequest {chassis lm port} \ +{ + return [transmitPortArpRequest $chassis $lm $port] +} + + +######################################################################## +# Procedure: ixClearPortArpTable +# +# This command clears the arp table on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixClearPortArpTable {chassis lm port} \ +{ + return [clearPortArpTable $chassis $lm $port] +} + +######################################################################## +# Procedure: ixSetPacketGroupMode +# +# This command sets all the RX ports in the list or array to packet +# group mode +# +# Arguments(s): +# TxRxArray - list or array of RX ports to change +# write - write ports to hw as they are modified +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixSetPacketGroupMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + return [setPacketGroupMode txRxArray $write] +} + + +######################################################################## +# Procedure: ixSetPortPacketGroupMode +# +# This command sets all the RX ports for this port to packet +# group mode +# +# Arguments(s): +# chassis +# lm +# port +# write - write port to hw +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixSetPortPacketGroupMode {chassis lm port {write nowrite}} \ +{ + set retCode $::TCL_OK + + if [catch {format "%d,%d,%d" $chassis $lm $port} txRxArray] { + errorMsg $txRxArray + set retCode $::TCL_ERROR + } else { + set retCode [setPacketGroupMode txRxArray $write] + } + + return $retCode +} + + +######################################################################## +# Procedure: ixSetAutoDetectInstrumentationMode +# +# This command sets all the RX ports in the list or array to +# all the autoinstrumentation modes PG/DataIntegrity/SequenceChecking +# +# Arguments(s): +# TxRxArray - list or array of RX ports to change +# write - write ports to hw as they are modified +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixSetAutoDetectInstrumentationMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + return [setAutoDetectInstrumentationMode txRxArray $write] +} + + +######################################################################## +# Procedure: ixSetPortAutoDetectInstrumentationMode +# +# This command sets all the RX ports for this port to +# all the autoinstrumentation modes PG/DataIntegrity/SequenceChecking +# +# Arguments(s): +# chassis +# lm +# port +# write - write port to hw +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixSetPortAutoDetectInstrumentationMode {chassis lm port {write nowrite}} \ +{ + set retCode $::TCL_OK + + if [catch {format "%d,%d,%d" $chassis $lm $port} txRxArray] { + errorMsg $txRxArray + set retCode $::TCL_ERROR + } else { + set retCode [setAutoDetectInstrumentationMode txRxArray $write] + } + + return $retCode +} + +######################################################################## +# Procedure: ixSetWidePacketGroupMode +# +# This command sets all the RX ports in the list or array to wide packet +# group mode +# +# Arguments(s): +# TxRxArray - list or array of RX ports to change +# write - write ports to hw as they are modified +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixSetWidePacketGroupMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + return [setWidePacketGroupMode txRxArray $write] +} + + +######################################################################## +# Procedure: ixSetPortWidePacketGroupMode +# +# This command sets all the RX ports for this port to wide packet +# group mode +# +# Arguments(s): +# chassis +# lm +# port +# write - write port to hw +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixSetPortWidePacketGroupMode {chassis lm port {write nowrite}} \ +{ + set retCode $::TCL_OK + + if [catch {format "%d,%d,%d" $chassis $lm $port} txRxArray] { + errorMsg $txRxArray + set retCode $::TCL_ERROR + } else { + set retCode [setWidePacketGroupMode txRxArray $write] + } + + return $retCode +} + + +######################################################################## +# Procedure: ixSetCaptureMode +# +# This command sets all the RX ports in the list or array to capture +# mode +# +# Arguments(s): +# TxRxArray - list or array of RX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc ixSetCaptureMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + return [setCaptureMode txRxArray $write] +} + + +######################################################################## +# Procedure: ixSetPortCaptureMode +# +# This command sets receive mode for the specified port to capture mode +# +# Arguments(s): +# chassis +# lm +# port +# write - write port to hw +# +######################################################################## +proc ixSetPortCaptureMode {chassis lm port {write nowrite}} \ +{ + set retCode $::TCL_OK + + if [catch {format "%d,%d,%d" $chassis $lm $port} txRxArray] { + errorMsg $txRxArray + set retCode $::TCL_ERROR + } else { + set retCode [setCaptureMode txRxArray $write] + } + + return $retCode +} + + +######################################################################## +# Procedure: ixSetTcpRoundTripFlowMode +# +# This command sets all the RX ports in the list or array to tcp round +# trip flow mode +# +# Arguments(s): +# TxRxArray - list or array of RX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc ixSetTcpRoundTripFlowMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + return [setTcpRoundTripFlowMode txRxArray $write] +} + + +######################################################################## +# Procedure: ixSetPortTcpRoundTripFlowMode +# +# This command sets receive mode for the specified port to tcp round +# trip flow mode +# +# Arguments(s): +# c - chassis +# l - card +# p - port +# write - write port to hw +# +######################################################################## +proc ixSetPortTcpRoundTripFlowMode {c l p {write nowrite}} \ +{ + set retCode 0 + + if [catch {format "%d,%d,%d" $c $l $p} txRxArray] { + errorMsg "$txRxArray" + set retCode 1 + } else { + set retCode [setTcpRoundTripFlowMode txRxArray $write] + } + + return $retCode +} + + +######################################################################## +# Procedure: ixSetDataIntegrityMode +# +# This command sets all the RX ports in the list or array to Data Integrity +# mode +# +# Arguments(s): +# TxRxArray - list or array of RX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc ixSetDataIntegrityMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + return [setDataIntegrityMode txRxArray $write] +} + + +######################################################################## +# Procedure: ixSetPortDataIntegrityMode +# +# This command sets receive mode for the specified port to Data Integrity +# mode +# +# Arguments(s): +# c - chassis +# l - card +# p - port +# write - write port to hw +# +######################################################################## +proc ixSetPortDataIntegrityMode {c l p {write nowrite}} \ +{ + set retCode $::TCL_OK + + if [catch {format "%d,%d,%d" $c $l $p} txRxArray] { + errorMsg "$txRxArray" + set retCode $::TCL_ERROR + } else { + set retCode [setDataIntegrityMode txRxArray $write] + } + + return $retCode +} + +######################################################################## +# Procedure: ixSetPrbsMode +# +# This command sets all the RX ports in the list or array to Prbs mode +# +# Arguments(s): +# TxRxArray - list or array of RX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc ixSetPrbsMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + return [setPrbsMode txRxArray $write] +} + + +######################################################################## +# Procedure: ixSetPortPrbsMode +# +# This command sets receive mode for the specified port to Prbs mode +# +# Arguments(s): +# c - chassis +# l - card +# p - port +# write - write port to hw +# +######################################################################## +proc ixSetPortPrbsMode {c l p {write nowrite}} \ +{ + set retCode $::TCL_OK + + if [catch {format "%d,%d,%d" $c $l $p} txRxArray] { + errorMsg "$txRxArray" + set retCode $::TCL_ERROR + } else { + set retCode [setPrbsMode txRxArray $write] + } + + return $retCode +} + + +######################################################################## +# Procedure: ixSetSequenceCheckingMode +# +# This command sets all the TX ports in the list or array to Sequence +# Checking Mode +# +# Arguments(s): +# TxRxArray - list or array of RX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc ixSetSequenceCheckingMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + return [setSequenceCheckingMode txRxArray $write] +} + + +######################################################################## +# Procedure: ixSetPortSequenceCheckingMode +# +# This command sets receive mode for the specified port to Sequence +# Checking Mode +# +# Arguments(s): +# c - chassis +# l - card +# p - port +# write - write port to hw +# +######################################################################## +proc ixSetPortSequenceCheckingMode {c l p {write nowrite}} \ +{ + set retCode $::TCL_OK + + if [catch {format "%d,%d,%d" $c $l $p} txRxArray] { + errorMsg "$txRxArray" + set retCode $::TCL_ERROR + } else { + set retCode [setSequenceCheckingMode txRxArray $write] + } + + return $retCode +} + + +######################################################################## +# Procedure: ixSetPacketFlowMode +# +# This command sets all the TX ports in the list or array to packet +# flow mode +# +# Arguments(s): +# TxRxArray - list or array of TX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc ixSetPacketFlowMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + return [setPacketFlowMode txRxArray $write] +} + + +######################################################################## +# Procedure: ixSetPortPacketFlowMode +# +# This command sets specified port to packet flow mode +# +# Arguments(s): +# chassis +# lm +# port +# write - write port to hw +# +######################################################################## +proc ixSetPortPacketFlowMode {c l p {write nowrite}} \ +{ + set retCode 0 + + if [catch {format "%d,%d,%d" $c $l $p} txRxArray] { + logMsg "ixSetPortPacketFlowMode: $txRxArray" + set retCode 1 + } else { + set retCode [setPacketFlowMode txRxArray $write] + } + + return $retCode +} + + +######################################################################## +# Procedure: ixSetPacketStreamMode +# +# This command sets all the TX ports in the list or array to packet +# stream mode +# +# Arguments(s): +# TxRxArray - list or array of TX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc ixSetPacketStreamMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + return [setPacketStreamMode txRxArray $write] +} + + +######################################################################## +# Procedure: ixSetPortPacketStreamMode +# +# This command sets specified port to packet stream mode +# +# Arguments(s): +# chassis +# lm +# port +# write - write port to hw +# +######################################################################## +proc ixSetPortPacketStreamMode {chassis lm port {write nowrite}} \ +{ + set retCode 0 + + if [catch {format "%d,%d,%d" $chassis $lm $port} txRxArray] { + logMsg "ixSetPortPacketStreamMode: $txRxArray" + set retCode 1 + } else { + set retCode [setPacketStreamMode txRxArray $write] + } + + return $retCode +} + + +######################################################################## +# Procedure: ixSetAdvancedStreamSchedulerMode +# +# This command sets all the TX ports in the list or array to packet +# flow mode +# +# Arguments(s): +# TxRxArray - list or array of TX ports to change +# write - write ports to hw as they are modified +# +######################################################################## +proc ixSetAdvancedStreamSchedulerMode {TxRxArray {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + return [changePortTransmitMode txRxArray $::portTxModeAdvancedScheduler $write] +} + + +######################################################################## +# Procedure: ixSetPortAdvancedStreamSchedulerMode +# +# This command sets specified port to packet flow mode +# +# Arguments(s): +# chassis +# lm +# port +# write - write port to hw +# +######################################################################## +proc ixSetPortAdvancedStreamSchedulerMode {c l p {write nowrite}} \ +{ + set retCode $::TCL_OK + + if [catch {format "%d,%d,%d" $c $l $p} txRxArray] { + errorMsg "$txRxArray" + set retCode $::TCL_ERROR + } else { + set retCode [setAdvancedStreamSchedulerMode txRxArray $write] + } + + return $retCode +} + + +######################################################################## +# Procedure: ixWritePortsToHardware +# +# This command writes the ports, including speed, etc into hardware. It +# differs from writeConfigToHardware because this command writes all +# the phy as well as the configuration. +# +# Argument(s): +# PortArray either list of ports or array of ports +# args options include: +# -verbose +# -noVerbose +# -writeProtocolServer +# -noProtocolServer +# +######################################################################## +proc ixWritePortsToHardware {PortArray args} \ +{ + upvar $PortArray portArray + + return [writePortsToHardware portArray [join [list -noVerbose $args]]] +} + + +######################################################################## +# Procedure: ixWriteConfigToHardware +# +# This command writes the port array into hardware +# +# Argument(s): +# PortArray either list of ports or array of ports +# args options include: +# -verbose +# -noVerbose +# -writeProtocolServer +# -noProtocolServer +# +######################################################################## +proc ixWriteConfigToHardware {PortArray args} \ +{ + upvar $PortArray portArray + + return [writeConfigToHardware portArray [join [list -noVerbose $args]]] +} + + +######################################################################## +# Procedure: ixCheckTransmitDone +# +# This command polls the TX rate counters & returns when done transmitting +# +# Argument(s): +# PortArray either list of ports or array of ports +# +######################################################################## +proc ixCheckTransmitDone {PortArray} \ +{ + upvar $PortArray portArray + + return [checkAllTransmitDone portArray] +} + + +######################################################################## +# Procedure: ixCheckPortTransmitDone +# +# This command polls the TX rate counters & returns the number of frames +# transmitted +# +# Argument(s): +# chassis chassis ID +# lm Load Module number +# port port number +# +######################################################################## +proc ixCheckPortTransmitDone {chassis lm port} \ +{ + return [checkTransmitDone $chassis $lm $port] +} + + +######################################################################## +# Procedure: ixCheckLinkState +# +# This command checks the link state of all ports in parallel and labels +# the ones that are down. Then it polls the links that are down for two +# seconds and returns 1 if any port is still down and a 0 if all ports are +# up. +# +# Arguments(s): +# PortArray array or list of ports, ie, ixgSortMap +# +######################################################################## +proc ixCheckLinkState {PortArray {message messageOn}} \ +{ + upvar $PortArray portArray + + return [checkLinkState portArray] +} + + +######################################################################## +# Procedure: ixCheckPPPState +# +# This command checks the PPP state of all PoS ports in parallel and labels +# the ones that are down. Then it polls the links that are down for two +# seconds and returns 1 if any port is still down and a 0 if all ports are +# up. +# +# Arguments(s): +# PortArray array or list of ports, ie, ixgSortMap +# +######################################################################## +proc ixCheckPPPState {PortArray {message messageOn}} \ +{ + upvar $PortArray portArray + + return [checkPPPState portArray $message] +} + + + +######################################################################## +# Procedure: ixCollectStats +# +# This command polls the RX counters for the specified stat +# +# Argument(s): +# rxList - list of receive ports +# statName - name of stat to poll (need cget name) +# RxNumFrames - array containing the returned rx stats +# TotalRxFrames - total received frames +# +######################################################################## +proc ixCollectStats {rxList statName RxNumFrames TotalRxFrames} \ +{ + upvar $RxNumFrames rxNumFrames + upvar $TotalRxFrames totalRxFrames + + return [collectStats $rxList $statName rxNumFrames totalRxFrames] +} + + +######################################################################################## +# Procedure: ixProxyConnect +# +# Description: This command connects to the proxy server... +# +# Argument(s): +# tclServer - The name of the machine that is running an ixTclServer to connect with +# chassisList - The list of hostnames or IP addresses of chassis in a chain +# cableLen - The choice of the cable length between chassis. Defaults to cable3feet is not given +# logFilename - The name of the file for the test logs output +# +# Returns: +# 0 : if no error +# 1 : if error connecting to chassis +# 2 : if version mismatch +# 3 : if chassis timeout occurred +# 4 : if connection interrupted by user +# 5 : if error connecting to Tcl server +######################################################################################## +proc ixProxyConnect {tclServer chassisList {cableLen cable3feet} {logFilename ""}} \ +{ + # Default return code to no error found + set retCode 0 + + if {[ixTclHal::isCleanUpDone]} { + debugMsg "ixProxyConnect: package req IxTclHal" + package req IxTclHal + } + + if {[info exists logFilename] && ([string length $logFilename] > 0)} { + logOn $logFilename + } + + if {[isUNIX]} { + set retCode [ixConnectToTclServer $tclServer] + if {$retCode == 1} { + # We need to change the return code, because it conflicts with the code from ixConnectToChassis + set retCode 5 + } + } + if {$retCode == 0} { + set retCode [ixConnectToChassis $chassisList $cableLen] + } + return $retCode +} + + +######################################################################## +# Procedure: ixResetSequenceIndex +# +# This command reset the sequence index +# +# Argument(s): +# PortArray either list of ports or array of ports +# +######################################################################## +proc ixResetSequenceIndex {PortArray} \ +{ + upvar $PortArray portArray + + return [resetSequenceIndex portArray] +} + + +######################################################################## +# Procedure: ixResetPortSequenceIndex +# +# Description: This command reset sequence index on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Return: 0 if OK, 1 if port not configured +######################################################################## +proc ixResetPortSequenceIndex {chassis lm port} \ +{ + return [resetPortSequenceIndex $chassis $lm $port] +} + + + +######################################################################## +# Procedure: ixRestartAutoNegotiation +# +# Description: This command restarts auto negotiation +# +# Arguments: +# TxRxArray - either list of ports or array of ports +# +# Returns: The return code from restartAutoNegotiation +######################################################################## +proc ixRestartAutoNegotiation {TxRxArray} \ +{ + upvar $TxRxArray txRxArray + + return [restartAutoNegotiation txRxArray] +} + +######################################################################## +# Procedure: ixRestartPortAutoNegotiation +# +# Description: This command restarts auto negotiation +# +# Arguments(s): +# chassis +# lm +# port +# +# Returns: The return code from restartAutoNegotiation +######################################################################## +proc ixRestartPortAutoNegotiation {chassis lm port} \ +{ + set retCode $::TCL_OK + + if [catch {format "%d,%d,%d" $chassis $lm $port} txRxArray] { + errorMsg "$txRxArray" + set retCode $::TCL_ERROR + } else { + set retCode [restartAutoNegotiation txRxArray] + } + + return $retCode +} + + +######################################################################## +# Procedure: ixRestartPPPNegotiation +# +# Description: This command restarts PPP negotiation +# +# Arguments: +# TxRxArray - either list of ports or array of ports +# +# Returns: The return code from restartAutoNegotiation +######################################################################## +proc ixRestartPPPNegotiation {TxRxArray} \ +{ + upvar $TxRxArray txRxArray + + set allPorts [getAllPorts txRxArray] + + ixStopTransmit allPorts + return [restartAutoNegotiation txRxArray] +} + + +######################################################################## +# Procedure: ixRestartPortPPPNegotiation +# +# Description: This command restarts PPP negotiation +# +# Arguments(s): +# chassis +# lm +# port +# +# Returns: The return code from restartAutoNegotiation +######################################################################## +proc ixRestartPortPPPNegotiation {chassis lm port} \ +{ + set retCode $::TCL_OK + + if [catch {format "%d,%d,%d" $chassis $lm $port} txRxArray] { + errorMsg "$txRxArray" + set retCode $::TCL_ERROR + } else { + ixStopPortTransmit $chassis $lm $port + set retCode [restartAutoNegotiation txRxArray] + } + + return $retCode +} + + +######################################################################## +# Procedure: ixSimulatePhysicalInterfaceUp +# +# Description: This command simulates physical interface up on a port list +# +# Arguments: +# TxRxArray - either list of ports or array of ports +# +# Returns: The return code from simulatePhysicalInterfaceUp +######################################################################## +proc ixSimulatePhysicalInterfaceUp {TxRxArray} \ +{ + upvar $TxRxArray txRxArray + + return [simulatePhysicalInterfaceUp txRxArray] +} + + + +######################################################################## +# Procedure: ixSimulatePortPhysicalInterfaceUp +# +# Description: This command simulates physical interface down on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Returns: The return code from simulatePhysicalInterfaceUp +######################################################################## +proc ixSimulatePortPhysicalInterfaceUp {chassis lm port} \ +{ + set retCode $::TCL_OK + + if [catch {format "%d,%d,%d" $chassis $lm $port} txRxArray] { + errorMsg "$txRxArray" + set retCode $::TCL_ERROR + } else { + set retCode [simulatePhysicalInterfaceUp txRxArray] + } + + return $retCode +} + + +######################################################################## +# Procedure: ixSimulatePhysicalInterfaceDown +# +# Description: This command simulates physical interface down on a port list +# +# Arguments: +# TxRxArray - either list of ports or array of ports +# +# Returns: The return code from simulatePhysicalInterfaceDown +######################################################################## +proc ixSimulatePhysicalInterfaceDown {TxRxArray} \ +{ + upvar $TxRxArray txRxArray + + return [simulatePhysicalInterfaceDown txRxArray] +} + + + +######################################################################## +# Procedure: ixSimulatePortPhysicalInterfaceDown +# +# Description: This command simulates physical interface down on a single port +# +# Arguments(s): +# chassis +# lm +# port +# +# Returns: The return code from simulatePhysicalInterfaceDown +######################################################################## +proc ixSimulatePortPhysicalInterfaceDown {chassis lm port} \ +{ + set retCode $::TCL_OK + + if [catch {format "%d,%d,%d" $chassis $lm $port} txRxArray] { + errorMsg "$txRxArray" + set retCode $::TCL_ERROR + } else { + set retCode [simulatePhysicalInterfaceDown txRxArray] + } + + return $retCode +} + + + +################################################################################## +# Procedure: ixIsOverlappingIpAddress +# +# Description: Check if IP addresses are overlapping. +# +# Arguments: ipAddress1 - first IP address to compare to +# count1 - number of IP addresses starting from ipAddress1 to compare to +# ipAddress2 - first IP address to compare to +# count2 - number of IP addresses starting from ipAddress2 to compare to +# +# Returns: 1 - $::true +# 0 - $::false +# +################################################################################## +proc ixIsOverlappingIpAddress {ipAddress1 count1 ipAddress2 count2} \ +{ + return [dataValidation::isOverlappingIpAddress $ipAddress1 $count1 $ipAddress2 $count2] +} + + +################################################################################## +# Procedure: ixIsSameSubnet +# +# Description: Check if ip addresses are in the same subnet. +# Note that all ports in one user porfile should be in same subnet. +# +# Arguments: ipAddr1 - ipAddress to compare to +# mask1 - net mask of ipAddr1 +# ipAddr2 - ipAddress to compare to +# mask2 - net mask of ipAddr2 +# +# Returns: 1 - $::true +# 0 - $::false +# +################################################################################## +proc ixIsSameSubnet {ipAddr1 mask1 ipAddr2 mask2} \ +{ + return [dataValidation::isSameSubnet $ipAddr1 $mask1 $ipAddr2 $mask2] +} + + +################################################################################## +# Procedure: ixIsValidHost +# +# Description: Check if the host part of an IP address is not all 0's or all 1's assuming +# its net mask is valid. +# +# Arguments: ipAddr - ipAddress +# mask - net mask +# +# Returns: 1 - $::true +# 0 - $::false +# +################################################################################## +proc ixIsValidHost {ipAddr mask} \ +{ + return [dataValidation::isValidHostPart $ipAddr $mask] +} + + +################################################################################## +# Procedure: ixIsValidNetMask +# +# Description: Check if net mask is valid. i.e. In binary form, the mask must +# have consecutive 1's followed by consecutive 0's +# +# Arguments: mask - net mask +# +# Returns: 1 - $::true +# 0 - $::false +# +################################################################################## +proc ixIsValidNetMask {mask} \ +{ + return [dataValidation::isValidNetMask $mask] +} + + +################################################################################## +# Procedure: ixIsValidUnicastIp +# +# Description: Check if ipAddress accomplied with the following +# 1) it is not 0.0.0.0 +# 2) it is not 255.255.255.255 +# 3) it is not loopback address (127.x.x.x) +# 4) it is not multicast address +# (224.0.0.0 - 239.255.255.255, i.e first 4 bits not 1110) +# +# Arguments: ipAddr - IP address +# +# Returns: 1 - $::true +# 0 - $::false +# +################################################################################## +proc ixIsValidUnicastIp {ipAddr} \ +{ + return [dataValidation::isValidUnicastIp $ipAddr] +} + + +################################################################################## +# Procedure: ixConvertFromSeconds +# +# Description: Convert seconds to hours, minutes, seconds +# +# Arguments: seconds +# Hours - returned +# Minutes - returned +# Seconds - returned +# +# Returns: $::TCL_OK or $::TCL_ERROR +# +################################################################################## +proc ixConvertFromSeconds {time Hours Minutes Seconds} \ +{ + upvar $Hours hours + upvar $Minutes minutes + upvar $Seconds seconds + + return [convertFromSeconds $time hours minutes seconds] +} + + +################################################################################## +# Procedure: ixConvertToSeconds +# +# Description: Convert time in hours:minutes:seconds format to seconds +# +# Arguments: hours - number of hours of time +# minutes - number of minutes of time +# seconds - number of seconds of time +# +# Returns: number of seconds +# +################################################################################## +proc ixConvertToSeconds {hours minutes seconds} \ +{ + return [convertToSeconds $hours $minutes $seconds] +} + + +######################################################################## +# Procedure: ixEnablePortIntrinsicLatencyAdjustment +# +# This command enables the Intrinsic Latency Adjustment on the ports +# that support the feature +# +# Arguments(s): +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixEnablePortIntrinsicLatencyAdjustment { chassId cardId portId enable {write nowrite}} \ +{ + set portList [list [list $chassId $cardId $portId]] + return [enableIntrinsicLatencyAdjustment portList $enable $write] +} + +######################################################################## +# Procedure: ixEnableIntrinsicLatencyAdjustment +# +# This command enables the Intrinsic Latency Adjustment on the ports +# that support the feature +# +# Arguments(s): +# +# Return: +# 0 if OK, 1 if port not configured +# +######################################################################## +proc ixEnableIntrinsicLatencyAdjustment { TxRxArray enable {write nowrite}} \ +{ + upvar $TxRxArray txRxArray + + return [enableIntrinsicLatencyAdjustment txRxArray $enable $write] +} + + + +######################################################################## +# Procedure: ixIsIntrinsicLatencyAdjustmentEnabled +# +# This command returns the true if the Intrinsic Latency is enabled +# +# Arguments(s): +# +# Return: +# 1 if enabled, 0 if not +# +######################################################################## +proc ixIsIntrinsicLatencyAdjustmentEnabled { chassId cardId portId} \ +{ + return [isIntrinsicLatencyAdjustmentEnabled $chassId $cardId $portId] +} + + + + diff --git a/dep/tclclient/ixTcl1.0/Generic/interface.tcl b/dep/tclclient/ixTcl1.0/Generic/interface.tcl new file mode 100644 index 00000000..0e786f41 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/interface.tcl @@ -0,0 +1,262 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: interface.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 01-23-1998 DS Genesis +# +# Description: This is the main configuration interface file for all the commands. +# If new commands or paramters are to be added, only this file needs to be updated. +# The actual files containing the commands do not have to be touched. +# The "config" commands contain all configurable parameters, that is, +# read-write. The "cget parameters" are read-only. "cget" can be used to store +# default values which the user cannot change. +# +# Included in this file are the user config params that are generic for all tests. +# +################################################################################## + + +################################################################################## + +# A list containing all the commands. Used to display them when "help" +# command is given +# +# **********NOTE************* +# Update this list whenever a new command is added or an old one deleted. + +set allCommands {{Commands Description} \ + {-------- ----------------------------------------------------------------------------} \ + {advancedTestParameter Configure advanced test parameters} \ + {fastpath Configure fastpath configuration parameters} \ + {ipfastpath Configure ipfastpath configuration parameters (deprecated)} \ + {learn Configure learn frames} \ + {logger Configure and enable logging to files} \ + {map Configure the transmit and receive Chassis.Card.Port traffic mapping} \ + {tclClient Configure the tclClient parameters} \ + {testProfile Configure test parameters} } + + +################################## +# COMMAND: advancedTestParameter +################################## +proc initCommand_zz_advancedTestParameter {} \ +{ + defineCommand registerCommand advancedTestParameter + + defineCommand registerParameter -command advancedTestParameter -type string -parameter dialogState -defaultValue normal -validValues {normal iconic destroyedByUser} + + defineCommand registerParameter -command advancedTestParameter -type integer -parameter maxConnectRetries -defaultValue 3 + defineCommand registerParameter -command advancedTestParameter -type string -parameter streamPatternType -defaultValue repeat -validValues {incrByte incrWord decrByte decrWord patternTypeRandom repeat nonRepeat} + defineCommand registerParameter -command advancedTestParameter -type string -parameter streamDataPattern -defaultValue x00010203 -validValues {dataPatternRandom allOnes allZeroes xAAAA x5555 x7777 xDDDD xF0F0 x0F0F xFF00FF00 x00FF00FF xFFFF0000 x0000FFFF x00010203 x00010002 xFFFEFDFC xFFFFFFFE userpattern} + defineCommand registerParameter -command advancedTestParameter -type integer -parameter streamFrameType -defaultValue {08 00} + defineCommand registerParameter -command advancedTestParameter -type string -parameter streamPattern -defaultValue {} + defineCommand registerParameter -command advancedTestParameter -type string -parameter l2DataProtocol -defaultValue {native} -validValues {native ethernetII ip} + + defineCommand registerParameter -command advancedTestParameter -type string -parameter ipProtocol -defaultValue udp + defineCommand registerParameter -command advancedTestParameter -type string -parameter ipFragment -defaultValue may -validValues {may dont} + defineCommand registerParameter -command advancedTestParameter -type string -parameter ipLastFragment -defaultValue last -validValues {last more} + defineCommand registerParameter -command advancedTestParameter -type integer -parameter ipFragmentOffset -defaultValue 0 + defineCommand registerParameter -command advancedTestParameter -type string -parameter ipDelay -defaultValue normalDelay -validValues {normalDelay lowDelay} + defineCommand registerParameter -command advancedTestParameter -type string -parameter ipThroughput -defaultValue normalThruput -validValues {normalThruput highThruput} + defineCommand registerParameter -command advancedTestParameter -type string -parameter ipReliability -defaultValue normalReliability -validValues {normalReliability highReliability} + defineCommand registerParameter -command advancedTestParameter -type integer -parameter ipIdentifier -defaultValue 0 + defineCommand registerParameter -command advancedTestParameter -type string -parameter ipCost -defaultValue normalCost -validValues {normalCost lowCost} + defineCommand registerParameter -command advancedTestParameter -type string -parameter ipOptions -defaultValue {} + defineCommand registerParameter -command advancedTestParameter -type integer -parameter ipTTL -defaultValue 10 + + defineCommand registerParameter -command advancedTestParameter -type integer -parameter ipV6HopLimit -defaultValue 255 + defineCommand registerParameter -command advancedTestParameter -type integer -parameter ipV6FlowLabel -defaultValue 0 + defineCommand registerParameter -command advancedTestParameter -type integer -parameter ipV6TrafficClass -defaultValue 3 + + defineCommand registerParameter -command advancedTestParameter -type integer -parameter udpSourcePort -defaultValue 7 + defineCommand registerParameter -command advancedTestParameter -type integer -parameter udpDestPort -defaultValue 7 + + defineCommand registerParameter -command advancedTestParameter -type integer -parameter tcpSourcePort -defaultValue 0 + defineCommand registerParameter -command advancedTestParameter -type integer -parameter tcpDestPort -defaultValue 0 + + defineCommand registerParameter -command advancedTestParameter -type string -parameter igmpReportMode -defaultValue igmpReportToAllWhenQueried -validValues {igmpReportToOneWhenQueried igmpReportToAllWhenQueried igmpReportToAllUnsolicited} + + defineCommand registerParameter -command advancedTestParameter -type string -parameter ipxPacketType -defaultValue typeIpx + defineCommand registerParameter -command advancedTestParameter -type string -parameter vlanCFI -defaultValue resetCFI -validValues {resetCFI setCFI} + defineCommand registerParameter -command advancedTestParameter -type string -parameter removePortOnLinkDown -defaultValue false + defineCommand registerParameter -command advancedTestParameter -type integer -parameter linkStateTimeout -defaultValue 25 + defineCommand registerParameter -command advancedTestParameter -type string -parameter savedTerminationOption -defaultValue return -validValues {return exit} + defineCommand registerParameter -command advancedTestParameter -type double -parameter percentLossFormat -defaultValue 7.3 + defineCommand registerParameter -command advancedTestParameter -type double -parameter defaultFloatFormat -defaultValue 12.3 + defineCommand registerParameter -command advancedTestParameter -type boolean -parameter verifyAllArpReply -defaultValue false -validValues {true false} + defineCommand registerParameter -command advancedTestParameter -type boolean -parameter useRxVlanId -defaultValue true + defineCommand registerParameter -command advancedTestParameter -type integer -parameter dutDelay -defaultValue 100 + defineCommand registerParameter -command advancedTestParameter -type integer -parameter numAddressesPerPort -defaultValue 1 + defineCommand registerParameter -command advancedTestParameter -type integer -parameter octetToIncr -defaultValue 4 + defineCommand registerParameter -command advancedTestParameter -type boolean -parameter closeAllFilesInCleanUp -defaultValue false + defineCommand registerParameter -command advancedTestParameter -type boolean -parameter removeStreamsAtCompletion -defaultValue false + defineCommand registerParameter -command advancedTestParameter -type boolean -parameter stopProcessesOnEarlyTermination -defaultValue false + + defineCommand registerParameter -command advancedTestParameter -type boolean -parameter primeDut -defaultValue false + defineCommand registerParameter -command advancedTestParameter -type integer -parameter portWriteChunkSize -defaultValue 0 -validRange {0 1000} +} + + +################################## +# COMMAND: fastpath +################################## +proc initCommand_zz_fastpath {} \ +{ + defineCommand registerCommand fastpath + + # rate is in fps + defineCommand registerParameter -command fastpath -parameter rate -type integer -defaultValue 100 -validRange {1 2000000000} + defineCommand registerParameter -command fastpath -parameter numframes -type integer -defaultValue 10 -validRange {1 2000000000} + defineCommand registerParameter -command fastpath -parameter framesize -type integer -defaultValue 64 -validRange {12 2000000000} + defineCommand registerParameter -command fastpath -parameter enable -type boolean -defaultValue false + defineCommand registerParameter -command fastpath -parameter calculateLatency -type boolean -defaultValue no + defineCommand registerParameter -command fastpath -parameter waitTime -type integer -defaultValue 2000 -validRange {1 2000000000} +} + + +################################## +# COMMAND: ipfastpath +################################## +proc initCommand_zz_ipfastpath {} \ +{ + defineCommand registerCommand ipfastpath + + # rate is in fps + defineCommand registerParameter -command ipfastpath -parameter rate -type integer -defaultValue 100 -validRange {1 2000000000} + defineCommand registerParameter -command ipfastpath -parameter numframes -type integer -defaultValue 10 -validRange {1 2000000000} + defineCommand registerParameter -command ipfastpath -parameter framesize -type integer -defaultValue 64 -validRange {12 2000000000} + defineCommand registerParameter -command ipfastpath -parameter enable -type boolean -defaultValue false + defineCommand registerParameter -command ipfastpath -parameter calculateLatency -type boolean -defaultValue no + defineCommand registerParameter -command ipfastpath -parameter waitTime -type integer -defaultValue 2000 -validRange {1 2000000000} +} + + +################################## +# COMMAND: learn +################################## +proc initCommand_zz_learn {} \ +{ + defineCommand registerCommand learn + + defineCommand registerParameter -command learn -parameter when -type string -defaultValue oncePerFramesize -validValues {never once oncePerTest oncePerFramesize onIteration onTrial} + defineCommand registerParameter -command learn -parameter rate -type integer -defaultValue 100 -validRange {1 2000000000};# fps + defineCommand registerParameter -command learn -parameter numframes -type integer -defaultValue 10 -validRange {1 2000000000} + defineCommand registerParameter -command learn -parameter retries -type integer -defaultValue 20 + defineCommand registerParameter -command learn -parameter type -type string -defaultValue default -validValues {default mac ip ipx ipV6} + defineCommand registerParameter -command learn -parameter numDHCPframes -type integer -defaultValue 1 + defineCommand registerParameter -command learn -parameter framesize -type integer -defaultValue 64 -validRange {64 2000000000} + defineCommand registerParameter -command learn -parameter removeOnError -type boolean -defaultValue false + defineCommand registerParameter -command learn -parameter snoopConfig -type boolean -defaultValue false + defineCommand registerParameter -command learn -parameter duration -type integer -defaultValue 0 + defineCommand registerParameter -command learn -parameter waitTime -type integer -defaultValue 1000 -validRange {1 2000000000} + defineCommand registerParameter -command learn -parameter dhcpWaitTime -type integer -defaultValue 2 -validRange {1 200} ;# seconds + + # this command has been obsoleted, just around for backwards compatibility + defineCommand registerParameter -command learn -parameter errorAction -type string -defaultValue deprecated -validValues {deprecated continue remove} +} + + +################################## +# COMMAND: logger +# General logger parameters +################################## +proc initCommand_zz_logger {} \ +{ + global LOGS_DIR + + defineCommand registerCommand logger + + defineCommand registerParameter -command logger -parameter fileBackup -type boolean -defaultValue false + defineCommand registerParameter -command logger -parameter logFileName -type string -defaultValue "defaultFile.log" + defineCommand registerParameter -command logger -parameter directory -type string -defaultValue $LOGS_DIR + defineCommand registerParameter -command logger -parameter startTime -type string + defineCommand registerParameter -command logger -parameter endTime -type string + defineCommand registerParameter -command logger -parameter fileID -type string -defaultValue stdout -access r + defineCommand registerParameter -command logger -parameter ioHandle -type string -defaultValue stdout + + defineCommand registerMethod logger on + defineCommand registerMethod logger off + defineCommand registerMethod logger message priority +} + + +################################## +# COMMAND: map +# Traffic mapping configuration commands +################################## +proc initCommand_zz_map {} \ +{ + defineCommand registerCommand map + + defineCommand registerParameter -command map -parameter echo -type boolean -defaultValue false + defineCommand registerParameter -command map -parameter type -type string -defaultValue one2one -validValues {one2one one2many many2one many2many} + + defineCommand registerMethod map new {type} + defineCommand registerMethod map add {txChassis txLm txPort rxChassis rxLm rxPort} + defineCommand registerMethod map del {txChassis txLm txPort rxChassis rxLm rxPort} +} + + +################################## +# COMMAND: tclClient +################################## +proc initCommand_zz_tclClient {} \ +{ + defineCommand registerCommand tclClient + + defineCommand registerParameter -command tclClient -parameter enableStdout -type boolean -defaultValue false + defineCommand registerParameter -command tclClient -parameter enableResults -type boolean -defaultValue true +} + + +################################## +# COMMAND: advancedTestParameter +################################## +proc initCommand_zz_testProfile {} \ +{ + defineCommand registerCommand testProfile + + defineCommand registerParameter -command testProfile -type string -parameter chassisChain -defaultValue {loopback} + defineCommand registerParameter -command testProfile -type string -parameter serverName -defaultValue "" + defineCommand registerParameter -command testProfile -type string -parameter chassisID -defaultValue {1} + defineCommand registerParameter -command testProfile -type string -parameter syncCableLength -defaultValue {cable3feet} + defineCommand registerParameter -command testProfile -type string -parameter chassisSequence -defaultValue {1} + defineCommand registerParameter -command testProfile -type string -parameter timeSource -defaultValue {tsInternal} + defineCommand registerParameter -command testProfile -type string -parameter sntpAddress -defaultValue "" +} + + diff --git a/dep/tclclient/ixTcl1.0/Generic/interfaceTableUtils.tcl b/dep/tclclient/ixTcl1.0/Generic/interfaceTableUtils.tcl new file mode 100644 index 00000000..ecc959d5 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/interfaceTableUtils.tcl @@ -0,0 +1,850 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: interfaceTableUtils.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 2002/07/09 +# +# Description: Utilities for maintaining the Inteface Table. +# +################################################################################## + +namespace eval interfaceTable { +} + +######################################################################## +# Procedure: setDefault +# +# Arguments: None +# +# Returns: TCL_OK +# +######################################################################## +proc interfaceTable::setDefault {PortArray} \ +{ + upvar $PortArray portArray + + interfaceIpV6 setDefault + interfaceIpV4 setDefault + interfaceEntry setDefault + + foreach portMap [getAllPorts portArray] { + scan $portMap "%d %d %d" c l p + interfaceTable select $c $l $p + interfaceTable clearAllInterfaces + interfaceEntry clearAllItems $::addressTypeIpV4 + interfaceEntry clearAllItems $::addressTypeIpV6 + } + + return $::TCL_OK +} + +######################################################################## +# Procedure: configurePort +# +# Description: Given a port, configure the interface table +# with information stored in the port, ip and ipV6 objects. +# +# Arguments(s): chassis +# card +# port +# protocolList: list of protocols to configure (default is all) +# write: write or noWrite (default) +# +# Return: TCL_OK or TCL_ERROR +# +######################################################################## +proc interfaceTable::configurePort "chassis card port {protocolList {$::ipV4 $::ipV6}} {numInterfaces 1} {write nowrite} {reset true}" \ +{ + set portList [list [list $chassis $card $port]] + set retCode [interfaceTable::configure portList $protocolList $numInterfaces $write $reset] + + return $retCode +} + + +######################################################################## +# Procedure: configure +# +# Description: Given a list of ports, configure the interface table +# with information stored in the port, ip and ipV6 objects. +# +# Arguments(s): portList list of ports to configure +# protocolList: list of protocols to configure (default is all) +# write: write or noWrite (default) +# +# Return: TCL_OK or TCL_ERROR +# +# NOTE: This currently only builds one ipV6 table entry per port, however +# in future implementations it needs the capability to do multiple +# interfaces per port. +# +######################################################################## +proc interfaceTable::configure "PortList {protocolList {$::ipV4 $::ipV6}} {numInterfaces 1} {write nowrite} {reset true}" \ +{ + set retCode $::TCL_OK + + upvar $PortList portList + + if {$reset == "true"} { + setDefault portList + } + + foreach port $portList { + + scan $port "%d %d %d" c l p + if {[port get $c $l $p]} { + errorMsg "Error: Unable to get port: $c $l $p" + set retCode $::TCL_ERROR + break + } + + if {![interfaceTable select $c $l $p]} { + if {[addEntry $c $l $p $protocolList $numInterfaces]} { + set retCode $::TCL_ERROR + break + } + + } else { + set retCode $::TCL_ERROR + break + } + } + + if {$retCode == $::TCL_OK} { + set lowerWrite [string tolower $write] + if {[stringCompare $lowerWrite "write"] == 0} { + set retCode [writeConfigToHardware portList] + } + } + + return $retCode +} + + +######################################################################## +# Procedure: addEntry +# +# Description: Build and save an entry into the interface table with +# information stored in the port, ip and ipV6 objects. +# +# Arguments(s): chassis +# card +# port +# protocolList: list of protocols to configure +# +# Return: TCL_OK or TCL_ERROR +# +######################################################################## +proc interfaceTable::addEntry "chassis card port {protocolList {$::ipV4 $::ipV6}} {numInterfaces 1} " \ +{ + eval array set protocolFunctions \{ \ + $::ipV4 interfaceTable::updateItemIpV4 \ + $::ipV6 interfaceTable::updateItemIpV6 \ + \} + + set retCode $::TCL_OK + + # If the interface already exists, then update it. + if {![interfaceTable getInterface [interfaceTable::formatEntryDescription $chassis $card $port]]} { + set retCode [updateEntry $chassis $card $port $protocolList] + + # Else, add new interface entry. + } else { + + interfaceEntry setDefault + + # Build IPv4/IPv6 Entries. + set message "" + + set sourceIpAddr [ip cget -sourceIpAddr] + + # Build & Save General Entry. + + set portMacAddress [port cget -MacAddress] + interfaceEntry config -description [formatEntryDescription $chassis $card $port] + interfaceEntry config -macAddress [port cget -MacAddress] + interfaceEntry config -enable $::true + + for {set interfaceCount 0 } { $interfaceCount < $numInterfaces} { incr interfaceCount} { + + if {[vlanUtils::isPortTagged $chassis $card $port]} { + if {[protocol cget -enable802dot1qTag]} { + if {![vlan get $chassis $card $port]} { + interfaceEntry config -enableVlan $::true + interfaceEntry config -vlanId [vlan cget -vlanID] + } else { + errorMsg "Error getting vlan parameters for $chassis $card $port" + set retCode $::TCL_ERROR + } + } + } + foreach protocol $protocolList { + if {[info exists protocolFunctions($protocol)]} { + set retCode [eval $protocolFunctions($protocol) $chassis $card $port message $interfaceCount] + } + } + + if {[interfaceTable addInterface ]} { + errorMsg "Error: Unable to add interface to Interface Table for port: $chassis $card $port." + set retCode $::TCL_ERROR + } + + # Don't change the following order, otherwise the configuratin will be lost + interfaceEntry config -description [formatEntryDescription $chassis $card $port] + interfaceEntry config -enable $::true + incrMacAddress portMacAddress 1 + interfaceEntry config -macAddress $portMacAddress + set sourceIpAddr [incrIpField $sourceIpAddr 4] + ip config -sourceIpAddr $sourceIpAddr + } + + interfaceEntry clearAllItems $::addressTypeIpV4 + interfaceEntry clearAllItems $::addressTypeIpV6 + } + + return $retCode +} + + + +######################################################################## +# Procedure: updateEntry +# +# Description: Modify an existing entry and re-save (actually, delete and +# re-add) the entry. +# +# In it's current state, this method doesn't handle multiple +# entries per interface. +# +# Arguments(s): chassis +# card +# port +# protocolList: list of protocols to configure +# +# Return: TCL_OK or TCL_ERROR +# +######################################################################## +proc interfaceTable::updateEntry "chassis card port {protocolList {$::ipV4 $::ipV6}}" \ +{ + eval array set protocolFunctions \{ \ + $::ipV4 interfaceTable::updateItemIpV4 \ + $::ipV6 interfaceTable::updateItemIpV6 \ + \} + + set retCode $::TCL_ERROR + + foreach protocol $protocolList { + if {$protocol == $::ipV4} { + set addressType $::addressTypeIpV4 + if {![interfaceEntry getFirstItem $addressType]} { + interfaceEntry delItem $addressType [interfaceIpV6 cget -ipAddress] + } + } else { + set addressType $::addressTypeIpV6 + if {![interfaceEntry getFirstItem $addressType]} { + interfaceEntry delItem $addressType [interfaceIpV6 cget -ipAddress] + } + } + set retCode [eval $protocolFunctions($protocol) $chassis $card $port message] + } + + if {$retCode == $::TCL_OK} { + if {![interfaceTable delInterface]} { + if {[interfaceTable addInterface]} { + set retCode [interfaceTable write] + } + } + } + + return $retCode +} + +######################################################################## +# Procedure: updateIpItemV4 +# +# Description: Updates the IPv4 entry of the interface table. +# +# Arguments(s): chassis +# card +# port + +# Return: TCL_OK or TCL_ERROR +# +######################################################################## +proc interfaceTable::updateItemIpV4 {chassis card port Message {incrByte 0}} \ +{ + set retCode $::TCL_OK + + upvar $Message message + set message "" + + if {![ip get $chassis $card $port]} { + interfaceIpV4 setDefault + set sourceIpAddr [ip cget -sourceIpAddr] + interfaceIpV4 config -ipAddress [incrIpField $sourceIpAddr 4 $incrByte] + interfaceIpV4 config -gatewayIpAddress [ip cget -destDutIpAddr] + interfaceIpV4 config -maskWidth [getIpV4MaskWidth [ip cget -sourceIpMask]] + + if {[interfaceEntry addItem $::addressTypeIpV4]} { + set retCode $::TCL_ERROR + set message "Error: Unable to add IPv4 Item for port $chassis $card $port" + } + + } else { + set retCode $::TCL_ERROR + set message "Error: Unable to get IP for port $chassis $card $port" + } + + + return $retCode +} + +######################################################################## +# Procedure: updateIpItemV6 +# +# Description: Updates the IPv6 entry of the interface table. +# +# Arguments(s): chassis +# card +# port +# +# Return: TCL_OK or TCL_ERROR +# +######################################################################## +proc interfaceTable::updateItemIpV6 {chassis card port Message {numInterfaces 1} } \ +{ + set retCode $::TCL_OK + + upvar $Message message + set message "" + + if {![ipV6 get $chassis $card $port]} { + + interfaceIpV6 setDefault + interfaceIpV6 config -maskWidth [ipV6 cget -sourceMask] + interfaceIpV6 config -ipAddress [ipV6 cget -sourceAddr] + + if {[interfaceEntry addItem $::addressTypeIpV6]} { + set retCode $::TCL_ERROR + set message "Error: Unable to add IPv6 Item for port $chassis $card $port" + } + + } else { + set retCode $::TCL_ERROR + set message "Error: Unable to get IPv6 for port $chassis $card $port" + } + + return $retCode +} + + +######################################################################## +# Procedure: addEntries +# +# Description: Build and save an entries into the interface table with +# information stored in the port, ip V4, and vlan objects. +# +# Arguments(s): chassis +# card +# port +# protocolList: list of protocols to configure +# numInterfaces not used. It is only for compatibility with addEntry proc +# +# Return: TCL_OK or TCL_ERROR +# +######################################################################## +proc interfaceTable::addMultipleEntry "chassis card port {protocolList {$::ipV4}} {numInterfaces 1} " \ +{ + eval array set protocolFunctions \{ \ + $::ipV4 interfaceTable::addItemIpV4 \ + \} + + set retCode $::TCL_OK + + interfaceEntry setDefault + + # Build IPv4/IPv6 Entries. + set message "" + + set sourceIpAddr [ip cget -sourceIpAddr] + set destDutIpAddr [ip cget -destDutIpAddr] + set octetToIncr [advancedTestParameter cget -octetToIncr] + set incrGateway no + + if {[vlanUtils::isPortTagged $chassis $card $port] && [protocol cget -enable802dot1qTag]} { + set vlanSupport 1 + if {![vlan get $chassis $card $port]} { + set vlanId [vlan cget -vlanID] + set numInterfaces [vlan cget -repeat] + set incrGateway yes + } else { + errorMsg "Error getting vlan parameters for $chassis $card $port" + set retCode $::TCL_ERROR + } + } else { + set vlanSupport 0 + set numInterfaces 1 + } + + # Build & Save General Entry. + set portMacAddress [port cget -MacAddress] + interfaceEntry config -description [formatEntryDescription $chassis $card $port] + interfaceEntry config -macAddress [port cget -MacAddress] + interfaceEntry config -enable $::true + + for {set interfaceCount 0 } { $interfaceCount < $numInterfaces} { incr interfaceCount} { + + if {$vlanSupport} { + interfaceEntry config -enableVlan $::true + interfaceEntry config -vlanId $vlanId + incr vlanId + } + foreach protocol $protocolList { + if {[info exists protocolFunctions($protocol)]} { + set retCode [eval $protocolFunctions($protocol) $chassis $card $port message \ + $octetToIncr $interfaceCount $incrGateway] + } + } + + if {[interfaceTable addInterface ]} { + errorMsg "Error: Unable to add interface to Interface Table for port: $chassis $card $port." + set retCode $::TCL_ERROR + } + + # Don't change the following order, otherwise the configuratin will be lost + interfaceEntry config -description [formatEntryDescription $chassis $card $port] + interfaceEntry config -enable $::true + incrMacAddress portMacAddress 1 + interfaceEntry config -macAddress $portMacAddress + } + + interfaceEntry clearAllItems $::addressTypeIpV4 + + return $retCode +} + + +######################################################################## +# Procedure: interfaceTable +# +# Description: Create new IPv4 entry of the interface table. The IP source and gateway +# address is based on the sourceIpAddr and destDutIpAddr fields of the ip +# command with the adjustments specified by "octetToIncr" and "incrValue" +# parameters. +# +# Arguments(s): chassis +# card +# port +# octetToIncr - octet of the IP address to increment +# incrValue - the number to increment +# +# Return: TCL_OK or TCL_ERROR +# +######################################################################## +proc interfaceTable::addItemIpV4 {chassis card port Message {octetToIncr 3} {incrValue 0} {incrGateway no}}\ +{ + set retCode $::TCL_OK + + upvar $Message message + set message "" + + if {![ip get $chassis $card $port]} { + interfaceIpV4 setDefault + set sourceIpAddr [ip cget -sourceIpAddr] + interfaceIpV4 config -ipAddress [incrIpField $sourceIpAddr $octetToIncr $incrValue] + set destDutIpAddr [ip cget -destDutIpAddr] + if {$incrGateway == "yes"} { + interfaceIpV4 config -gatewayIpAddress [incrIpField $destDutIpAddr $octetToIncr $incrValue] + } else { + interfaceIpV4 config -gatewayIpAddress $destDutIpAddr + } + interfaceIpV4 config -maskWidth [getIpV4MaskWidth [ip cget -sourceIpMask]] + + if {[interfaceEntry addItem $::addressTypeIpV4]} { + set retCode $::TCL_ERROR + set message "Error: Unable to add IPv4 Item for port $chassis $card $port" + } + + } else { + set retCode $::TCL_ERROR + set message "Error: Unable to get IP for port $chassis $card $port" + } + + return $retCode +} + + +######################################################################## +# Procedure: formatEntryDescription +# +# Description: TBD. +# +# Currently, this procedure builds a description in the format +# of "card:port". This will need to change later when +# multiple interfaces are allowed per port. +# +# Arguments(s): chassis +# card +# port +# identifier: true/false: Prepend count id to description. +# +# Return: Entry Description +# +######################################################################## +proc interfaceTable::formatEntryDescription {chassis card port {identifier "false"}} \ +{ + set retValue {} + + if {$identifier == "false"} { + set retValue [format "%02d:%02d" $card $port] +# set retValue [format "%02d:%02d ProtocolInterface" $card $port] + } else { + set id [getInterfaceCount $chassis $card $port] + set retValue [format "%d-%02d:%02d ProtocolInterface" $id $card $port] + } + + return "$retValue" +} + +######################################################################## +# Procedure: getInterfaceId +# +# Description: Given a port # and a MAC address, return the interface id. +# defined for that port. +# +# Arguments(s): chassis +# card +# port +# ipAddress: Ip address associated with port interface, +# if null, the id of the first interface is +# returned. +# +# Return: interface id +# +######################################################################## +proc interfaceTable::getInterfaceId {chassis card port {macAddress ""}} \ +{ + set retValue 0 + + if {![interfaceTable select $chassis $card $port]} { + + if {[string length $macAddress] == 0} { + if {![interfaceTable getFirstInterface]} { + set retValue [interfaceEntry cget -description] + } + + } else { + if {![interfaceTable getFirstInterface]} { + set interfaceMacAddress [interfaceEntry cget -macAddress] + if {[stringCompare $macAddress $interfaceMacAddress] == 0} { + set retValue [interfaceEntry cget -description] + } else { + while {![interfaceTable getNextInterface]} { + set interfaceMacAddress [interfaceEntry cget -macAddress] + if {[stringCompare $macAddress $interfaceMacAddress] == 0} { + set retValue [interfaceEntry cget -description] + break + } + } + } + } + } + } + + return $retValue +} + + +######################################################################## +# Procedure: getInterfaceCount +# +# Description: Given a port number, return the number of interfaces +# defined for that port. +# +# Arguments(s): chassis +# card +# port +# +# Return: # of interfaces +# +######################################################################## +proc interfaceTable::getInterfaceCount {chassis card port} \ +{ + set retValue 0 + + if {![interfaceTable select $chassis $card $port]} { + if {![interfaceTable getFirstInterface]} { + incr retValue + while {![interfaceTable getNextInterface]} { + incr retValue + } + } + } + return $retValue +} + +######################################################################## +# Procedure: enableInterface +# +# Description: Given a port number and interface Id, enable the interface. +# +# Arguments(s): chassis +# card +# port +# interfaceId: See format returned by [formatEntryDescription] +# +# Return: ::TCL_OK or ::TCL_ERROR +# +######################################################################## +proc interfaceTable::enableInterface {chassis card port interfaceId} \ +{ + set retCode $::TCL_ERROR + + if {![interfaceTable select $chassis $card $port]} { + if {![interfaceTable getInterface $interfaceId]} { + interfaceEntry config -enable true + interfaceTable write + set retCode $::TCL_OK + } + } + return $retCode +} + +######################################################################## +# Procedure: disableInterface +# +# Description: Given a port number and interface Id, disable the interface. +# +# Arguments(s): chassis +# card +# port +# interfaceId: See format returned by [formatEntryDescription] +# +# Return: ::TCL_OK or ::TCL_ERROR +# +######################################################################## +proc interfaceTable::disableInterface {chassis card port interfaceId} \ +{ + set retCode $::TCL_ERROR + + if {![interfaceTable select $chassis $card $port]} { + if {![interfaceTable getInterface $interfaceId]} { + interfaceEntry config -enable false + interfaceTable write + set retCode $::TCL_OK + } + } + return $retCode +} + +######################################################################## +# Procedure: disableAllInterfaces +# +# Description: Given a port number, disable the interfaces on that port. +# +# Arguments(s): chassis +# card +# port +# +# Return: ::TCL_OK or ::TCL_ERROR +# +######################################################################## +proc interfaceTable::disableAllInterfaces {chassis card port} \ +{ + set retCode $::TCL_ERROR + + if {![interfaceTable select $chassis $card $port]} { + if {![interfaceTable getFirstInterface]} { + interfaceEntry config -enable false + set retCode $::TCL_OK + while {![interfaceTable getNextInterface]} { + interfaceEntry config -enable false + } + interfaceTable write + } + } + return $retCode +} + +######################################################################## +# Procedure: deleteInterface +# +# Description: Given a port number and interface Id, disable the interface. +# +# Arguments(s): chassis +# card +# port +# interfaceId: See format returned by [formatEntryDescription] +# +# Return: ::TCL_OK or ::TCL_ERROR +# +######################################################################## +proc interfaceTable::deleteInterface {chassis card port interfaceId} \ +{ + set retCode $::TCL_ERROR + + if {![interfaceTable select $chassis $card $port]} { + if {![interfaceTable getInterface $interfaceId]} { + interfaceTable delInterface + interfaceTable write + set retCode $::TCL_OK + } + } + return $retCode +} + +######################################################################## +# Procedure: getEntryList +# +# Description: Given a port number and interface Id return a list +# of all entries in that interface. Really just a debugging +# tool that I left in because I thought if might be useful. +# +# Arguments(s): chassis +# card +# port +# interfaceId: See format returned by [formatEntryDescription] +# typeList: list of address types desired, default is all: +# addressTypeIpV4, addressTypeIpV6 +# +# Return: ::TCL_OK or ::TCL_ERROR +# +######################################################################## +proc interfaceTable::getEntryList "chassis card port interfaceId {typeList {$::addressTypeIpV4 $::addressTypeIpV6}}" \ +{ + set interfaceList [list] + + if {![interfaceTable select $chassis $card $port]} { + + if {![interfaceTable getInterface $interfaceId]} { + + foreach typeId $typeList { + if {![interfaceEntry getFirstItem $typeId]} { + + switch $typeId " + $::addressTypeIpV4 { + set command interfaceIpV4 + } + $::addressTypeIpV6 { + set command interfaceIpV6 + } + " + lappend interfaceList [eval $command cget -ipAddress] + while {![interfaceEntry getNextItem $typeId]} { + lappend interfaceList [eval $command cget -ipAddress] + } + } + } + } + } + + return $interfaceList +} + + +######################################################################## +# Procedure: getGatewayList +# +# Description: Given a port number, this proc returns a list of all gateway +# entries in that port. +# Arguments(s): chassis +# card +# port +# typeList: list of address types desired, default is all: +# addressTypeIpV4, addressTypeIpV6 +# +# Return: ::TCL_OK or ::TCL_ERROR +# +######################################################################## +proc interfaceTable::getGatewayArray "GatewayArray portList {typeList {$::addressTypeIpV4 $::addressTypeIpV6}}" \ +{ + upvar $GatewayArray gatewayArray + + foreach txMap $portList { + scan $txMap "%d %d %d" c l p + set gatewayArray($c,$l,$p) [list] + + if {![interfaceTable select $c $l $p]} { + + if {![interfaceTable getFirstInterface]} { + + foreach typeId $typeList { + if {![interfaceEntry getFirstItem $typeId]} { + + switch $typeId " + $::addressTypeIpV4 { + set command interfaceIpV4 + } + $::addressTypeIpV6 { + set command interfaceIpV6 + } + " + lappend gatewayArray($c,$l,$p) [eval $command cget -gatewayIpAddress] + + while {![interfaceEntry getNextItem $typeId]} { + lappend gatewayArray($c,$l,$p) [eval $command cget -gatewayIpAddress] + } + } + } + } + + while {![interfaceTable getNextInterface]} { + foreach typeId $typeList { + if {![interfaceEntry getFirstItem $typeId]} { + + switch $typeId " + $::addressTypeIpV4 { + set command interfaceIpV4 + } + $::addressTypeIpV6 { + set command interfaceIpV6 + } + " + lappend gatewayArray($c,$l,$p) [eval $command cget -gatewayIpAddress] + while {![interfaceEntry getNextItem $typeId]} { + lappend gatewayArray($c,$l,$p) [eval $command cget -gatewayIpAddress] + } + } + } + } + } + } + +} + + + diff --git a/dep/tclclient/ixTcl1.0/Generic/ipV6Utils.tcl b/dep/tclclient/ixTcl1.0/Generic/ipV6Utils.tcl new file mode 100644 index 00000000..50e1d0d4 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/ipV6Utils.tcl @@ -0,0 +1,1479 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: ipV6Utils.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Description: This file contains utilities for manipulating IPv6 addresses. +# +# Revision Log: +# Date Author Comments +# ----------- ------------------- -------------------------------------------- +# 2002/05/16 D. Heins-Gelder Initial release +# +################################################################################## + +# Procedure List +# +# convertAddress +# convertIpToIpV6 +# convertIpV6ToIp +# convertIpV6ToMac +# convertMacToIpV6 +# convertNoop +# expandAddress +# getAddressFieldOffset +# getAddressFields +# getFieldListByPrefix +# getFieldMask +# getFieldNamesByPrefix +# getFormatPrefix +# getHeaderLength +# getInterfaceId +# getLoopbackAddress +# getMinimumValidFramesize +# getNextLevelAggregateId +# getSiteLevelAggregateId +# getSubnetId +# getTopLevelAggregateId +# host2addr +# incrIpField +# isMixedVersionAddress +# isReservedMCAddress +# isValidAddress +# validateAddress + +namespace eval ipv6 { + + variable ipV6AddressSize 128 + variable ipV4AddressSize 32 + variable macAddressSize 48 + + # IPv6 Addresses can be mixed with Ipv4 address as: 66:66:66:66:66:66:444.444.444.444 + # In this case the first 6 segements are the hex V6 address, the lower 4 segment are + # decimal V4 address (traditional format). For example: + # + # fffe:0000:0a3d:0001:0dce:1234:192.168.10.1 + + # Known IPv6 addresses. + variable addressUnspecified ::0 + variable addressLoopback ::1 + variable addressTest 03ff:e0::00 + variable addressUnicastLinkLocal fe80::00 + variable addressUnicastSiteLocal fec0::00 + variable addressIsatap 00005efe + + # + # Multicast addresses + # + variable addressMulticast ff::00 + variable addressMulticastAllNodes [list \ + ff:01::01 \ + ff:02::01 ] + + variable addressMulticastAllRouters [list ff:01::02 \ + ff:02::02 \ + ff:05::02] + # Well known multicast addresses + variable reservedMCAddressList + set reservedMCAddressList [list \ + ff01:0000:0000:0000:0000:0000:0000:0001 \ + ff01:0000:0000:0000:0000:0000:0000:0002 \ + ff02:0000:0000:0000:0000:0000:0000:0001 \ + ff02:0000:0000:0000:0000:0000:0000:0002 \ + ff02:0000:0000:0000:0000:0000:0000:0003 \ + ff02:0000:0000:0000:0000:0000:0000:0004 \ + ff02:0000:0000:0000:0000:0000:0000:0005 \ + ff02:0000:0000:0000:0000:0000:0000:0006 \ + ff02:0000:0000:0000:0000:0000:0000:0007 \ + ff02:0000:0000:0000:0000:0000:0000:0008 \ + ff02:0000:0000:0000:0000:0000:0000:0009 \ + ff02:0000:0000:0000:0000:0000:0000:000a \ + ff02:0000:0000:0000:0000:0000:0000:000b \ + ff02:0000:0000:0000:0000:0000:0000:000c \ + ff02:0000:0000:0000:0000:0000:0000:000d \ + ff02:0000:0000:0000:0000:0000:0000:000e \ + ff02:0000:0000:0000:0000:0000:0001:0001 \ + ff02:0000:0000:0000:0000:0000:0001:0002 \ + ff05:0000:0000:0000:0000:0000:0000:0002 \ + ff05:0000:0000:0000:0000:0000:0001:0003 \ + ff05:0000:0000:0000:0000:0000:0001:0004 ] + # The reservedMCAddressList also contains addresses in the following range + # ff02:0000:0000:0000:0000:0001:FFXX:XXXX where X is a place holder for a variable scope value + # ff05:0000:0000:0000:0000:0000:0001:1000 to ff05:0000:0000:0000:0000:0000:0001:13FF + + variable fieldNames {topLevelAggregationId nextLevelAggregationId siteLevelAggregationId subnetId interfaceId} + + variable fieldListByPrefix + eval array set fieldListByPrefix \{ \ + $::ipV6Reserved \{interfaceId\} \ + $::ipV6NSAPAllocation \{interfaceId\} \ + $::ipV6IPXAllocation \{interfaceId\} \ + $::ipV6GlobalUnicast \{topLevelAggregationId reserved nextLevelAggregationId siteLevelAggregationId interfaceId\} \ + $::ipV6LinkLocalUnicast \{interfaceId\} \ + $::ipV6SiteLocalUnicast \{subnetId interfaceId \} \ + $::ipV6UserDefined \{interfaceId topLevelAggregationId nextLevelAggregationId siteLevelAggregationId subnetId\} \ + \} + + variable fieldNamesByPrefix + eval array set fieldNamesByPrefix \{ \ + $::ipV6Reserved \{\"Interface Id\"\} \ + $::ipV6NSAPAllocation \{\"Interface Id\"\} \ + $::ipV6IPXAllocation \{\"Interface Id\"\} \ + $::ipV6GlobalUnicast \{\"Interface Id\" \"Top-Level Aggregation Id\" \"Next-Level Aggregation Id\" \"Site-Level Aggregation Id\"\} \ + $::ipV6SiteLocalUnicast \{\"Interface Id\" \"Subnet Id\"\} \ + $::ipV6LinkLocalUnicast \{\"Interface Id\"\} \ + $::ipV6UserDefined \{\"Interface Id\" \"Top-Level Aggregation Id\" \"Next-Level Aggregation Id\" \"Site-Level Aggregation Id\" \"Subnet Id\"\} \ + \} + + + + variable fieldPositions + array set fieldPositions { + prefix 4 + topLevelAggregationId 16 + nextLevelAggregationId 48 + siteLevelAggregationId 64 + subnetId 64 + interfaceId 128 + groupId 128 + } + + variable fieldOffsets + array set fieldOffsets { + prefix 0 + topLevelAggregationId 0 + nextLevelAggregationId 3 + siteLevelAggregationId 6 + subnetId 6 + interfaceId 8 + } + + variable fieldMasks + array set fieldMasks { + prefix 0xE0000000000000000000000000000000 + topLevelAggregationId 0x1FFF0000000000000000000000000000 + nextLevelAggregationId 0x000000FFFFFF00000000000000000000 + siteLevelAggregationId 0x000000000000FFFF0000000000000000 + subnetId 0x000000000000FFFF0000000000000000 + interfaceId 0x0000000000000000FFFFFFFFFFFFFFFF + groupId 0xFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFF + } + +} + +######################################################################################## +# +# Conversion Utilities +# +######################################################################################## + +######################################################################################## +# +# Procedure: ipv6::host2addr +# +# Description: Given an IPv6 address, expand it, then return a string of hex +# characters (similar function to host2addr for IPv4 in utils.tcl) +# +# Argument(s): address: Ipv6 address (any acceptable IPv6 format) +# +# Returns: hex byte string, for example: +# ffe1::1 becomes +# ff e1 00 00 00 00 00 00 00 00 00 00 00 00 00 01 +# +# ffe1:ffff:eeee:dddd:cccc:bbbb:aaaa:0001 becomes +# ff e1 ff ff ee ee dd dd cc cc bb bb aa aa 00 01 +# +######################################################################################## +proc ipv6::host2addr {address} \ +{ + variable ipV6AddressSize + set bytes {} + + if {[isValidAddress $address] || \ + [isMixedVersionAddress $address] } { + + set address [expandAddress $address] + + set length [expr $ipV6AddressSize / 8] + regsub -all ":" $address {} address + for {set i 0} {$i < $length} {incr i} { + lappend bytes [string range $address 0 1] + set address [string replace $address 0 1] + } + } + + return $bytes +} + +######################################################################################## +# +# Procedure: ipv6::expandAddress +# +# Description: Expand and IPv6 address from it's compressed form (RFC 2373, section +# 2.1 & 2.2) to a full 16 byte address delimited by colons: +# +# Handles the following: +# +# 1. Zeros compression operator :: +# ::1 becomes +# 0000:0000:0000:0000:0000:0000:0000:0001 +# +# fffe::1 becomes +# fffe:0000:0000:0000:0000:0000:0000:0001 +# +# 2. Mixed IPv4 and IPv6 Address: +# 0:0:0:0:0:0:192.168.10.1 becomes +# 0000:0000:0000:0000:0000:0000:C0a8:0a01 +# +# Argument(s): address: compressed ipv6 address +# +# Returns: expanded address +# +######################################################################################## +proc ipv6::expandAddress {address} \ +{ + variable ipV6AddressSize + variable ipV4AddressSize + + set retValue {} + set segments 8 + + if {[isValidAddress $address]} { + + # Convert IPv4 address to Hex. + if {[isMixedVersionAddress $address]} { + set end [expr [llength [split $address :]] - 1] + set ipv4Address [lindex [split $address :] $end] + regsub "$ipv4Address" $address {} address + regsub -all {(.*)\.(.*)\.(.*)\.(.*)} $ipv4Address \ + {[format "%02x%02x:%02x%02x" \1 \2 \3 \4]} ipv4Address + set ipv4Address [subst $ipv4Address] + append address $ipv4Address + } + + # Check for Zero Compression operator, if found split into before and after. + set segmentsBefore {} + set segmentsAfter $address + + regexp {(.*)::(.*)} $address result segmentsBefore segmentsAfter + + # + # Fill in the zeroes needed to expand. + set segmentsBefore [split $segmentsBefore :] + set segmentsAfter [split $segmentsAfter :] + set segmentsNeeded [expr $segments - ([llength $segmentsBefore] + [llength $segmentsAfter])] + set segmentList "$segmentsBefore [string repeat " 0" $segmentsNeeded] $segmentsAfter" + + # Build it back into a list as the expanded address in 8 segments (2 bytes each). + set expandedAddress [list] + foreach segment $segmentList { + lappend expandedAddress [format "%04x" 0x$segment] + } + set retValue [join $expandedAddress :] + } + + return $retValue +} + +######################################################################################## +# +# Procedure: ipv6::compressAddress +# +# Description: +# +# Argument(s): +# +# Returns: +# +######################################################################################## +proc ipv6::compressAddress { address } \ +{ + regsub -all {(:0{1,3})+} $address ":" stripZeros + regsub {(:0)+} $stripZeros ":" dc + if {[string index $dc end] == ":"} { + set num_colons [regsub -all {:} $dc " " dc_ignore] + if {$num_colons < 7} { + append dc :0 + } else { + append dc 0 + } + } + regsub {^(0{1,3})(.*):(.*)} $dc {\2:\3} dc + return $dc +} + + +######################################################################################## +# +# Procedure: ipv6::convertAddress +# +# Description: Converts from one type of address to another: +# mac -> ipv4 invalid +# mac -> ipv6 +# ipv4 -> ipv6 +# ipv4 -> mac invalid +# ipv6 -> mac (returns lower 6 bytes) +# ipv6 -> ipv4 (returns lower 4 bytes) +# +# Argument(s): address: mac or ip address (version 4 or 6) +# mac format: 00:00:00:00:00:00 (hex) +# ip format: 000.000.000.000 (decimal) +# ipv6 format: 0000:0000:0000:0000:0000:0000:0000:0000 +# +# sourceType: ip, ipV6, mac +# destType: ip, ipV6, mac +# args: prefix if destType = ipv6 (must be fully expanded prefix) +# +# Returns: converted address +# +######################################################################################## +proc ipv6::convertAddress {address sourceType destType {args ""}} \ +{ + set retValue {} + + array set conversion { + mac,ip ipv6::convertNoop + mac,ipV6 ipv6::convertMacToIpV6 + ip,mac ipv6::convertNoop + ip,ipV6 ipv6::convertIpToIpV6 + ip,isatap ipv6::convertIpToIsatap + ip,ipV4Compatible ipv6::convertIptoIpV4Compatible + ip,6to4 ipv6::convertIpTo6to4 + isatap,ip ipv6::convertIpV6ToIp + ipV4Compatible,ip ipv6::convertIpV6ToIp + 6to4,ip ipv6::convertIpV6ToIp + ipV6,mac ipv6::convertIpV6ToMac + ipV6,ip ipv6::convertIpV6ToIp + } + + if {[info exists conversion($sourceType,$destType)]} { + set command $conversion($sourceType,$destType) + if {$command != {}} { + set retValue [eval $command $address $args] + } + } + + return $retValue +} + +######################################################################################## +# +# Procedure: ipv6::convertMacToIpV6 +# +# Description: Converts a MAC address to an IPv6 address. +# +# Argument(s): address: mac address +# prefix: defaults to 0 +# +# Returns: converted address +# +######################################################################################## +proc ipv6::convertMacToIpV6 {address {prefix 0}} \ +{ + variable ipV6AddressSize + variable macAddressSize + + set retValue {} + if {[isMacAddressValid $address] == $::TCL_OK} { + + # Convert the address and prefix to a string of bytes. + regsub -all ":" $prefix { } prefixList + set prefix {} + foreach segment $prefixList { + lappend prefix [format "%04X" 0x$segment] + } + regsub -all " " $prefix "" prefix + regsub -all ":" $address {} address + + # Expand if necessary. + set prefixLength [expr [string length $prefix]/2] + set expand [expr $ipV6AddressSize/8 - $macAddressSize/8 - $prefixLength] + for {set i $expand} {$i} {incr i -1} { + append prefix "00" + } + append prefix $address + + # Build prefix-address string into IPv6 style address + set address {} + while {[string length $prefix] > 0} { + append address "[string range $prefix 0 3]:" + set prefix [string replace $prefix 0 3] + } + regexp {(.*):$} $address match address + set retValue $address + } + + return $retValue +} + +######################################################################################## +# +# Procedure: ipv6::convertIpToIpV6 +# +# Description: Converts an IP address to an IPv6 address. +# +# Argument(s): address: version 4 style IP address +# prefix: defaults to 0 +# +# Returns: converted address +# +######################################################################################## +proc ipv6::convertIpToIpV6 {address {prefix 0} {option addressAtTheEnd}} \ +{ + variable ipV4AddressSize + variable ipV6AddressSize + + set retValue {} + + if {[isIpAddressValid $address]} { + + # Convert prefix to string. + regsub -all ":" $prefix { } prefixList + set prefix {} + foreach segment $prefixList { + lappend prefix [format "%04X" "0x$segment"] + } + regsub -all " " $prefix "" prefix + + + # Convert ip address to string. + regsub -all {(.*)\.(.*)\.(.*)\.(.*)} $address \ + {[format "%02x%02x%02x%02x" \1 \2 \3 \4]} address + set address [subst $address] + + + # Expand if necessary. + set prefixLength [expr [string length $prefix]/2] + set expand [expr $ipV6AddressSize/8 - $ipV4AddressSize/8 - $prefixLength] + if {$option == "addressFollowPrefix"} { + append prefix $address + ### append the rest with 00...01 + ### because host address cannot be all 0's + incr expand -1 + for {set i $expand} {$i} {incr i -1} { + append prefix "00" + } + append prefix "01" + } else { + for {set i $expand} {$i} {incr i -1} { + append prefix "00" + } + append prefix $address + } + + # Break it up into IPv6 address seperated by colons. + set address {} + while {[string length $prefix] > 0} { + append address "[string range $prefix 0 3]:" + set prefix [string replace $prefix 0 3] + } + regexp {(.*):$} $address match address + set retValue $address + } + + return $retValue +} + + +######################################################################################## +# +# Procedure: ipv6::convertIpToIsatap +# +# Description: Converts an IP address to an IPv6 Isatap address. +# +# Argument(s): address: version 4 style IP address +# prefix: defaults to 0 +# +# Returns: converted address +# +######################################################################################## +proc ipv6::convertIpToIsatap {address {prefix 0}} \ +{ + variable ipV4AddressSize + variable ipV6AddressSize + variable addressIsatap + + set retValue {} + + if {[isIpAddressValid $address]} { + + # Convert prefix to string. + regsub -all ":" $prefix { } prefixList + set prefix {} + foreach segment $prefixList { + + lappend prefix [format "%04X" "0x$segment"] + } + regsub -all " " $prefix "" prefix + + # Convert ip address to string. + regsub -all {(.*)\.(.*)\.(.*)\.(.*)} $address \ + {[format "%02x%02x%02x%02x" \1 \2 \3 \4]} address + set address [subst $address] + set address [format "%08x%08x" 0x$addressIsatap 0x$address] + + set isatapSize [expr [string length $addressIsatap]/2] + + + # Expand if necessary. + set prefixLength [expr [string length $prefix]/2] + set expand [expr $ipV6AddressSize/8 - $ipV4AddressSize/8 - $prefixLength - $isatapSize] + for {set i $expand} {$i} {incr i -1} { + append prefix "00" + } + append prefix $address + + # Break it up into IPv6 address seperated by colons. + set address {} + while {[string length $prefix] > 0} { + append address "[string range $prefix 0 3]:" + set prefix [string replace $prefix 0 3] + } + regexp {(.*):$} $address match address + set retValue $address + } + + return $retValue +} + +######################################################################################## +# +# Procedure: ipv6::convertIpToIpV4Compatible +# +# Description: Converts an IP address to an IPv6 IPv4 Compatible address. +# +# Argument(s): address: version 4 style IP address +# prefix: defaults to 0 +# +# Returns: converted address +# +######################################################################################## +proc ipv6::convertIptoIpV4Compatible {address {prefix 0}} \ +{ + set retValue [convertIpToIpV6 $address $prefix] + return $retValue +} + + +######################################################################################## +# +# Procedure: ipv6::convertIpTo6To4 +# +# Description: Converts an IP address to an IPv6 6to4 address. +# +# Argument(s): address: version 4 style IP address +# prefix: defaults to 2002 +# +# Returns: converted address +# +######################################################################################## +proc ipv6::convertIpTo6to4 {address {prefix 2002}} \ +{ + set retValue [convertIpToIpV6 $address $prefix addressFollowPrefix] + return $retValue +} + + +######################################################################################## +# +# Procedure: ipv6::convertIpV6ToMac +# +# Description: Converts an IPv6 address to a MAC. +# +# Argument(s): address: IpV6 address +# +# Returns: converted address +# +######################################################################################## +proc ipv6::convertIpV6ToMac {address {args ""}} \ +{ + variable macAddressSize + variable ipV6AddressSize + + set retValue {} + + if {[isValidAddress $address]} { + set address [expandAddress $address] + regsub -all ":" $address {} address + + set start [expr ($ipV6AddressSize/4) - ($macAddressSize/4)] + set end [expr ($ipV6AddressSize/4) - 1] + set byteString [string range $address $start $end] + + set address {} + while {[string length $byteString] > 0} { + append address "[string range $byteString 0 1]:" + set byteString [string replace $byteString 0 1] + } + regexp {(.*):$} $address match address + set retValue $address + } + + return $retValue +} + +######################################################################################## +# +# Procedure: ipv6::convertIpV6ToIp +# +# Description: Converts an IPv6 address to an IP address, aka IPv4-Compatible Address. +# +# Argument(s): address: IPv4 style address (192.168.1.10) +# +# Returns: converted address +# +######################################################################################## +proc ipv6::convertIpV6ToIp {address {args ""}} \ +{ + variable ipV4AddressSize + variable ipV6AddressSize + + set retValue {} + + if {[isValidAddress $address]} { + set address [expandAddress $address] + regsub -all ":" $address {} address + + set start [expr ($ipV6AddressSize/4) - ($ipV4AddressSize/4)] + set end [expr ($ipV6AddressSize/4) - 1] + set byteString [string range $address $start $end] + + set address {} + while {[string length $byteString] > 0} { + lappend address "[string range $byteString 0 1]" + set byteString [string replace $byteString 0 1] + } + regsub -all {(.*) (.*) (.*) (.*)} $address \ + {[format "%d.%d.%d.%d" 0x\1 0x\2 0x\3 0x\4]} address + set address [subst $address] + set retValue $address + } + + return $retValue +} + +######################################################################################## +# +# Procedure: ipv6::convertNoop +# +# Description: Place holder, doesn't do any thing +# +# Argument(s): None +# +# Returns: None +# +######################################################################################## +proc ipv6::convertNoop {address {args ""}} \ +{ +} + +######################################################################################## +# +# Field 'get' Utilities +# +######################################################################################## + +proc ipv6::getAddressFields {} \ +{ + variable fieldNames + return $fieldNames +} + +######################################################################## +# Procedure: getFieldListByPrefix +# +# Description: Given an address, return a list of the valid fields +# for that address type. +# +# Argument(s): ipAddress: IP address +# +# Returns: field list (enums are interfaceId, subnetID, siteLevelAggregationId, +# nextLevelAggregationId, topLevelAggregationId). +# +######################################################################## +proc ipv6::getFieldListByPrefix {address} \ +{ + variable fieldListByPrefix + + set retValue {} + + if {![ipV6Address decode $address]} { + set prefixType [ipV6Address cget -prefixType] + if {[info exists fieldListByPrefix($prefixType)]} { + set retValue $fieldListByPrefix($prefixType) + } + } + + return $retValue +} + +######################################################################## +# Procedure: getFieldNamesByPrefix +# +# Description: Given an address, return a list of the field names +# for that address type. +# +# Argument(s): ipAddress: IP address +# +# Returns: list of field names +# +######################################################################## +proc ipv6::getFieldNamesByPrefix {address} \ +{ + variable fieldNamesByPrefix + + set retValue {} + + if {![ipV6Address decode $address]} { + set prefixType [ipV6Address cget -prefixType] + if {[info exists fieldNamesByPrefix($prefixType)]} { + set retValue $fieldNamesByPrefix($prefixType) + } + } + + return $retValue +} + + + + +######################################################################################## +# +# Procedure: ipv6::getFormatPrefix +# +# Description: Returns the format prefix of an IPv6 address. +# +# Argument(s): address +# +# Returns: format prefix (enums: ipV6NSAPAllocation, ipV6GlobalUnicast, +# ipV6LinkLocalUnicast, ipV6SiteLocalUnicast, +# ipV6IPXAllocation, ipV6Multicast) +# +######################################################################################## +proc ipv6::getFormatPrefix {address} \ +{ + set retValue {} + + if {![ipV6Address decode $address]} { + set retValue [list [ipV6Address cget -prefixValue]] + } + + return $retValue +} + + +######################################################################################## +# +# Procedure: ipv6::getTopLevelAggregationId +# +# Description: Returns the top level aggregation id of the global aggregate address. +# +# Argument(s): address +# +# Returns: top level aggregation id +# +######################################################################################## +proc ipv6::getTopLevelAggregateId {address} \ +{ + set retValue {} + + if {![ipV6Address decode $address]} { + if {[ipV6Address cget -prefixType] == $::ipV6GlobalUnicast} { + set retValue [ipV6Address cget -topLevelAggregationId] + } + } + + return $retValue +} + +######################################################################################## +# +# Procedure: ipv6::getNextLevelAggregationId +# +# Description: Returns the Next Level Aggregation Id of the Global Aggregate address. +# +# Argument(s): address +# +# Returns: next level aggregation id +# +######################################################################################## +proc ipv6::getNextLevelAggregateId {address} \ +{ + set retValue {} + + if {![ipV6Address decode $address]} { + if {[ipV6Address cget -prefixType] == $::ipV6GlobalUnicast} { + set retValue [ipV6Address cget -nextLevelAggregationId] + } + } + + return $retValue +} + +######################################################################################## +# +# Procedure: ipv6::getSiteLevelAggregationId +# +# Description: Returns the Site Level Aggregation Id of the Global Aggregate address. +# +# Argument(s): address +# +# Returns: site level aggregation id +# +######################################################################################## +proc ipv6::getSiteLevelAggregateId {address} \ +{ + set retValue {} + + if {![ipV6Address decode $address]} { + if {[ipV6Address cget -prefixType] == $::ipV6GlobalUnicast} { + set retValue [ipV6Address cget -siteLevelAggregationId] + } + } + + return $retValue +} + + +######################################################################################## +# +# Procedure: ipv6::getSubnetId +# +# Description: Returns the Subnet Id of the Site Local address. +# +# Argument(s): address +# +# Returns: subnet id +# +######################################################################################## +proc ipv6::getSubnetId {address} \ +{ + set retValue {} + + if {![ipV6Address decode $address]} { + if {[ipV6Address cget -prefixType] == $::ipV6SiteLocalUnicast} { + set retValue [ipV6Address cget -subnetId] + } + } + + return $retValue +} + +######################################################################################## +# +# Procedure: ipv6::getInterfaceId +# +# Description: Returns the Interface Id Global Aggregate, Site Local, or Link +# local address. +# +# Argument(s): address +# +# Returns: interface id +# +######################################################################################## +proc ipv6::getInterfaceId {address} \ +{ + set retValue {} + + if {![ipV6Address decode $address]} { + switch [ipV6Address cget -prefixType] " + $::ipV6GlobalUnicast - + $::ipV6SiteLocalUnicast - + $::ipV6LinkLocalUnicast { + set retValue [list [ipV6Address cget -interfaceId]] + } + " + } + + return $retValue +} + +######################################################################################## +# +# Procedure: ipv6::getLoopbackAddress +# +# Description: Returns the loopback address. +# +# Argument(s): None +# Argument(s): None +# +# Returns: loopback address +# +######################################################################################## +proc ipv6::getLoopbackAddress {} \ +{ + variable addressLoopback + return $addressLoopback +} + + +######################################################################################## +# +# Validation Utilities +# +######################################################################################## + +######################################################################################## +# +# Procedure: ipv6::isValidAddress +# +# Description: TRUE/FALSE: Is the given ipv6 address valid? +# +# Argument(s): address +# type: unicast (default), anycast, multicast +# +# Returns: ::true or ::false +# +######################################################################################## +proc ipv6::isValidAddress {address {type unicast}} \ +{ + variable ipV4AddressSize + + set retCode $::false + + set segments 8 + set nibbleSize 4 + + set segmentsBefore {} + set segmentsAfter $address + set ipv4Address {} + + set count [regsub -all ":" $address ":" address] + if {$count > 0 && $count <= [expr $segments-1]} { + + if {[isMixedVersionAddress $address]} { + set end [expr [llength [split $address :]] - 1] + set ipv4Address [lindex [split $address :] $end] + regsub "$ipv4Address" $address {} address + regsub -all {(.*)\.(.*)\.(.*)\.(.*)} $ipv4Address \ + {[format "%02x%02x:%02x%02x" \1 \2 \3 \4]} ipv4Address + set ipv4Address [subst $ipv4Address] + append address $ipv4Address + } + + # + # Fill in the zeroes needed to expand. + set segmentsBefore {} + set segmentsAfter $address + + if {[regexp {(.*)::(.*)} $address match segmentsBefore segmentsAfter]} { + set segmentsBefore [split $segmentsBefore :] + set segmentsAfter [split $segmentsAfter :] + set segmentsNeeded [expr $segments - ([llength $segmentsBefore] + [llength $segmentsAfter])] + set segmentList "$segmentsBefore [string repeat " 0" $segmentsNeeded] $segmentsAfter" + } else { + set segmentList [split $address :] + } + + if {[llength [join $segmentList]] == 8} { + set retCode $::true + foreach segment $segmentList { + if {[regexp {[^0-9a-fA-f]} $segment match] > 0} { + set retCode $::false + break + } + if {[mpexpr 0x$segment > 0xffff]} { + set retCode $::false + break + } + } + } + } + + + return $retCode +} + +######################################################################################## +# +# Procedure: ipv6::validAddress +# +# Description: Given and address, determine it's validitiy +# +# Argument(s): address: ipv6 address +# type: unicast, multicast, anycast +# +# Returns: ::true or ::false +# +######################################################################################## +proc ipv6::validateAddress {address {type unicast}} \ +{ + set retCode $::true + + set retCode [isValidAddress $address] + + if { $retCode } { + + switch $type { + unicast - + anycast { + + } + multicast { + set retCode [isValidMCAddress $address] + } + default { + set retCode $::false + } + } + } + + return $retCode +} + +######################################################################################## +# +# Procedure: ipv6::isReservedMCAddress +# +# Description: TRUE/FALSE: Is this a reserved multicast address? +# +# Argument(s): address: Ipv6 address +# +# Returns: ::true or ::false +# +######################################################################################## +proc ipv6::isReservedMCAddress {address} \ +{ + variable reservedMCAddressList + set retCode $::false + + set expand_address [ipv6::expandAddress $address] + if { [llength expand_address] } { + + # Check in the list for predefined multicast addresses + if { [lsearch $reservedMCAddressList $expand_address] < 0 } { + + # Check for Solicited-node addresses + if {[string first "ff02:0000:0000:0000:0000:0001:ff" $expand_address] < 0 } { + + # Check for Service location addresses + if { [string first "ff05:0000:0000:0000:0000:0000:0001" $expand_address] == 0} { + set splittedAddr [split $expand_address ":"] + set comparedPart [format "0x%s" [lindex $splittedAddr 7]] + if { $comparedPart >= 0x1000 && $comparedPart <= 0x13ff } { + set retCode $::true + } + } + } else { + set retCode $::true + } + } else { + set retCode $::true + } + } + + return $retCode +} + + +######################################################################################## +# +# Procedure: ipv6::isValidMCAddress +# +# Description: TRUE/FALSE: Is this a reserved multicast address? +# +# Argument(s): address: Ipv6 address +# +# Returns: ::true or ::false +# +######################################################################################## +proc ipv6::isValidMCAddress {address} \ +{ + set retCode $::false + + set expand_address [ipv6::expandAddress $address] + if { [llength expand_address] } { + + set splittedAddr [split $expand_address ":"] + set mcastPart [format "0x%s" [lindex $splittedAddr 0]] + if { $mcastPart >= 0xff00 && $mcastPart <= 0xff1f } { + set retCode $::true + } + } + + return $retCode +} + +######################################################################################## +# +# Procedure: ipv6::isMixedVersionAddress +# +# Description: Given an IPv6 address, determine if it is compiled of both +# IPv4 and IPv6 components, ie: +# +# 0:0:0:0:0:0:192.168.10.1 +# +# Argument(s): address: Ipv6 address (any acceptable IPv6 format) +# +# Returns: ::true or ::false +# +######################################################################################## +proc ipv6::isMixedVersionAddress {address} \ +{ + set retCode $::false + set address [lindex [split $address :] end] + if {[llength [split $address .]] == 4} { + set retCode $::true + } + + return $retCode +} + +######################################################################## +# Procedure: incrIpV6Field +# +# Description: Increments the specified field of a 128 bit IPv6 address +# +# Argument(s): ipAddress: IP address whose field to be incremented +# field: IPv6 - field to be incremented, default is interfaceId +# refer to TCLIpV6Address field enumerations, or +# any prefix, overflow is not supported yet +# increment: increment the field by this number, default is 1 +# +# Returns: Modified IP address +# +######################################################################## +proc ipv6::incrIpField {address {prefix 128} {increment 1} } \ +{ + variable fieldPositions + + set newAddress {} + set errorFlag 0 + + if {[info exists fieldPositions($prefix)]} { + set prefix $fieldPositions($prefix) + } else { + if {[isValidInteger $prefix] } { + if { $prefix > 128 || $prefix < 0 } { + errorMsg "Error: Invalid prefix value, must be between 0 - 128, inclusive" + set errorFlag 1 + } + } else { + set errorFlag 1 + errorMsg "Error: Invalid predefined field enumeration" + } + + } + if { !$errorFlag } { + ipV6Address setDefault + if {![ipV6Address decode $address]} { + + set prefixType [ipV6Address cget -prefixType] + set newAddress [incIpv6AddressByPrefix $address $prefix $increment] + + # Invalid if increment overflows into the format prefix. + if {![ipV6Address decode $newAddress]} { + set newAddress [ipV6Address encode] + if {[ipV6Address cget -prefixType] != $prefixType} { + set newAddress {} + } + } + } else { + errorMsg "Error: Invalid ipV6 address:$address" + } + } + + return $newAddress +} + + + +# This is NOT COMPLETE +# The idea was to get the previous field to the "prefix" from the fieldListByPrefix then +# make sure it didn't change after increment, and only supports the predefined prefixes +# in order to finish this method, we need to create one more method, and test it +# proc getOverflowField { prefix prefixType } this will return the field that is overflown + +#proc ipv6::incrIpField_withOverflow {address {prefix 0} {increment 1} {wrapOverflow no} } \ +#{ +# variable fieldPositions +# +# set newAddress {} +# set predefinedField 0 +# set fieldMaxValue 255 +# +# if {[info exists fieldPositions($prefix)]} { +# set prefix $fieldPositions($prefix) +# set predefinedField 1 +# } else { +# set wrapOverflow no +# } +# puts "prefix:$prefix" +# +# ipV6Address setDefault +# if {![ipV6Address decode $address]} { +# +# set prefixType [ipV6Address cget -prefixType] +# set newAddress [incIpv6AddressByPrefix $address $prefix $increment] +# if { $predefinedField } { +# set overflowField [getOverflowField $prefix $prefixType] +# } +# +# # Invalid if increment overflows into the format prefix. +# if {![ipV6Address decode $newAddress]} { +# +# set newAddress [ipV6Address encode] +# if {$wrapOverflow == "yes" } { +# set newOverflowField [hexlist2Value [ipV6Address cget -overflowField]] +# ipV6Address decode $address +# set oldOverflowFieldValue [hexlist2Value [ipV6Address cget -overflowField]] +# if { $newOverflowField > $oldOverflowFieldValue } { +# ipV6Address config -$prefix [value2Hexlist $increment] +# } +# } +# set newAddress [ipV6Address encode] +# } +# } else { +# errorMsg "Invalid ipV6 address:$address" +# } +# +# return $newAddress +#} + +######################################################################## +# Procedure: convertIpv6AddrToBytes +# +# Description: Converts the IPv6 into bytes +# +# Argument(s): address: IPv6 address +# +# Returns: +# +######################################################################## +proc ipv6::convertIpv6AddrToBytes { address } \ +{ + set expand_address [expandAddress $address] + regsub -all ":" $expand_address " " expand_address + regsub -all {([0-9a-fA-F]{2})([0-9a-fA-F]{2})} $expand_address {\1 \2} addrList + return $addrList +} + +######################################################################## +# Procedure: convertBytesToIpv6Address +# +# Description: Converts the bytes into IPv6 address +# +# Argument(s): address: IPv6 address +# +# Returns: +# +######################################################################## +proc ipv6::convertBytesToIpv6Address { bytes } \ +{ + set str {} + foreach {b1 b2} $bytes { + lappend str "$b1$b2" + } + set str [join $str ":"] + return [compressAddress [join $str ""]] +} + + +######################################################################## +# Procedure: incIpv6AddressByPrefix +# +# Description: Increments the specified field of a 128 bit IPv6 address +# +# Argument(s): ipAddress: IP address whose field to be incremented +# prefix: IPv6 - field to be incremented, default is 32 +# inc: increment the prefix by this number, default is 1 +# +# Returns: Modified IP address +# +######################################################################## +proc ipv6::incIpv6AddressByPrefix {ipAddress {prefix 32} {inc 1}} \ +{ + variable ipV6AddressSize + + set retAddress {} + + if {[isValidInteger $prefix] } { + if { $prefix > 128 || $prefix < 0 } { + errorMsg "Error: Invalid prefix value, must be between 0 - 128, inclusive" + set errorFlag 1 + } else { + set ipAddress [expandAddress $ipAddress] + set host [mpexpr [hexlist2Value [convertIpv6AddrToBytes $ipAddress]] & (int(pow(2,($ipV6AddressSize - $prefix)) - 1))] + set network [mpexpr [hexlist2Value [convertIpv6AddrToBytes $ipAddress]] >> ($ipV6AddressSize - $prefix)] + mpincr network $inc + + set retAddress [convertBytesToIpv6Address [value2Hexlist [mpexpr ($network << ($ipV6AddressSize - $prefix)) | $host] 16]] + } + } else { + errorMsg "Error: Expecting integer prefix value between 0 - 128, inclusive." + } + return $retAddress +} + +######################################################################## +# Procedure: getFieldMask +# +# Description: Return the IPv6 Field Mask (used with stream increment). +# +# Argument(s): field: IPv6 - field to be incremented, default is interfaceId +# refer to TCLIpV6Address field enumerations +# +# Returns: mask +# +######################################################################## +proc ipv6::getFieldMask {{field interfaceId}} \ +{ + variable fieldPositions + + set mask 64 + + switch $field { + interfaceId { + set mask 64 + } + subnetId - + siteLevelAggregationId - + nextLevelAggregationId - + topLevelAggregationId { + if {[info exists fieldPositions($field)]} { + set mask $fieldPositions($field) + } + } + } + + return $mask +} + +######################################################################## +# Procedure: getMinimumValidFramesize +# +# Description: Returns the minimum valid header length for and IPv6 +# packet. + +# IPv6 header length can vary depending on the options +# selected. The base header is 40 bytes long. +# +# Argument(s): useUdf +# useFir +# +# Returns: minimum acceptable frame size +# +######################################################################## +proc ipv6::getMinimumValidFramesize {{useUdf true} {useFir true}} \ +{ + global kFirSize kCrcSize kUdfSize kHeaderLength + + if {$useFir == "true"} { + set firSize $kFirSize + } else { + set firSize 0 + } + + if {$useUdf == "true"} { + set udfSize $kUdfSize + } else { + set udfSize 0 + } + + set minimum [expr [getHeaderLength] + \ + $firSize + \ + $udfSize + \ + $kCrcSize] + + set minimum [expr $minimum & 0xfffffffe] + +} + +######################################################################## +# Procedure: getHeaderLength +# +# Description: Return the length of the IPv6 Header including the +# MAC and UDP headers (in other words, everything up to +# the payload). +# +# NOTE: THis is incomplete since it handles only +# the simplest case... needs work. +# +# Arguments(s): None +# +# Returns: length of IPv6 header +# +######################################################################## +proc ipv6::getHeaderLength {} \ +{ + global kHeaderLength + + set headerLength 0 + + if {[protocol cget -name] == $::ipV6} { + set headerLength $::DaSaLength + switch [protocol cget -ethernetType] " + $::ethernetII - + $::ieee8023 - + $::ieee8022 { + incr headerLength 2 + } + $::ieee8023snap { + incr headerLength 10 + } + " + incr headerLength [expr $::kHeaderLength(ipV6) + $::udpHeaderLength] + } + + return $headerLength +} + +######################################################################## +# Procedure: getAddressFieldOffset +# +# Description: Return the offset to the given address field from the +# start of the IPv6 address field (not from the start of +# the header). +# +# Arguments(s): field: prefix, topLevelAggregationId, nextLevelAggregationId, +# siteLevelAggregationId, subnetId, interfaceId +# +# Returns: Offset +# +######################################################################## +proc ipv6::getAddressFieldOffset {field} \ +{ + variable fieldOffsets + + set retValue 0 + + if {[info exists fieldOffsets($field)]} { + set retValue $fieldOffsets($field) + } + + return $retValue +} + + diff --git a/dep/tclclient/ixTcl1.0/Generic/ixFileUtils.tcl b/dep/tclclient/ixTcl1.0/Generic/ixFileUtils.tcl new file mode 100644 index 00000000..b54a753a --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/ixFileUtils.tcl @@ -0,0 +1,158 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: ixFileUtils.tcl +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 09-05-2002 MG Genesis +# +# Description: Contains miscellaneous file commands. +# +################################################################################## + + +namespace eval ixFileUtils { + variable filesToClose "" +} + + +######################################################################## +# Procedure: ixFileUtils::closeOpenFiles +# +# Description: Either close all open file handles or close all file handles opened by Ixia routines +# The advanced test parameter closeAllFilesInCleanUp controls which one is done +# +# Arguments: None +# +# Returns: Nothing +######################################################################## +proc ixFileUtils::closeAll {} \ +{ + variable filesToClose + + if {[info tclversion] >= 8.3} { + set closeAllFiles [advancedTestParameter cget -closeAllFilesInCleanUp] + set listOfFiles "" + if {$closeAllFiles} { + set listOfFiles $filesToClose + } else { + foreach fileId [file channels file*] { + set index [lsearch -exact $filesToClose $fileId] + if {$index >= 0} { + lappend listOfFiles $fileId + } + } + } + foreach fileId $listOfFiles { + close $fileId + } + } +} + + +######################################################################## +# Procedure: ixFileUtils::addFileToList +# +# Description: Add a file handle to the list of handles to be closed during the cleanUp procedure +# +# Arguments: fileId - the file handle to add +# +# Returns: Nothing +######################################################################## +proc ixFileUtils::addFileToList { fileId } \ +{ + variable filesToClose + + lappend filesToClose $fileId +} + + +######################################################################## +# Procedure: ixFileUtils::removeFileFromList +# +# Description: Remove a file handle from the list of handles to be closed during the cleanUp procedure +# +# Arguments: fileId - the file handle to remove +# +# Returns: Nothing +######################################################################## +proc ixFileUtils::removeFileFromList { fileId } \ +{ + variable filesToClose + + set index [lsearch -exact $filesToClose $fileId] + if {$index >= 0} { + set filesToClose [lreplace $filesToClose $index $index] + } +} + + +######################################################################## +# Procedure: ixFileUtils::open +# +# Description: This command opens a file +# +# Arguments: filename - full path to filename +# access - file access parameter to use +# permission - file permission parameter to use +# +# Returns: fileID if successful, or "" if unsuccessful +######################################################################## +proc ixFileUtils::open {filename {access ""} {permission ""}} \ +{ + if {[catch {eval ::open [list $filename] $access $permission} fileID]} { + set fileID "" + } else { + ixFileUtils::addFileToList $fileID + } + return $fileID +} + + +######################################################################## +# Procedure: ixFileUtils::close +# +# Description: This command closes a file +# +# Arguments: fileId - channel id of the file to be closed +# +# Returns: Nothing +######################################################################## +proc ixFileUtils::close {fileId} \ +{ + catch {::close $fileId} + ixFileUtils::removeFileFromList $fileId +} + + diff --git a/dep/tclclient/ixTcl1.0/Generic/ixGraph.tcl b/dep/tclclient/ixTcl1.0/Generic/ixGraph.tcl new file mode 100644 index 00000000..1a526ded --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/ixGraph.tcl @@ -0,0 +1,982 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: ixGraph.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# Date Author Comments +# ----------- ------------------- -------------------------------------------- +# 2001/06/11 D. Heins-Gelder Initial release +# +# Description: This file contains API commands for generating a graph +# and displaying data on the graph. +# +################################################################################## + +package ifneeded BLT 2.4 + +namespace eval ixGraph { +# +# Public Procedures: +# ixGraph::create: Create graph with given attributes +# ixGraph::destroy Destroy graph. +# ixGraph::reset Reset all data. +# ixGraph::addLine Add a line to given graph +# ixGraph::deleteLine Delete a line from given graph +# ixGraph::resetLine Clear a given line of it's data +# ixGraph::updateLine Add new x,y coordinates(s) to graph +# ixGraph::updateCoordinates Append new coordinate to either x or y axis. +# ixGraph::getLines Show a list of line names +# ixGraph::getDataVector Obtain data vector handle from graph +# ixGraph::setDataVector Set data vector handle in graph +# ixGraph::updateDataVector Append data to the end of the data vector +# ixGraph::resetDataVector Clear data from the data vector +# + +# Private Procedures: +# None +# +# Private Variables: +# None +# +# +# SAMPLE USAGE: +# ------------- +# +# SAMPLE 1 +# +#set xyList \ +# [list 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 9.5 11 9 12 8 13 7 14 6 15 5 16 4 17 3 18 2 19 1.5 20 2 21 3 22 4 23 5 24 6 25 7 26 8 27 9 28 8.5 29 9 30 8] +# +#set g [ixGraph::create .g -title "MY GRAPH" \ +# -background black \ +# -foreground white \ +# -plotColor grey \ +# -legendColor grey \ +# -xColor red \ +# -yColor red \ +# -font {Arial-Bold 14} \ +# -xTitle "X-AXIS TITLE" \ +# -yTitle "Y-AXIS TITLE" \ +# -xRange [list 0 10 2] \ +# -yRange [list 0 10 2] \ +# -logoFile ixiaLogo.gif \ +# -crossHairs on \ +# -grid on ] +#pack $g +# +#ixGraph::addLine $g rate -color white \ +# -lineWidth 2 +# +## Update line with a list of coordinates. +#ixGraph::updateLine $g rate $xyList +#foreach {x y} $xyList { +## Update line with a single set of coordinates. +# ixGraph::updateLine $g rate [list $x $y] +# +## or +# +## Update x / y coordinates individually. +# ixGraph::updateCoordinate $g rate x [list $x] +# ixGraph::updateCoordinate $g rate y [list $y] +# +# after 250 +#} +# +#ixGraph::destroy $g +# +# END SAMPLE OF SAMPLE 1 + +# +# SAMPLE 2 +# +#ixGraph::destroy $g +#stat setDefault +#set g [ixGraph::create .g -title "MY GRAPH" \ +# -background black \ +# -foreground white \ +# -plotColor grey \ +# -legendColor grey \ +# -xColor red \ +# -yColor red \ +# -font {Arial-Bold 14} \ +# -xTitle "X-AXIS TITLE" \ +# -yTitle "Y-AXIS TITLE" \ +# -xRange [list 0 20 1] \ +# -yRange [list 0 200000 10000] \ +# -crossHairs on \ +# -grid on ] +#pack $g +# +#ixGraph::addLine $g rate -color white \ +# -lineWidth 2 +# +#set i 0 +#while {$i < 50} { +# after 1000 +# incr i 1 +# stat get -bytesReceived 1 1 1 +# set value [stat cget -bytesReceived] +# set value [expr $value / 1000] +# ixGraph::updateLine $g rate [list $i $value] +#} +# END SAMPLE OF SAMPLE 2 + +} +## End of ixGraph namespace + +######################################################################################## +# Procedure: create +# +# Description: Create a graph as a free-floating element and return it's handle +# +# Input: graphName: window path name, ie: .myGraph or .window.myGraph +# args: configuration parameters for the graph, the following +# are currently valid: +# -title value - Graph Title +# -foreground value - Graph foreground color (default grey) +# -background value - Graph background color (default black) +# -plotColor value - Color of plot area (default white) +# -font value - Graph text font +# -logoFile value - path and file of logo +# -xTickFont value - font of the numbers on the graph +# -yTickFont value - font of the numbers on the graph +# -xTitle value - X Axis Title +# -yTitle value - Y Axis Title +# -xTitleFont value - font of the axis titles +# -yTitleFont value - font of the axis titles +# -xRange value - [list $minimum $maximum $step] +# -yRange value - [list $minimum $maximum $step] +# -xColor value - color of x axis titles +# -yColor value - color of y axis titles +# -grid value - on or off +# -crossHairs value - on or off +# -legendColor value - color of legend (default grey) +# +# Output: +# +######################################################################################## +proc ixGraph::create {graphName args} \ +{ + set parameters [list title "" \ + foreground "black" \ + background "grey" \ + plotColor "white" \ + legendColor "grey" \ + logoFile "" \ + font {Arial 10} \ + xTitle "" \ + xTitleFont {Arial 10} \ + xTickFont {Arial 8} \ + xMinimum "" \ + xMaximum "" \ + xStep 0.0 \ + xColor "black" \ + yTitle "" \ + yTitleFont {Arial 10} \ + yTickFont {Arial 8} \ + yMinimum "" \ + yMaximum "" \ + yStep 0.0 \ + yColor "black" \ + grid "off" \ + crossHairs "off" ] + + set retValue "" + + # Initialize graph parameters + foreach {parameter value} $parameters { + set $parameter $value + } + + foreach {parameter value} $args { + switch -- $parameter { + -title { + set title $value + } + -foreground { + set foreground $value + } + -background { + set background $value + } + -plotColor { + set plotColor $value + } + -legendColor { + set legendColor $value + } + -font { + set font $value + } + -xTitle { + set xTitle $value + } + -yTitle { + set yTitle $value + } + -xColor { + set xColor $value + } + -yColor { + set yColor $value + } + -xTickFont { + set xTickFont $value + } + -yTickFont { + set yTickFont $value + } + -xTitleFont { + set xTitleFont $value + } + -yTitleFont { + set yTitleFont $value + } + -xRange { + foreach {xMinimum xMaximum xStep} $value {} + if {$xMaximum != ""} { + if {$xMinimum > $xMaximum} { + return $::TCL_ERROR + } + } + } + -yRange { + foreach {yMinimum yMaximum yStep} $value {} + if {$yMaximum != ""} { + if {$yMinimum > $yMaximum} { + return $::TCL_ERROR + } + } + if {$yStep == ""} {set yStep 0} + } + -grid { + switch $value { + on - + true - + yes { + set grid "on" + } + } + } + -crossHairs { + switch $value { + on - + true - + yes { + set crossHairs "on" + } + } + } + -logoFile { + if {[file exists $value]} { + set logoFile $value + } else { + return $::TCL_ERROR + } + } + default { + return $retValue + } + } + } + + # Create graph and configure as requested. + if {![catch {blt::graph $graphName} graph]} { + + $graph configure -title $title + $graph configure -bufferelements $::false + $graph axis configure x -title $xTitle -min $xMinimum \ + -max $xMaximum -stepsize $xStep + $graph axis configure y -title $yTitle -min $yMinimum \ + -max $yMaximum -stepsize $yStep -command "stringFormatNumber value" + + $graph grid $grid + + if {$crossHairs == "on"} { + Blt_Crosshairs $graph + $graph crosshairs configure -color grey + } + + $graph configure -foreground $foreground + $graph configure -background $background + $graph configure -plotbackground $plotColor + $graph configure -font $font + + $graph legend configure -background $legendColor + + $graph axis configure x -tickfont $xTickFont + $graph axis configure y -tickfont $yTickFont + $graph axis configure x -titlefont $xTitleFont + $graph axis configure y -titlefont $yTitleFont + $graph axis configure x -color $xColor + $graph axis configure y -color $yColor + $graph axis configure x -titlecolor $foreground + $graph axis configure y -titlecolor $foreground + + if {$logoFile == "on"} { + set marker [$graph marker create image \ + -image [image create photo -file ixiaLogo.gif]] + $graph marker configure $marker -coords {1 1} -under 1 + } + + set retValue $graph + } + + return $retValue +} + +######################################################################################## +# Procedure: reset +# +# Description: Set/Reset the graph and all associated data vectors to initial values. +# +# Input: graph: handle +# +# Output: TCL_OK or TCL_ERROR +# +######################################################################################## +proc ixGraph::reset {graph} \ +{ + set retCode $::TCL_OK + + if ![winfo exists $graph] { + return $::TCL_ERROR + } + + foreach item [$graph element names] { + resetLine $graph $item + } + + # Axis maximum set dynamically, reset to auto scale. + $graph yaxis configure -max "" + + update idletasks + return $retCode + +} + +######################################################################################## +# Procedure: destroy +# +# Description: Deletes all data associated with the graph. +# +# Input: graph: handle +# +# Output: TCL_OK or TCL_ERROR +# +######################################################################################## +proc ixGraph::destroy {graph} \ +{ + set retCode $::TCL_OK + + if {![winfo exists $graph]} { + return $::TCL_ERROR + } + + foreach item [$graph element names] { + blt::vector destroy [$graph element cget $item -xdata] + blt::vector destroy [$graph element cget $item -ydata] + + $graph element delete $item + } + ::destroy $graph + + return $retCode + +} + +######################################################################################## +# Procedure: addLine +# +# Description: Add a data line to the given graph. +# +# Input: graph: handle +# args: configuration parameters for the graph, the following +# are currently valid: +# +# -color value - line color name (ie red) or +# hex value (ie #000000) +# -lineWidth value - line size (default 1) +# -dashes value - # from 1-255, "" is solid line +# -legendFont value - Font of legend text +# +# Output: vector handle +# +######################################################################################## +proc ixGraph::addLine {graph name args} \ +{ + set parameters [list color black \ + symbol none \ + smooth linear \ + lineWidth 1 \ + dashes "" \ + legendFont {Arial 7} \ + ] + + set retValue "" + + # Initialize graph parameters + foreach {parameter value} $parameters { + set $parameter $value + } + + if {![winfo exists $graph]} { + return $retValue + } + + foreach {parameter value} $args { + switch -- $parameter { + -color { + set color $value + } + -xSize { + set xSize $value + } + -ySize { + set ySize $value + } + -symbol { + set symbol $value + } + -lineWidth { + set lineWidth $value + } + -dashes { + set dashes $value + } + -legendFont { + set legendFont $value + } + default { + return $retValue + } + } + } + + if {![catch {$graph element create $name}]} { + set xVector [blt::vector create #auto(1)] + set yVector [blt::vector create #auto(1)] + + + $graph element configure $name -xdata $xVector + $graph element configure $name -ydata $yVector + $graph element configure $name -color $color + $graph element configure $name -symbol $symbol + $graph element configure $name -smooth $smooth + $graph element configure $name -linewidth $lineWidth + $graph element configure $name -dashes $dashes + $graph legend configure -font $legendFont + + set retValue [list $xVector $yVector] + } + + return $retValue +} + +######################################################################################## +# Procedure: getLines +# +# Description: Get a list of lines attached to a graph. +# +# Input: graph: handle +# +# Output: List of lines attached to graph +# +######################################################################################## +proc ixGraph::getLines {graph} \ +{ + + set retCode $::TCL_OK + + if ![winfo exists $graph] { + return $::TCL_ERROR + } + + return [lsort [$graph element names]] +} + + +######################################################################################## +# Procedure: deleteLine +# +# Description: Delete a line from the given graph. +# +# Input: graph: handle +# line: name of graph line +# +# Output: vector handle +# +######################################################################################## +proc ixGraph::deleteLine {graph name} \ +{ + set retCode $::TCL_OK + + if ![winfo exists $graph] { + return $::TCL_ERROR + } + + if {[lsearch [$graph element names] $name] < 0} { + return $::TCL_ERROR + } + + set xVector [$graph element cget $name -xdata] + if {$xVector != ""} { + blt::vector destroy $xVector + } + set yVector [$graph element cget $name -ydata] + if {$yVector != ""} { + blt::vector destroy $yVector + } + + if {[catch {$graph element delete $name}]} { + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################################## +# Procedure: resetLine +# +# Description: Resets a graph line to no data. +# +# Input: graph: handle +# line: name of graph line +# +# Output: vector handle +# +######################################################################################## +proc ixGraph::resetLine {graph line} \ +{ + set retCode $::TCL_OK + + # Valid graph handle? + if ![winfo exists $graph] { + return $::TCL_ERROR + } + + # Is the given line a member of this graph? + if {[lsearch [$graph element names] $line] < 0} { + return $::TCL_ERROR + } + + # Clear the x & y vectors. + if {[set xVector [$graph element cget $line -xdata]] != ""} { + resetDataVector $graph $xVector + } + if {[set yVector [$graph element cget $line -ydata]] != ""} { + resetDataVector $graph $yVector + } + + $graph axis view x moveto 0.0 +} + +######################################################################################## +# Procedure: updateLine +# +# Description: Inserts new data into a given line. +# +# Input: graph: handle +# line: name of graph line +# xyCoordinate: [list x y x1 y1 x2 x2 .... xn yn] +# +# Output: vector handle +# +######################################################################################## +proc ixGraph::updateLine {graph line xyCoordinate} \ +{ + set retCode $::TCL_OK + + if ![winfo exists $graph] { + return $::TCL_ERROR + } + + if {[lsearch [$graph element names] $line] < 0} { + return $::TCL_ERROR + } + + # Don't allow graph to grow beyond size setting. + set xVector [$graph element cget $line -xdata] + set yVector [$graph element cget $line -ydata] + + foreach {x y} $xyCoordinate { + updateDataVector $graph $xVector $yVector [list $x $y] + + set xMaximum [$graph xaxis cget -max] + if {$x > $xMaximum} { + $graph axis view x moveto 1.0 + } + } + update idletasks + + return $retCode +} + +######################################################################################## +# Procedure: getDataVector +# +# Description: Given a graph and line, return the data vector +# associated with the given axis. +# +# Input: graph: handle +# line: desired line (name of) +# axis: x or y +# +# Output: vector handle +# +######################################################################################## +proc ixGraph::getDataVector {graph line axis} \ +{ + set retValue "" + + # Valid graph? + if {[winfo exists $graph]} { + + # Valid line? + if {[lsearch [getLines $graph] $line] >= 0} { + switch $axis { + x { + set retValue [$graph element cget $line -xdata] + } + y { + set retValue [$graph element cget $line -ydata] + } + default { + set retValue "" + } + } + } + } + return $retValue +} + +######################################################################################## +# Procedure: setDataVector +# +# Description: Associate the given data vector with the given graph-line-axis +# combination. +# +# Input: graph: graph handle +# line: line handle +# axis: x or y +# vector: vector handle +# +# Output: TCL_OK or TCL_ERROR +# +######################################################################################## +proc ixGraph::setDataVector {graph line axis vector} \ +{ + set retCode $::TCL_ERROR + + if {[winfo exists $graph]} { + + if {[lsearch [getLines $graph] $line] >= 0} { + switch $axis { + x { + $graph element config $line -xdata $vector + set retCode $::TCL_OK + } + y { + $graph element config $line -ydata $vector + set retCode $::TCL_OK + } + default { + set retCode $::TCL_ERROR + } + } + } + } + return $retCode +} + +######################################################################################## +# Procedure: resetDataVector +# +# Description: Resets a data vector to no data. +# +# Input: graph: graph handle +# vector: vector handle +# +# Output: vector handle +# +######################################################################################## +proc ixGraph::resetDataVector {graph vector} \ +{ + set retCode $::TCL_OK + + # Valid graph handle? + if ![winfo exists $graph] { + return $::TCL_ERROR + } + + # Valid vector handle? + if {![info exists $vector]} { + return $::TCL_ERROR + } + + if {[$vector length] > 0} { + $vector delete 0:end + } +} + + +######################################################################################## +# Procedure: updateDataVectorPair +# +# Description: Inserts new data into a given vector x,y pair. +# +# Input: graph: handle +# vector: vector handle +# xyCoordinate: [list x y x1 y1 x2 x2 .... xn yn] +# +# Output: vector handle +# +######################################################################################## +proc ixGraph::updateDataVectorPair {graph xVector yVector xyCoordinate} \ +{ + set retCode $::TCL_OK + + if {![winfo exists $graph]} { + return $::TCL_ERROR + } + + foreach {x y} $xyCoordinate { + + updateDataVector $graph $xVector $yVector x $x + updateDataVector $graph $xVector $yVector y $y + } + + return $retCode +} + + +######################################################################################## +# Procedure: updateDataVector +# +# Description: Inserts new data into a given vector +# +# Input: graph: handle +# vector: vector handle +# Coordinate: [list x x1 x2 ... xn] OR [list y y1 y2 ... yn] +# increment: graph update increment (ie, how often is the graph updated) +# +# Output: vector handle +# +######################################################################################## +proc ixGraph::updateDataVector {graph xVector yVector axis coordinates {increment 1}} \ +{ + set retCode $::TCL_OK + + if {![winfo exists $graph]} { + return $::TCL_ERROR + } + + switch $axis { + x { + set vector $xVector + } + y { + set vector $yVector + } + default { + return $::TCL_ERROR + } + } + + set xMaximum [$graph xaxis cget -max] + set xMinimum [$graph xaxis cget -min] + set windowLength [expr $xMaximum-$xMinimum] + + foreach coordinate $coordinates { + + # BLT is limited to an signed long - temporary fix. + if {$coordinate < 0} { + set coordinate 0x07fffffff + errorMsg "Negative number encountered" + } + if {[catch {set ${vector}(++end) $coordinate} errorMsg]} { + errorMsg "$errorMsg" + set ${vector}(++end) 0x07fffffff + } + + # Dynamically set maximum for Y axis. + set yMaximum [$graph yaxis cget -max] + if {$yMaximum == "" } { + $graph yaxis config -max 100 + } + if {[$graph yaxis cget -max] < $coordinate} { + if {[catch {$graph yaxis config -max [expr {round($coordinate*1.05)}]}]} { + $graph yaxis config -max [expr {round($coordinate)}] + } + } + + if {[$vector length] > [expr {$windowLength/$increment}]} { + $xVector delete 0:0 + $yVector delete 0:0 + } + advanceBuffer $graph $xVector $windowLength $increment + } + + return $retCode +} + + +######################################################################################## +# Procedure: updateCoordinates +# +# Description: Append new data into the given axis on the given line in the given +# graph. +# +# Input: graph: handle +# line: name of graph line +# axis: x or y +# coordinates: [list value1 value2 value 3... valuen] +# increment: graph update increment (ie, how often is the graph updated) +# +# Output: vector handle +# +######################################################################################## +proc ixGraph::updateCoordinates {graph line axis coordinates {increment 1}} \ +{ + set retCode $::TCL_OK + + if ![winfo exists $graph] { + return $::TCL_ERROR + } + + if {[lsearch [$graph element names] $line] < 0} { + return $::TCL_ERROR + } + + set xVector [$graph element cget $line -xdata] + set yVector [$graph element cget $line -ydata] + + switch $axis { + x { + set vector $xVector + } + y { + set vector $yVector + } + default { + return $::TCL_ERROR + } + } + + set xMaximum [expr {round([$graph xaxis cget -max])}] + set xMinimum [expr {round([$graph xaxis cget -min])}] + set windowLength [expr $xMaximum-$xMinimum] + + foreach coordinate $coordinates { + + # BLT is limited to an signed long - temporary fix. + if {$coordinate < 0} { + set coordinate 0x07fffffff + errorMsg "Negative number encountered, statistic: $line." + } + if {[catch {set ${vector}(++end) $coordinate} errorMsg]} { + errorMsg "$errorMsg" + set ${vector}(++end) 0x07fffffff + } + + # Dynamically set maximum for Y axis. + set yMaximum [$graph yaxis cget -max] + if {$yMaximum == "" } { + $graph yaxis config -max 100 + } + if {[$graph yaxis cget -max] < $coordinate} { + if {[catch {$graph yaxis config -max [expr {round($coordinate*1.05)}]}]} { + $graph yaxis config -max [expr {round($coordinate)}] + } + } + + if {[$vector length] > [expr {$windowLength/$increment}]} { + $yVector delete 0:0 + $xVector delete 0:0 + + $graph axis view x moveto [mpexpr 1.0/[$xVector length]] + } + advanceBuffer $graph $xVector $windowLength $increment + } + + return $retCode +} + +######################################################################################## +# Procedure: advanceBuffer +# +# Description: The x axis contains time data which can be determined at the start of +# the test, therefore to optimize graphing the x axis is updated +# in multiples of the window length. This procedure performs the +# incrementation of the x axis vector. +# +# Input: graph: handle +# vector: vector handle +# range: increment size (window) +# increment: graph update increment +# +# Output: $::TCL_ERROR or $::TCL_OK +# +######################################################################################## +proc ixGraph::advanceBuffer {graph vector range {increment 1}} \ +{ + set retCode $::TCL_OK + + if ![winfo exists $graph] { + return $::TCL_ERROR + } + + if {[$vector length] > [expr {$range/$increment}]} { + return $::TCL_OK + } + + set nextVectorSegment [blt::vector create #auto(1)] + #set range [expr round($range - 1)] + $nextVectorSegment set [$vector range 0 [mpexpr $range/$increment-1]] + + $nextVectorSegment expr {$nextVectorSegment + $range} + $vector append $nextVectorSegment + + blt::vector destroy $nextVectorSegment + + return $retCode +} + + + + + diff --git a/dep/tclclient/ixTcl1.0/Generic/log.tcl b/dep/tclclient/ixTcl1.0/Generic/log.tcl new file mode 100644 index 00000000..ae5d2e9b --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/log.tcl @@ -0,0 +1,177 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: log.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 10-22-1999 HS Genesis +# +# Description: Command to set log parameters. +# +################################################################################## + + + +####################################################################################### +# Procedure: logger::on +# +# Description: This command is used to turn on logging. Usage is as follows: +# log on +# +####################################################################################### +proc logger::on {args} \ +{ + global loggerParms + + # if there are any current log files open, close them first + if [info exists loggerParms(enabled)] { + logOff + } + + set loggerParms(enabled) 1 + if {[string length [logger cget -logFileName]] == 0} { + logger config -logFileName defaultFile.log + logger config -fileBackup false + } + + set loggerParms(fileID) [openMyFile [logger cget -logFileName]] + + set startTime [clock seconds] + catch {results config -startTime $startTime} + + puts [logger cget -fileID] ">>>>>>> Start Time: [clock format [clock seconds] -format "%A, %b %d, %Y %I:%M:%S %p"]\n" + logger config -startTime $startTime + + if {[info script] != ""} { + puts [logger cget -fileID] "\nFile being sourced : [info script]\n" + } +} + + +####################################################################################### +# Procedure: logger::off +# +# Description: This command is used to turn off logging. Usage is as follows: +# log off +# +####################################################################################### +proc logger::off {args} \ +{ + global loggerParms + + if [info exists loggerParms(enabled)] { + logger config -endTime [clock seconds] + if {[info exists loggerParms(fileID)]} { + puts [logger cget -fileID] "\n>>>>>>> End Time: [clock format [clock seconds] -format "%A, %b %d, %Y %I:%M:%S %p"]" + puts [logger cget -fileID] "Actual Duration of Test: [formatDurationTime [expr $loggerParms(endTime) - $loggerParms(startTime)]] seconds" + flush [logger cget -fileID] + if {[logger cget -fileID] != "stdout"} { + closeMyFile [logger cget -fileID] + } + set loggerParms(fileID) stdout + } + unset loggerParms(enabled) + } +} + + +############################################################################### +# Procedure: logger::message +# +# Description: This command is used to write messages to the log. +# Usage is as follows: +# log message <-priority 1> "This is my message" +# +############################################################################### +proc logger::message {args} \ +{ + global loggerParms loggerMethods tcl_platform + + set argLen [llength $args] + set type logger + + if {[lindex $args 0] == "-priority"} { + set args [lreplace $args 0 0] + set value [lindex $args 0] + if [catch {format %d $value} err] { + set errMsg "Usage: $type message " + foreach op $loggerMethods(message) { + set errMsg [format "%s\n -$op " $errMsg] + } + error $errMsg + return + } + set args [lreplace $args 0 0] + # set the priority here when we decide to use it... + } + + if {[lindex $args 0] == "-nonewline"} { + set args [lreplace $args 0 0] + + catch {puts -nonewline [logger cget -ioHandle] [join $args " "]} + + if [info exists loggerParms(enabled)] { + puts -nonewline [logger cget -fileID] [join $args " "] + } + } else { + catch {puts [logger cget -ioHandle] [join $args " "]} + + if [info exists loggerParms(enabled)] { + puts [logger cget -fileID] [join $args " "] + } + } + + flush [logger cget -ioHandle] + + # required not only for flushing the stdout, but also to flush anything from the + # open socket connections (should probably revisit the socket code later) + update +} + + +####################################################################################### +# Procedure: logger::show +# +# Description: This command is used to show log parameters. Usage is as follows: +# logger show +# +####################################################################################### +proc logger::show {args} \ +{ + ixPuts " logger parameters" + ixPuts " ==================================" + showCmd logger +} + diff --git a/dep/tclclient/ixTcl1.0/Generic/logCollector.tcl b/dep/tclclient/ixTcl1.0/Generic/logCollector.tcl new file mode 100644 index 00000000..35935976 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/logCollector.tcl @@ -0,0 +1,162 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: logCollector.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 05-29-2007 DS +# +# Description: This file contains procs used to collect & send logs to support +# +################################################################################## + + +######################################################################## +# Procedure: ixCollectAndSendLogs +# +# This command wraps the exe used to collect logs, zip them & send them to support +# NOTE: This command does NOT require a package req IxTclHal to run +# +# Arguments(s): +# argList - options include: +# -chassis <.card><.port> +# -userEmail +# -supportEmail +# -stepsToReproduce +# -zipFileName +# +######################################################################## +proc ixCollectAndSendLogs {args} \ +{ + set level [expr [info level]] + set procName "[lindex [info level $level] 0]" + + set optList {-chassis -userEmail -supportEmail -stepsToReproduce -zipFileName} + + if {[llength $args] == 0} { + puts "Usage: $procName " + foreach op $optList { + puts " $op <[string trimleft $op "-"]>" + } + flush stdout + return 1 + } + + # this has to change, but leaving it here for the moment... + set exeString [list exec "../diagnostic/logcollectorclient.exe"] + + foreach {option parameter} $args { + switch -glob -- $option { + -chassis { + set parameter [split $parameter .] + set result [scan $parameter "%s %d %d" chassis card port] + switch -- $result { + 1 { + set exeString [concat $exeString "--chassis $chassis"] + } + 2 { + set exeString [concat $exeString "--chassis $chassis,$card"] + } + 3 { + set exeString [concat $exeString "--chassis $chassis,$card,$port"] + } + default { + puts "$procName: No chassis/hostname specified" + return 1 + } + } + } + -userEmail { + # just do some quickie checking looking for @ & . The exe should do the rest. + if {[llength $parameter] > 0} { + if {[regexp -all {[@.]} $parameter] >= 2} { + lappend exeString "--useremail=$parameter" + } else { + puts "$procName: Invalid userEmail: $parameter" + return 1 + } + } else { + puts "$procName: No userEmail specified" + return 1 + } + } + -supportEmail { + # just do some quickie checking looking for @ & . The exe should do the rest. + if {[llength $parameter] > 0} { + if {[regexp -all {[@.]} $parameter] >= 2} { + lappend exeString "--supportemail=$parameter" + } else { + puts "$procName: Invalid supportEmail: $parameter" + return 1 + } + } else { + puts "$procName: No supportEmail specified" + return 1 + } + } + -stepsToReproduce { + if {[llength $parameter] > 0} { + set exeString [concat $exeString "--usertext $parameter"] + } else { + puts "Warning: No steps to reproduce included with collection of log files" + } + } + -zipFileName { + if {[llength $parameter] > 0} { + set exeString [concat $exeString "--zip $parameter"] + } + } + default { + puts "Usage: $procName " + foreach op $optList { + puts " -$op " + } + flush stdout + return + } + } + catch {unset option} + catch {unset parameter} + } + + puts $exeString + + if [catch {eval $exeString} retCode] { + puts "$procName: $exeString failed: $retCode" + return 1 + } + + return 0 +} diff --git a/dep/tclclient/ixTcl1.0/Generic/map.tcl b/dep/tclclient/ixTcl1.0/Generic/map.tcl new file mode 100644 index 00000000..11c2163a --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/map.tcl @@ -0,0 +1,383 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: map.tcl +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 02-05-1998 DS Genesis +# +# Description: This file contains all the commands to configure the mapping +# of traffic which could be from ports of one card of one chassis to any port +# on same card on same chassis, different card on same chassis or different +# cards on different chassis. Any combination can be specified. +# +################################################################################## + + + +######################################################################################## +# Procedure: map::new +# +# Description: This command deletes the existing type & creates a new map. +# +######################################################################################## +proc map::new {args} \ +{ + global mapMethods mapParms + + set type "map" + + if {[llength $args] == 0} { + logMsg "Usage: $type new " + foreach op $mapMethods(new) { + logMsg " -$op " + } + return + } + + if {[string index [lindex $args 0] 0] != "-"} { + logMsg "Usage: $type new " + foreach op $mapMethods(new) { + logMsg " -$op " + } + return + } + set opt [string trimleft [lindex $args 0] "-"] + if {[lsearch $mapMethods(new) $opt] == -1} { + logMsg "Usage: $type new " + foreach op $mapMethods(new) { + logMsg " -$op " + } + return + } + + set mapType [lindex $args 1] + + if {![validateMapType $mapType]} { + set mapArray [format "%sArray" $mapType] + global $mapArray + + if [info exists $mapArray] { + unset $mapArray + } + } +} + + +####################################################################################### +# Procedure: map::add +# +# Description: This command adds a chassis, card and port number for tx and rx. +# +######################################################################################## +proc map::add {args} \ +{ + global mapMethods mapParms mapConfigVals + global one2oneArray one2manyArray many2oneArray many2manyArray + + # if no option name was passed then display all options available for tx + if {[llength $args] == 0} { + return -code error "Usage: map add $mapMethods(add)" + } + + if {[llength $args] != [llength $mapMethods(add)]} { + return -code error "Usage: map add $mapMethods(add)" + } + + if {![info exists mapParms(type)]} { + return -code error "map type not configured yet." + } + + set tx_c [lindex $args 0] + set tx_l [lindex $args 1] + set tx_p [lindex $args 2] + set rx_c [lindex $args 3] + set rx_l [lindex $args 4] + set rx_p [lindex $args 5] + + # first of all, don't let tx=rx!! + #if {($tx_c == $rx_c) && ($tx_l == $rx_l) && ($tx_p == $rx_p)} { + # return -code error "Invalid map: Tx == Rx" + #} + + switch $mapParms(type) { + "one2one" { + # check if tx is already configured as tx + if {[info exists one2oneArray($tx_c,$tx_l,$tx_p)]} { + return -code error "map Tx:[getPortId $tx_c $tx_l $tx_p] exists.\ + Delete it and add again to change the Rx" + } + + # check if rx is already configured as rx + foreach r [array names one2oneArray] { + if {[string compare $one2oneArray($r) "{$rx_c $rx_l $rx_p}"] == 0} { + return -code error "map Rx:[getPortId $rx_c $rx_l $rx_p] is already configured for Rx." + } + } + + set one2oneArray($tx_c,$tx_l,$tx_p) [list [list $rx_c $rx_l $rx_p]] + } + + "one2many" { + # check if rx is already configured as rx on this tx port + foreach txPort [array names one2manyArray] { + if {[lsearch $one2manyArray($txPort) [list $rx_c $rx_l $rx_p]] >= 0} { + if {"$tx_c,$tx_l,$tx_p" == $txPort} { + return -code error "map Tx:[getPortId $tx_c $tx_l $tx_p] to Rx:[getPortId $rx_c $rx_l $rx_p] already exists." + } else { + return -code error "map Rx:[getPortId $rx_c $rx_l $rx_p] is already configured for Rx." + } + } + } + + set one2manyArray($tx_c,$tx_l,$tx_p) [lappend one2manyArray($tx_c,$tx_l,$tx_p) [list $rx_c $rx_l $rx_p]] + } + + "many2one" { + # check if tx is already configured as tx + if {[info exists many2oneArray($tx_c,$tx_l,$tx_p)]} { + if {$many2oneArray($tx_c,$tx_l,$tx_p) == "{$rx_c $rx_l $rx_p}"} { + return -code error "map Tx:[getPortId $tx_c $tx_l $tx_p] to Rx:[getPortId $rx_c $rx_l $rx_p] already exists." + } else { + return -code error "map Tx:[getPortId $tx_c $tx_l $tx_p] is already configured for Tx." + } + } + + set many2oneArray($tx_c,$tx_l,$tx_p) [list [list $rx_c $rx_l $rx_p]] + } + + "many2many" { + if {([lsearch [array names many2manyArray] "$tx_c,$tx_l,$tx_p"] != -1)} { + if {[lsearch $many2manyArray($tx_c,$tx_l,$tx_p) "$rx_c $rx_l $rx_p"] == -1} { + set many2manyArray($tx_c,$tx_l,$tx_p) [lappend many2manyArray($tx_c,$tx_l,$tx_p) [list $rx_c $rx_l $rx_p]] + } + } else { + set many2manyArray($tx_c,$tx_l,$tx_p) [lappend many2manyArray($tx_c,$tx_l,$tx_p) [list $rx_c $rx_l $rx_p]] + } + } + + } + + return -code ok +} + +######################################################################################## +# Procedure: map::del +# +# Description: This command deletes a chassis, card and port number for tx and rx. +# +######################################################################################## + +proc map::del {args} \ +{ + global mapMethods mapParms mapConfigVals + + set retCode $::TCL_ERROR + + # if no option name was passed then display all options available for tx + if {[llength $args] == 0} { + logMsg "Usage: map del $mapMethods(del)" + return $::TCL_ERROR + } + + if {[llength $args] != [llength $mapMethods(add)]} { + logMsg "Usage: map del $mapMethods(del)" + return $::TCL_ERROR + } + + if {![info exists mapParms(type)]} { + logMsg "map type not configured yet." + return $::TCL_ERROR + } + + set tx_c [lindex $args 0] + set tx_l [lindex $args 1] + set tx_p [lindex $args 2] + set rx_c [lindex $args 3] + set rx_l [lindex $args 4] + set rx_p [lindex $args 5] + + switch $mapParms(type) { + "one2one" { + global one2oneArray + foreach tx [array names one2oneArray] { + if {$tx == "$tx_c,$tx_l,$tx_p" && [join $one2oneArray($tx)] == [list $rx_c $rx_l $rx_p]} { + unset one2oneArray($tx) + set retCode $::TCL_OK + break + } + } + } + + "one2many" { + global one2manyArray + + if [info exists one2manyArray($tx_c,$tx_l,$tx_p)] { + set indx [lsearch $one2manyArray($tx_c,$tx_l,$tx_p) [list $rx_c $rx_l $rx_p]] + if {$indx >= 0} { + if {[llength $one2manyArray($tx_c,$tx_l,$tx_p)] == 1} { + unset one2manyArray($tx_c,$tx_l,$tx_p) + } else { + set one2manyArray($tx_c,$tx_l,$tx_p) [lreplace $one2manyArray($tx_c,$tx_l,$tx_p) $indx $indx] + } + set retCode $::TCL_OK + } + } + } + + "many2one" { + global many2oneArray + foreach tx [array names many2oneArray] { + if {$tx == "$tx_c,$tx_l,$tx_p" && [join $many2oneArray($tx)] == [list $rx_c $rx_l $rx_p]} { + unset many2oneArray($tx) + set retCode $::TCL_OK + break + } + } + } + + "many2many" { + global many2manyArray + if [info exists many2manyArray($tx_c,$tx_l,$tx_p)] { + set indx [lsearch $many2manyArray($tx_c,$tx_l,$tx_p) [list $rx_c $rx_l $rx_p]] + if {$indx >= 0} { + if {[llength $many2manyArray($tx_c,$tx_l,$tx_p)] == 1} { + unset many2manyArray($tx_c,$tx_l,$tx_p) + } else { + set many2manyArray($tx_c,$tx_l,$tx_p) [lreplace $many2manyArray($tx_c,$tx_l,$tx_p) $indx $indx] + } + set retCode $::TCL_OK + } + } + } + + } + + return $retCode +} + + +######################################################################################## +# Procedure: map::show +# +# Description: This command is used to show the current port map. +# +# Arguments: +# mapType - valid options are: one2one, one2many, many2one or many2many +# Default is current configured mapType +# +######################################################################################## +proc map::show {{mapType ""}} \ +{ + global mapConfigVals + + if {$mapType == ""} { + set mapType [map cget -type] + } + + if [validateMapType $mapType] { + return + } + + set mapArray [format "%sArray" $mapType] + global $mapArray + + logMsg "\nMap type: $mapType" + + if {[llength [array names $mapArray]] < 1} { + logMsg "--->No ports in map.\n" + set retCode 1 + } else { + foreach txMap [lnumsort [array names $mapArray]] { + scan [join $txMap] "%d,%d,%d" tx_c tx_l tx_p + + foreach rxMap [set ${mapArray}($tx_c,$tx_l,$tx_p)] { + scan [join $rxMap] "%d %d %d" rx_c rx_l rx_p + + logMsg "\tTx:[getPortId $tx_c $tx_l $tx_p] Rx: [getPortId $rx_c $rx_l $rx_p]" + } + } + logMsg "\n" + } +} + + +######################################################################################## +# Procedure: map::validateMapType +# +# Description: This command is used to find out if the given map type is valid. +# +# Arguments: +# mapType - valid options are: one2one, one2many, many2one or many2many +# +######################################################################################## +proc map::validateMapType {mapType {verbose verbose}} \ +{ + global mapConfigVals + + set notValidMap 1 + + foreach thing [map getValidValues -type] { + if {$mapType == $thing} { + set notValidMap 0 + break + } + } + if {$notValidMap && $verbose == "verbose"} { + errorMsg "Error - invalid map type: $mapType" + } + + return $notValidMap +} + + +######################################################################################## +# Procedure: showmaps +# +# Description: This command is used to show all port maps. +# +# +######################################################################################## +proc showmaps {} \ +{ + global mapConfigVals + + foreach thing [split $mapConfigVals(type) |] { + map show $thing + } +} + + + + diff --git a/dep/tclclient/ixTcl1.0/Generic/miiUtils.tcl b/dep/tclclient/ixTcl1.0/Generic/miiUtils.tcl new file mode 100644 index 00000000..e379b6d7 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/miiUtils.tcl @@ -0,0 +1,619 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: miiUtils.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 05-05-03 DS +# +# Description: This file contains common procs used to configure features +# via the MII ixTclHal api +# +################################################################################## + + +proc ixMiiConfigPreEmphasis {chassis card port peSetting} {return [miiConfig::preEmphasis $chassis $card $port $peSetting]} +proc ixMiiConfigLossOfSignalThreshold {chassis card port threshold} {return [miiConfig::lossOfSignalThreshold $chassis $card $port $threshold]} +proc ixMiiConfigXgxsLinkMonitoring {chassis card port enable} {return [miiConfig::xgxsLinkMonitoring $chassis $card $port $enable]} +proc ixMiiConfigAlignRxDataClock {chassis card port clock} {return [miiConfig::alignRxDataClock $chassis $card $port $clock]} +proc ixMiiConfigReceiveEqualization {chassis card port value} {return [miiConfig::receiveEqualization $chassis $card $port $value]} +proc ixMiiConfigXauiOutput {chassis card port enable} {return [miiConfig::xauiOutput $chassis $card $port $enable]} +proc ixMiiConfigXauiSerialLoopback {chassis card port enable} {return [miiConfig::xauiSerialLoopback $chassis $card $port $enable]} +proc ixMiiConfigXgmiiParallelLoopback {chassis card port enable} {return [miiConfig::xgmiiParallelLoopback $chassis $card $port $enable]} + + +namespace eval miiConfig { +} + +namespace eval miiaeConfig { +} + + +######################################################################## +# Procedure: preEmphasis +# +# This command configs the pre-emphasis bits for 10ge +# +# Arguments(s): +# chassis +# card +# port +# peSetting +# +######################################################################## +proc miiConfig::preEmphasis {chassis card port peSetting} \ +{ + switch $peSetting { + 75 { + set peSetting $::miiPreemphasis_75 + } + 38 { + set peSetting $::miiPreemphasis_38 + } + 18 { + set peSetting $::miiPreemphasis_18 + } + } + + if {$peSetting > 3 || $peSetting < 0} { + errorMsg "$peSetting not supported for preEmphasis" + set retCode $::TCL_ERROR + return $retCode + } + + set peSetting [expr $peSetting << 14] + + return [setRegister $chassis $card $port $::miiRegister28 0xc000 $peSetting] +} + + +######################################################################## +# Procedure: lossOfSignalThreshold +# +# This command configs the lossOfSignalThreshold bits for 10ge +# +# Arguments(s): +# chassis +# card +# port +# threshold +# +######################################################################## +proc miiConfig::lossOfSignalThreshold {chassis card port threshold} \ +{ + switch $threshold { + 160 { + set threshold $::miiLossOfSignal160mv + } + 240 { + set threshold $::miiLossOfSignal240mv + } + 200 { + set threshold $::miiLossOfSignal200mv + } + 120 { + set threshold $::miiLossOfSignal120mv + } + 80 { + set threshold $::miiLossOfSignal80mv + } + } + + if {$threshold > 4 || $threshold < 0} { + errorMsg "$threshold not supported for lossOfSignalThreshold" + set retCode $::TCL_ERROR + return $retCode + } + + set threshold [expr $threshold << 4] + + return [setRegister $chassis $card $port $::miiRegister29 0x0070 $threshold] +} + + +######################################################################## +# Procedure: xgxsLinkMonitoring +# +# This command configs the xgxsLinkMonitoring bits for 10ge +# +# Arguments(s): +# chassis +# card +# port +# enable <$::TRUE|$::FALSE> +# +######################################################################## +proc miiConfig::xgxsLinkMonitoring {chassis card port enable} \ +{ + switch $enable { + enable - + ENABLE - + true - + TRUE { + set enable $::TRUE + } + enable - + ENABLE - + false - + FALSE { + set enable $::FALSE + } + 1 - + 0 { + } + default { + errorMsg "XgxsLinkMonitoring enable must be bool <1|0>" + set retCode $::TCL_ERROR + return $retCode + } + } + + return [setRegister $chassis $card $port $::miiRegister29 0x0040 $enable] +} + + +######################################################################## +# Procedure: alignRxDataClock +# +# This command configs the alignRxDataClock bits for 10ge +# +# Arguments(s): +# chassis +# card +# port +# clock +# +######################################################################## +proc miiConfig::alignRxDataClock {chassis card port clock} \ +{ + set retCode $::TCL_OK + + switch $clock " + miiRecoveredClock - + $::miiRecoveredClock { + set clock $::miiRecoveredClock + } + miiLocalRefClock - + $::miiLocalRefClock { + set clock $::miiLocalRefClock + } + default { + errorMsg [list Invalid clock $clock selected.] + set retCode $::TCL_ERROR + return $retCode + } + " + return [setRegister $chassis $card $port $::miiRegister24 0x0001 $clock] +} + + +######################################################################## +# Procedure: receiveEqualization +# +# This command configs the receiveEqualization bits for 10ge +# +# Arguments(s): +# chassis +# card +# port +# value +# +######################################################################## +proc miiConfig::receiveEqualization {chassis card port value} \ +{ + if {$value > 15 || $value < 0} { + errorMsg "$value not supported for receiveEqualization" + set retCode $::TCL_ERROR + return $retCode + } + + return [setRegister $chassis $card $port $::miiRegister28 0x000F $value] +} + + +######################################################################## +# Procedure: xauiOutput +# +# This command configs the xauiOutput bits for 10ge +# +# Arguments(s): +# chassis +# card +# port +# enable <$::TRUE|$::FALSE> +# +######################################################################## +proc miiConfig::xauiOutput {chassis card port enable} \ +{ + switch $enable { + 1 - + enable - + ENABLE - + true - + TRUE { + set enable 0xAAAA + } + 0 - + enable - + ENABLE - + false - + FALSE { + set enable 0 + } + default { + errorMsg "XAUI Output enable must be bool <1|0>" + set retCode $::TCL_ERROR + return $retCode + } + } + + return [setRegister $chassis $card $port $::miiRegister30 0xAAAA $enable] +} + + +######################################################################## +# Procedure: xauiSerialLoopback +# +# This command enable/disables the xauiSerialLoopback bits for 10ge +# +# Arguments(s): +# chassis +# card +# port +# enable +# +######################################################################## +proc miiConfig::xauiSerialLoopback {chassis card port enable} \ +{ + set retCode $::TCL_ERROR + + switch $enable { + enable - + ENABLE - + true - + TRUE { + set enable $::TRUE + } + enable - + ENABLE - + false - + FALSE { + set enable $::FALSE + } + 1 - + 0 { + } + default { + errorMsg "XAUISerialLoopback enable must be bool <1|0>" + set retCode $::TCL_ERROR + return $retCode + } + } + + # If Serial loopback selected: + # - set bit 14 of register 0. If it's not selected, clear that bit + # - clear bits 0-3 and set bits 8-11 for register 23. + if {$enable} { + set reg23 0x0F00 + } else { + set reg23 0x0000 + } + + if {[setRegister $chassis $card $port $::miiRegister23 0x0F0F $reg23] == $::TCL_OK} { + set bits [expr $enable << 14] + if {[setRegister $chassis $card $port $::miiControl 0x4000 $bits noGet] == $::TCL_OK} { + set retCode $::TCL_OK + } + } + + return $retCode +} + + +######################################################################## +# Procedure: xgmiiParallelLoopback +# +# This command enable/disables the xgmiiParallelLoopback bits for 10ge +# +# Arguments(s): +# chassis +# card +# port +# loopback +# +######################################################################## +proc miiConfig::xgmiiParallelLoopback {chassis card port enable} \ +{ + switch $enable { + enable - + ENABLE - + true - + TRUE { + set enable $::TRUE + } + enable - + ENABLE - + false - + FALSE { + set enable $::FALSE + } + 1 - + 0 { + } + default { + errorMsg "XGMIIParallelLoopback enable must be bool <1|0>" + set retCode $::TCL_ERROR + return $retCode + } + } + + if {$enable} { + set reg23 0x000F + } else { + set reg23 0x0000 + } + + if {[setRegister $chassis $card $port $::miiControl 0x4000 0x0000] == $::TCL_OK} { + if {[setRegister $chassis $card $port $::miiRegister23 0x0F0F $reg23 noGet] == $::TCL_OK} { + set retCode $::TCL_OK + } + } + return $retCode +} + + +######################################################################## +# Procedure: isXauiSerialLoopback +# +# This command checks the xauiSerialLoopback bits for 10ge +# +# Arguments(s): +# chassis +# card +# port +# +# Return code: +# true if enabled +# +######################################################################## +proc miiConfig::isXauiSerialLoopback {chassis card port} \ +{ + return [expr [getRegister $chassis $card $port $::miiControl] & 0x4000 && \ + [getRegister $chassis $card $port $::miiRegister23 noGet] & 0x0f00] + +} + + +######################################################################## +# Procedure: isXgmiiParallelLoopback +# +# This command checks the isXgmiiParallelLoopback bits for 10ge +# +# Arguments(s): +# chassis +# card +# port +# +# Return code: +# true if enabled +# +######################################################################## +proc miiConfig::isXgmiiParallelLoopback {chassis card port} \ +{ + if {[expr [getRegister $chassis $card $port $::miiRegister23] & 0x000F] == 0x000f} { + set retCode $::true + } else { + set retCode $::false + } + + return $retCode +} + + +######################################################################## +# Procedure: setRegister +# +# This command sets the register for 10ge - PRIVATE +# +# Arguments(s): +# chassis +# card +# port +# register +# bitMask +# get default: true; sometimes you don't want to 'get' first... +# +######################################################################## +proc miiConfig::setRegister {chassis card port register bitMask value {get true}} \ +{ + set retCode $::TCL_OK + + if {$get == "true"} { + if [mii get $chassis $card $port] { + errorMsg "Error getting mii for port [getPortId $chassis $card $port]" + set retCode $::TCL_ERROR + } + } + + if [mii selectRegister $register] { + errorMsg "Error selecting register $register" + set retCode $::TCL_ERROR + } + + set registerValue [format "0x%s" [mii cget -registerValue]] + mii config -registerValue [format %04x [expr {$value & $bitMask} | {$registerValue & ~$bitMask}]] + + if [mii set $chassis $card $port] { + errorMsg "Error setting mii for port [getPortId $chassis $card $port]" + set retCode $::TCL_ERROR + } + + return $retCode +} + + + +######################################################################## +# Procedure: getRegister +# +# This command gets the register for 10ge - PRIVATE +# +# Arguments(s): +# chassis +# card +# port +# register +# get default: true; sometimes you don't want to 'get' first... +# +# Returns: +# value of register as hex number +# +######################################################################## +proc miiConfig::getRegister {chassis card port register {get true}} \ +{ + set value 0x0000 + + if {$get == "true"} { + if [mii get $chassis $card $port] { + errorMsg "Error getting mii for port [getPortId $chassis $card $port]" + set retCode $::TCL_ERROR + } + } + + if [mii selectRegister $register] { + errorMsg "Error selecting register $register" + return $value + } + + return [format "0x%s" [mii cget -registerValue]] +} + +######################################################################## +# Procedure: setRegister +# +# This command sets the register for 10ge - PRIVATE +# +# Arguments(s): +# chassis +# card +# port +# register +# bitMask +# get default: true; sometimes you don't want to 'get' first... +# +######################################################################## +proc miiaeConfig::setRegister { chassis card port mdioIndex deviceAddress register bitMask value } \ +{ + set retCode $::TCL_OK + + + set registerAddress [miiaeConfig::getRegister $chassis $card $port $mdioIndex $deviceAddress $register ] + set registerValue [format "0x%s" [mmdRegister cget -registerValue]] + + + # Since we don't have setRegister command, therefore we have to remove and the same register again + if [mmd delRegister $register] { + errorMsg "Error deleting mmd register $register" + set retCode $::TCL_ERROR + } + + if {[miiae delDevice $deviceAddress]} { + errorMsg "Error setting miiae for port [getPortId $chassis $card $port]" + set retCode $::TCL_ERROR + } + + mmdRegister config -registerValue [format %04x [expr {$value & $bitMask} | {$registerValue & ~$bitMask}]] + + if {[mmd addRegister ]} { + errorMsg "Error adding mmd register $register" + set retCode $::TCL_ERROR + } + + mmd config -address $registerAddress + + if {[miiae addDevice]} { + errorMsg "Error adding miiae device for port [getPortId $chassis $card $port]" + set retCode $::TCL_ERROR + } + + if {[miiae set $chassis $card $port $mdioIndex]} { + errorMsg "Error setting miiae for port [getPortId $chassis $card $port]" + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: getRegister +# +# This command gets the register for 10ge - PRIVATE +# +# Arguments(s): +# chassis +# card +# port +# register +# get default: true; sometimes you don't want to 'get' first... +# +# Returns: +# value of register as hex number +# +######################################################################## +proc miiaeConfig::getRegister {chassis card port mdioIndex deviceAddress register} \ +{ + set value 0x0000 + + if [miiae get $chassis $card $port $mdioIndex] { + errorMsg "Error getting miiae PHY $mdioIndex for port [getPortId $chassis $card $port]" + set retCode $::TCL_ERROR + } + + if [miiae getDevice $deviceAddress] { + errorMsg "Error getting miiae device $deviceAddress" + set retCode $::TCL_ERROR + } + + if [mmd getRegister $register] { + errorMsg "Error getting mmd register $register" + set retCode $::TCL_ERROR + } else { + set value [format "0x%s" [mmd cget -address]] + } + + return $value +} + diff --git a/dep/tclclient/ixTcl1.0/Generic/miscCmds.tcl b/dep/tclclient/ixTcl1.0/Generic/miscCmds.tcl new file mode 100644 index 00000000..3ccb3f9e --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/miscCmds.tcl @@ -0,0 +1,992 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: miscCmds.tcl +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 02-05-1998 DS Genesis +# +# Description: Contains miscellaneous log file commands. +# +################################################################################## + +######################################################################## +# Procedure: logOn +# +# This command wraps the logger command logger on & sets the parameter +# -logFileName to the defaulted file. +# +# Arguments: +# filename - filename, w/out directory, to open +# directory *must* be specified via cget -directory +# +######################################################################## +proc logOn {{file {}}} \ +{ + logger config -logFileName $file + logger on +} + + +######################################################################## +# Procedure: logOff +# +# This command wraps the logger command logger off +# +######################################################################## +proc logOff {} \ +{ + logger off +} + + +######################################################################## +# Procedure: logMsg +# +# Description: This command wraps the logger command logger message +# +# Arguments: args - a list of valid arguments +# +# Results: Returns 0 for ok and 1 for error. WARNING: Cannot use TCL_OK +# and TCL_ERROR at this point. It was failing on certain unix +# and linux combinations +######################################################################## +proc logMsg {args} \ +{ + set retCode 0 + if {[lindex $args 0] == "-nonewline"} { + set args [lreplace $args 0 0] + if {[catch {eval logger::message -nonewline $args} err]} { + set retCode 1 + } + } else { + if {[catch {eval logger::message $args} err]} { + set retCode 1 + } + } + return $retCode +} + + +######################################################################## +# Procedure: errorMsg +# +# This command prints the name of the current proc + a message - intended +# for output of error messages. +# +######################################################################## +proc errorMsg {args} \ +{ + set level [expr [info level] - 1] + if {$level > 0} { + set levelStr "[lindex [info level $level] 0]: " + } else { + set levelStr "Error: " + } + + set verbose 1 + set nonewline 0 + + while {[string index [lindex $args 0] 0] == "-"} { + switch [string range [lindex $args 0] 1 end] { + "verbose" { + set verbose 1 + set args [lreplace $args 0 0] + } + "noVerbose" { + set verbose 0 + set args [lreplace $args 0 0] + } + "nonewline" { + set nonewline 1 + set args [lreplace $args 0 0] + } + default { + break + } + } + } + + if {$verbose} { + if {$nonewline} { + logger message -priority 1 -nonewline $levelStr[join $args " "] + } else { + logger message -priority 1 "$levelStr[join $args " "]" + } + } +} + + +######################################################################## +# Procedure: debugOn +# +# This command enables debug messages & sends them to a file. +# +######################################################################## +proc debugOn {{file {}}} \ +{ + global debug + + set debug(enabled) 1 + if {[string length $file] == 0} { + set debug(file) [file join [logger cget -directory] debugfile.log] + } else { + set debug(file) [file join [logger cget -directory] $file] + } + + if [catch {open $debug(file) w} fileID] { + puts stdout "Cannot open $debug(file): $fileID" + set debug(file) stdout + } else { + set debug(file) $fileID + } + + proc debugMsg {args} \ + { + global debug + + set levelStr "" + set level [expr [info level] - 1] + if {$level > 0} { + set levelStr "[lindex [info level $level] 0]: " + } + + puts $debug(file) $levelStr[join $args " "] + } +} + + +######################################################################## +# Procedure: debugOff +# +# This command disables debug messages & sets the proc debugMsg to empty +# +######################################################################## +proc debugOff {} \ +{ + global debug + if [info exists debug(enabled)] { + unset debug(enabled) + flush $debug(file) + if {$debug(file) != "stdout"} { + close $debug(file) + } + } + proc debugMsg {args} \ + { + } +} + + + +######################################################################## +# Procedure: debugMsg +# +# If debugging is enabled, prints a message to the debug file. +# +######################################################################## +proc debugMsg {args} \ +{ +} + + +################################################################################## +# Procedure: showCmd +# +# This command lists all available cget options & their current values +# +# Argument(s): +# cmd name of command to show +# +################################################################################## +proc showCmd {cmd {method cget}} \ +{ + catch {$cmd $method} paramList + foreach param [lsort [join $paramList]] { + if {$param == "-this"} { + continue + } + if {[string index $param 0] == "-"} { + logMsg "$cmd $method $param: [$cmd cget $param]" + } + } +} + + +######################################################################## +# Procedure: openMyFile +# +# Description: This command opens a file set by a command, w/backup if required. +# +# The global array openedFileName contains the full name of opened files indexed +# by their channels. This information will be used to set the permission of the file +# back to both readable and writable when its channel is closed by closeMyFile. +# +# Arguments: filename - filename, w/out directory, to open directory *must* be specified via cget -directory +# testCmd - test command name, ie., logger or results +# +# Returns: fileID if successful, or "" if unsuccessful +######################################################################## +proc openMyFile {filename {fileAccess w} {command logger}} \ +{ + global openedFileName + + set fileID stdout + set fullFileName [file join [$command cget -directory] $filename] + + if {[$command cget -fileBackup] == "true" && $fileAccess == "w"} { + # if the log file by this name exists, then copy it into a different name + # with a magic number that increments every time the test is run + if {[file exists $fullFileName]} { + set filePrefix [file rootname [file tail $fullFileName]] + set magicNum [clock format [file mtime $fullFileName] -format "%H%M%S"] + + # put the full path for new file name + set newFileName [format "%s%s%s" $filePrefix $magicNum [file extension $filename]] + set newFullFileName [file join [$command cget -directory] $newFileName] + if [catch {file rename -force $fullFileName $newFullFileName} res] { + errorMsg "$res" + } + } + } + + set fileID [ixFileUtils::open $fullFileName $fileAccess] + if {$fileID == ""} { + set fileID stdout + } else { + set openedFileName($fileID) $fullFileName + } + + return $fileID +} + + +######################################################################## +# Procedure: closeMyFile +# +# Description: This command closes a file that is opened by the command openMyFile. +# +# Arguments: fileId - channel id of the file to be closed +# +# Returns: None +######################################################################## +proc closeMyFile {fileId} \ +{ + global openedFileName + + if {$fileId == "stdout"} { + return + } + + ixFileUtils::close $fileId + + if { [info exists openedFileName($fileId)] } { + catch {unset openedFileName($fileId)} + } +} + + +######################################################################## +# Procedure: callTraceMsg +# +# This command prints a trace of the procs +# +######################################################################## +proc callTraceMsg {{arg ""}} \ +{ + errorMsg $arg + for {set x [expr [info level]-1]} {$x > 0} {incr x -1} { + ixPuts "$x: [info level $x]" + } +} + + +######################################################################## +# Procedure: traceVariableMsg +# +# This command traces +# +######################################################################## +proc traceVariableMsg {name1 name2 op} \ +{ + upvar $name1 Name1 + errorMsg [format "variable = %s, value = %s" $name1 $Name1] +} + + +################################################################################## +# Procedure: isDigit +# +# This command checks to see if it is digit. +# +# Argument(s): +# +################################################################################## +proc isDigit {arg} \ +{ + set retCode 1 + + if [catch {format %d $arg}] { + set retCode 0 + } + return $retCode +} + + +################################################################################## +# Procedure: isNegative +# +# This command checks to see if it is negative - (needed for >32 bit numbers) +# +# Argument(s): +# +################################################################################## +proc isNegative {arg} \ +{ + set retCode 0 + + if { [regexp {^-[0-9]+$} $arg] } { + set retCode 1 + } + + return $retCode +} + + +################################################################################## +# Procedure: isValidExponentialFloat +# +# Description: Tells if a value is a legal exponential float. +# +# Argument(s): valueToCheck - the value to check. +# +# Returns: true if valid, false otherwise +################################################################################## +proc isValidExponentialFloat { valueToCheck } \ +{ + if {($valueToCheck == "+") || ($valueToCheck == "-")} { + # Do not allow just these symbols. Regexp will not catch them. + set retCode $::false + } else { + # First check for the one digit - a lot clearer than putting alternates in one big pattern + # Whole digit + set wholeDigit {^[+-]?[0-9]+} + # No whole digit but fraction digit + set fraction {^[+-]?([\.][0-9]+)+} + if {!([regexp $wholeDigit $valueToCheck] || [regexp $fraction $valueToCheck])} { + set retCode $::false + } else { + # The expression for a legal exponential float means: An optional +/- + # followed by zero or more digits followed (the fractional part is + # optional) by decimal followed by one or more digits followed by + # optional exponential expression which consists of e/E followed by + # optional +/- followed by an integer. + set validFloat {^[+-]?[0-9]*([\.][0-9]+)?([eE][+-]?[0-9]+)?$} + if {[regexp $validFloat $valueToCheck]} { + set retCode $::true + } else { + set retCode $::false + } + } + } + return $retCode +} + + +################################################################################## +# Procedure: isValidPositiveExponentialFloat +# +# Description: Tells if a given value is a legal positive exponential float +# +# Argument(s): valueToCheck - the input value to check +# +# Returns: true if valid, false otherwise +################################################################################## +proc isValidPositiveExponentialFloat { valueToCheck } \ +{ + if {[isValidExponentialFloat $valueToCheck] && [expr $valueToCheck > 0]} { + set retCode $::true + } else { + set retCode $::false + } + return $retCode +} + + +################################################################################## +# Procedure: isValidPartialFloat +# +# Description: Tells if a given value is a legal partial float. +# +# Argument(s): valueToCheck - the input value to check +# +# Returns: true if valid, false otherwise +################################################################################## +proc isValidPartialFloat { valueToCheck } \ +{ + # The expression for a legal partial float means: + # An optional +/- followed by digits. Followed by optional decimal and + # more digits. Followed by pieces of the optional exponential expression + # The key difference between partial and full is that partial uses * + # after 0-9 to show it can have 0 or more values. The values number + # must have 1 or more that is why it uses the +. + set validPartialFloat {^[+-]?[0-9]*([\.][0-9]*)?([eE][+-]?[0-9]*)?$} + if {[regexp $validPartialFloat $valueToCheck]} { + set retCode $::true + } else { + set retCode $::false + } + return $retCode +} + + +################################################################################## +# Procedure: isValidPositivePartialFloat +# +# Description: Tells if a given value is a legal positive partial float. +# +# Argument(s): valueToCheck - the input value to check +# +# Returns: true if valid, false otherwise +################################################################################## +proc isValidPositivePartialFloat { valueToCheck } \ +{ + set validPositivePartialFloat {^[+]?[0-9]*([\.][0-9]*)?([eE][+-]?[0-9]*)?$} + if {[regexp $validPositivePartialFloat $valueToCheck]} { + set retCode $::true + } else { + set retCode $::false + } + return $retCode +} + + +################################################################################## +# Procedure: isValidPositiveFloat +# +# Description: Tells if a given number is a legal positive float. +# +# Argument(s): valueToCheck - the input value to check +# +# Returns: true if valid, false otherwise +################################################################################## +proc isValidPositiveFloat { valueToCheck } \ +{ + # Special case to handle the singular plus and period + if {($valueToCheck == "+") || ($valueToCheck == ".")} { + set retCode $::false + } else { + # The expression for a legal float is an optional + followed by either + # (zero or more digits followed by decimal followed by one or more digits) + # or one or more digits. + set validPositiveFloat {^[+]?[0-9]+([\.]?[0-9]+)?$} + if {[regexp $validPositiveFloat $valueToCheck]} { + set retCode $::true + } else { + set retCode $::false + } + } + return $retCode +} + + +################################################################################## +# Procedure: isValidInteger +# +# Description: Tell if a given number is a legal integer +# +# Argument(s): valueToCheck - the input value to check +# +# Returns: true if valid, false otherwise +################################################################################## +proc isValidInteger { valueToCheck } \ +{ + # The expression for a legal integer is an optional +/- followed by one or more digits. + set validInteger {^[+-]?[0-9]+$} + if {[regexp $validInteger $valueToCheck]} { + set retCode $::true + } else { + set retCode $::false + } + return $retCode +} + + +################################################################################## +# Procedure: isValidPositiveInteger +# +# Description: Tell if a given number is a legal non-zero positive integer +# +# Argument(s): valueToCheck - the input value to check +# +# Returns: true if valid, false otherwise +################################################################################## +proc isValidPositiveInteger { valueToCheck } \ +{ + set validPositiveInteger {^[+]?[0-9]+([eE][+-]?[0-9]+)?$} + # If valueToCheck is zero, special case failure + if {$int == "0"} { + set retCode $::false + } else { + if {[regexp $validPositiveInteger $valueToCheck]} { + set retCode $::true + } else { + set retCode $::false + } + } + return $retCode +} + + + +################################################################################## +# Procedure: getProcList +# +# This command opens a Tcl file & prints all procs that live in that file. +# +# Argument(s): +# fileName file name +# sortOn sort either by proc name or line number (default is proc name) +# +################################################################################## +proc getProcList {fileName {sortOn procName} {ProcList ""} {verbose verbose}} \ +{ + set retCode 0 + + upvar $ProcList procList + + set cmd "proc " + if [catch {open $fileName r} fid] { + errorMsg "Cannot open file $fileName" + set retCode 1 + } else { + + if {$verbose == "verbose"} { + ixPuts "FILE: $fileName" + ixPuts "============================================================================" + } + + if [info exists procList] { + unset procList + } + + set linenum 1 + while {![eof $fid]} { + gets $fid originalLine + + if {[string first $cmd $originalLine] == 0} { + lappend procList [list $linenum [string range [string trim $originalLine \\] 5 end]] + } + incr linenum + } + close $fid + + if {$sortOn == "procName"} { + set procList [lsort -index 1 $procList] + } else { + set procList [lsort -index 0 -integer $procList] + } + + if {$verbose == "verbose"} { + foreach item $procList { + scan $item "%s %s" linenum name + ixPuts [format "Line: %4d ::::\t%s" $linenum [join [lrange $item 1 end]]] + } + } + } + + return $retCode +} + + +################################################################################## +# Procedure: getTxBasedOnRx +# +# This command returns the tx port based on rx port from TxRxArray +# +# Argument(s): +# TxRxArray - map, ie. one2oneArray +# +################################################################################## +proc getTxBasedOnRx {TxRxArray c l p } \ +{ + upvar $TxRxArray txRxArray + + set breakFlag 0 + + foreach txMap [lnumsort [array names txRxArray]] { + scan $txMap "%d,%d,%d" tx_c tx_l tx_p + + foreach rxMap $txRxArray($tx_c,$tx_l,$tx_p) { + scan $rxMap "%d %d %d" rx_c rx_l rx_p + + if { [list $rx_c $rx_l $rx_p] == [list $c $l $p]} { + set txPort [list $tx_c $tx_l $tx_p] + set breakFlag 1 + break + } + } + if {$breakFlag ==1} { + break + } + } + return $txPort +} + +################################################################################## +# Procedure: convertFromSeconds +# +# This command formats the duration in hour:minute:seconds format. +# +# Argument(s): +# duration duration of the test +# +################################################################################## +proc convertFromSeconds {time Hours Minutes Seconds} \ +{ + set retCode $::TCL_OK + + upvar $Hours hours + upvar $Minutes minutes + upvar $Seconds seconds + + set hours [expr {$time / 3600}] + + set minuteTemp [expr {$time - $hours * 3600}] + set minutes [expr {$minuteTemp / 60}] + + set seconds [expr {$minuteTemp - $minutes * 60}] + + return $retCode +} + + +############################################################################### +# Procedure: convertToSeconds +# +# Description: Convert hours:min:seconds to seconds +# +# Arguments: hours +# minutes +# seconds +# +# Returns: time, in seconds +# +############################################################################### +proc convertToSeconds {hours minutes seconds} \ +{ + if {[scan $hours "%d" x] <= 0} { + set hours 0 + } + if {[scan $minutes "%d" x] <= 0} { + set minutes 0 + } + if {[scan $seconds "%d" x] <= 0} { + set seconds 0 + } + + return [expr ((($hours*60) + $minutes) * 60) + $seconds] +} + + + +################################################################################## +# Procedure: formatDurationTime +# +# This command formats the duration in hour:minute:seconds format. +# +# Argument(s): +# duration duration of the test +# +################################################################################## +proc formatDurationTime {duration} \ +{ + convertFromSeconds $duration hour minute seconds + return [format "%.2d:%.2d:%.2d" $hour $minute $seconds] +} + + +################################################################################## +# Procedure: formatNumber +# +# This command formats the number based on the format given +# +# Argument(s): +# number number to be formated +# +################################################################################## +proc formatNumber {number formatTemplete} \ +{ + scan $formatTemplete "%d.%d" intDigits decDigits + set base [expr pow(10,$decDigits)] + + if {$number > 0.00 && $number < [expr 1/$base]} { + set number [expr ceil($number * $base)/$base] + } else { + set newFormat [format "%d.%df" $intDigits $decDigits] + set number [format "%$newFormat" $number] + } + + return $number + +} + +################################################################################## +# Procedure: unixCludgeGetExpr +# +# This command reroutes mpexpr command. (Note: for UNIX) +# +# Argument(s): +# duration duration of the test +# +################################################################################## +proc unixCludgeGetExpr {} \ +{ + set expr mpexpr + + if {[lsearch [join [info loaded]] Mpexpr] < 0} { + set expr expr + } + + return $expr +} + + +################################################################################## +# Procedure: useProfile +# +# This command get the profiles +# +# Argument(s): +# use "true" for using profile and "false" for not using it. It is set to +# "false" so whenever you want to use it set it to "true". +# +################################################################################## +proc useProfile {use} \ +{ + if { $use == "true" } \ + { + if {[info tclversion] > 8.0} { + proc ixInitProfiler {} \ + { + global currTime + set currTime [clock clicks -milliseconds] + } + proc profiler {args} \ + { + global currTime + + set now [clock clicks -milliseconds] + set diff [expr $now - $currTime] + set currTime $now + + return $diff + } + + } else { + global halCommands + + if {[lsearch $halCommands profiler] == -1} { + lappend halCommands profiler + } + + proc ixInitProfiler {} \ + { + if {[TCLProfile profiler] == ""} { + puts "Error instantiating TCLProfile object!" + return 1 + } + } + } + catch {ixInitProfiler} + proc ixProfile {args} \ + { # take an optional comment string + set commentStr "" + if {[llength $args] > 0} { + set commentStr "[lindex $args 0]" + } + set levelStr "" + set level [expr [info level] - 1] + if {$level > 0} { + set levelStr "[lindex [info level $level] 0]: " + } + logMsg "$levelStr: [profiler getDiffTime] $commentStr" + } + } else { + proc ixInitProfiler {} {} + proc ixProfile {args} {} + proc profiler {{arg ""}} {} + } +} + +######################################################################################## +# Procedure: CountGlobalMemory +# +# Description: Counts the memory usage of a global variable or array, or all global +# variables and arrays. +# +# Tcl arrays are stored as hash tables, the indices are stored +# as strings. The overhead in the hash tables is not easily +# accounted for, thus it is not accounted for in this procedure. +# +# The following is a quote from www.scriptic.com/bboard +# +# "...Each array entry will have an overhead of 16 bytes (on a 32-bit +# machine) over the size of the data due to the code that manages the +# hashtable on which the array is based, another 32 bytes due to the +# fact that it is a Tcl array entry and is not just a simple mapping +# between strings, and another 24 bytes for each unique value in the +# array. (There is also a small overhead due to the overall array and +# hashtable management themselves, but this probably doesn't matter +# since it is roughly invariant with the size of the table; the only +# non-constant part is the size of the C array holding the addresses +# of the bucket chains, but that's generally going to average at less +# than a byte per entry.) This gives you an overhead of 72 bytes per +# entry assuming a worst case of all values being different, which is +# likely to be the case with your code since Tcl doesn't work incredibly +# hard at merging values which look the same (if it did, the cost of doing +# this would be prohibitive.)" +# +# Input: memory: array name, wildcard characters are usuable (gxi*) +# default is all globals. +# +# Output: byte count +# +# +######################################################################################## +proc CountGlobalMemory {{memory ""}} \ +{ + + # Hash table overhead is 16 bytes per entry + set hashTableOverhead 16 + + # Tcl Array mapping overhead is 32 bytes per entry + set arrayMappingOverhead 32 + + # Unique value overhead is 24 bytes per entry + set uniqueValueOverhead 24 + + # Overall Overhead per Entry + set TclOverhead \ + [expr $hashTableOverhead + $arrayMappingOverhead + $uniqueValueOverhead] + + set arrayCounter 0 + set arraySizeCounter 0 + set arrayIndexSizeCounter 0 + + if {[string length $memory] == 0} { + set command [list info globals] + } else { + set command [list info globals $memory] + } + + foreach i [lsort [eval $command]] { + global $i + if [array exists $i] { + incr arrayCounter [array size $i] + foreach j [array names $i] { + incr arraySizeCounter [string length ${i}($j)] + incr arrayIndexSizeCounter [string length $j] + } + + } else { + incr arraySizeCounter [string length ${i}] + } + } + + set totalOverhead [expr $TclOverhead * $arrayCounter] + +# errorMsg "Array Elements: $arrayCounter, \ +# Array Element Overhead: $totalOverhead \ +# Array Size: $arraySizeCounter, \ +# Array Index Overhead: $arrayIndexSizeCounter" + + + return [expr $arraySizeCounter + $arrayIndexSizeCounter + $totalOverhead] + +} + + +######################################################################################## +# Procedure: createNamedFont +# +# Description: Create a font with the given name, font family, and size. +# If a font exists with the same name, set it to the given font family +# and size. +# +# Input: name: font name +# family: name of the font family +# size: size of the font +# +# Output: nothing +# +######################################################################################## +proc createNamedFont { name family size } \ +{ + if {[lsearch [font names] $name] == -1} { + font create $name -family $family -size $size + } else { + font config $name -family $family -size $size + } +} + + +######################################################################################## +# Procedure: buildFileName +# +# Description: Builds a new file name by appending the type to the main fileName. This +# is used mostly for the tests that either generate mutiple results files +# +# +# Input: fileName: filename without the ".results" +# type : type, ie. runtype, errorType etc.. +# +# Output: The new filename +# +######################################################################################## +proc buildFileName { fileName type} \ +{ + if { [regexp {\.} $fileName] == 1} { + + set firstPart [string range $fileName 0 [expr [string last . $fileName] -1] ] + set lastPart [string range $fileName [string last . $fileName] end ] + unset fileName + append fileName $firstPart _$type $lastPart + } else { + append fileName _$type + } + + return $fileName +} + diff --git a/dep/tclclient/ixTcl1.0/Generic/multiUser.tcl b/dep/tclclient/ixTcl1.0/Generic/multiUser.tcl new file mode 100644 index 00000000..d7335a00 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/multiUser.tcl @@ -0,0 +1,402 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: multiUser.tcl +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 03-01-1998 HS Genesis +# +# Description: This file contains commands related to multi-user scripts +# to run. +# +######################################################################## + +################################################################################## +# Procedure: ixSource +# +# This command sources a list of files passed an an argument or the files under +# the directory names passed as an argument. If a list of files is passed, then the +# full path of the file must be specified. +# +# Argument(s): +# dirFileName directory name or list of files to be sourced +# +################################################################################## +proc ixSource {dirFileName} \ +{ + if [file isdirectory $dirFileName] { + # if there are .tcl files here, source them first + if {![catch {glob -nocomplain -- [file join $dirFileName *.tcl]}] != -1} { + foreach filename [glob -nocomplain [file join $dirFileName *.tcl]] { + if [catch {source $filename} srcError] { + ixPuts "*** $filename: $srcError" + } + } + } + sourceRecursively $dirFileName + } else { + # else dirFileName is a list of files, so source them + foreach filename $dirFileName { + if [catch {source $filename} srcError] { + ixPuts "*** $filename: $srcError" + } + } + } +} + +################################################################################## +# Procedure: sourceRecursively +# +# This command sources all .tcl files in directories recursively. +# +# Argument(s): +# dirName directory name under which files to be sourced +# +################################################################################## +proc sourceRecursively {dirName} \ +{ + foreach currDir [glob -nocomplain [file join $dirName *]] { + if [file isdirectory $currDir] { + foreach filename [glob -nocomplain [file join $currDir *.tcl]] { + if [catch {source $filename} srcError] { + ixPuts "*** $filename: $srcError" + } + } + sourceRecursively [file join $dirName $currDir] + } + } +} + + +######################################################################## +# Procedure: ixLogin +# +# This command logs in a user. +# +# Argument(s): +# userName - name of the user to login +# +######################################################################## +proc ixLogin {userName} \ +{ + session login $userName +} + +######################################################################## +# Procedure: ixLogout +# +# This command logs out the current user. +# +# Argument(s): +# None +# +######################################################################## +proc ixLogout {} \ +{ + session logout +} + +######################################################################## +# Procedure: ixTakeOwnership +# +# This command attempts to take ownership of all the ports in the list +# +# Argument(s): +# txRxList - list of ports to take ownership +# takeType - if "force" take regardless of whether +# the port is previously owned by someone else +# +######################################################################## +proc ixTakeOwnership {txRxList {takeType ""}} \ +{ + set retCode 0 + + set portList $txRxList + set removedPorts {} + + foreach port $txRxList { + + scan $port "%d %d %d" c l p + + + if {[lsearch -exact $port "*"] != -1} { + set portList [ixCreatePortListWildCard $txRxList] + break + } else { + # in case we pass the name of the txRxList, which causes ixTakeOwnership + # misbehaves after error condition, so we check if the parm passed is + # really the value of the port list + if [catch {format "%d %d %d" $c $l $p} port] { + errorMsg "Error creating port list" + return 1 + } + } + } + + if {[info exists takeType] && $takeType == "force"} { + if [issuePortGroupCommand takeOwnershipForced portList] { + errorMsg "Error forcing ownership on requested ports" + set retCode 1 + } + } else { + foreach portItem $portList { + scan $portItem "%d %d %d" c l p + + if [canUse $c $l $p] { + set index [lsearch $portList [list $c $l $p]] + if {$index >= 0} { + logMsg "Port [getPortId $c $l $p] is not available, removing port from the list." + lappend removedPorts [list $c $l $p] + set portList [lreplace $portList $index $index] + } + continue + } + } + if [llength $portList] { + if [issuePortGroupCommand takeOwnership portList] { + errorMsg "Error taking ownership on requested ports" + set retCode 1 + } + } else { + logMsg "No available ports to take ownership." + set retCode 1 + } + } + + if {$retCode == 0} { + logMsg "Took ownership of following ports:" + logMsg "$portList" + } + + if [llength $removedPorts] { + errorMsg "Error taking ownership of ports:" + logMsg $removedPorts + } + + return $retCode +} + +######################################################################## +# Procedure: ixPortTakeOwnership +# +# This command attempts to take ownership of this port +# +# Arguments(s): +# chassis +# lm +# port +# takeType - if "force" take regardless of whether +# the port is previously owned by someone else +# +######################################################################## +proc ixPortTakeOwnership {chassis lm port {takeType ""}} \ +{ + set portList [list [list $chassis $lm $port]] + return [ixTakeOwnership $portList $takeType] +} + + +######################################################################## +# Procedure: ixClearOwnership +# +# This command clears ownership of all the ports in the list +# +# Argument(s): +# txRxList - list of ports to take ownership +# takeType - if "force" take regardless of whether +# the port is previously owned by someone else +# +######################################################################## +proc ixClearOwnership {{txRxList "" } {takeType ""}} \ +{ + set retCode 0 + + if {$txRxList == ""} { + set retCode [clearAllMyOwnership] + } else { + set portList [ixCreatePortListWildCard $txRxList] + if {[info exists takeType] && $takeType == "force"} { + if [issuePortGroupCommand clearOwnershipForced portList] { + errorMsg "Error forcing clear ownership on requested ports" + set retCode 1 + } + } else { + set otherPortList {} + if {[llength $portList] == 0} { + errorMsg "Error clear ownership on requested ports" + set retCode 1 + } + foreach port $portList { + scan $port "%d %d %d" c l p + if {![isMine $c $l $p]} { + lappend otherPortList $port + set index [lsearch $portList $port] + set portList [lreplace $portList $index $index] + } + } + if {[llength $portList] > 0} { + if [issuePortGroupCommand clearOwnership portList] { + errorMsg "Error clearing ownership on requested ports" + set retCode 1 + } + } + if {[llength $otherPortList] > 0} { + errorMsg "Error clearing ownership of ports owned by other user(s):" + logMsg $otherPortList + set retCode 1 + } + } + } + return $retCode +} + + +######################################################################## +# Procedure: ixPortClearOwnership +# +# This command clears ownership of this port +# +# Arguments(s): +# chassis +# lm +# port +# takeType - if "force" take regardless of whether +# the port is previously owned by someone else +# +######################################################################## +proc ixPortClearOwnership {chassis lm port {takeType ""}} \ +{ + set portList [list [list $chassis $lm $port]] + return [ixClearOwnership $portList $takeType] +} + + +######################################################################## +# Procedure: ixCheckOwnership +# +# This command checks ownership of all the ports in the list +# +# Argument(s): +# TxRxList - list of ports to take ownership +# +######################################################################## +proc ixCheckOwnership {txRxList} \ +{ + set retCode 0 + + set portList [ixCreatePortListWildCard $txRxList] + + foreach myport $portList { + scan $myport "%d %d %d" c l p + + set retCode [canUse $c $l $p] + if {$retCode != 0} { + return $retCode + } + } + + return $retCode +} + + +######################################################################## +# Procedure: canUse +# +# This command checks to see if the port can be used (ie., if it's not +# owned by someone else) +# +# Argument(s): +# c - chassis +# l - card +# p - port +# +# NOTE: logMsg is used here rather than ixPuts so that if logs are +# turned on, the canUse message *will* be saved into the test log. +# +######################################################################## +proc canUse {c l p} \ +{ + set retCode $::TCL_OK + + if {![port canUse $c $l $p]} { + set retCode $::ixTcl_notAvailable + } + + return $retCode +} + + +######################################################################## +# Procedure: isMine +# +# This command checks to see if the currently logged in user owns this +# port. +# +# Argument(s): +# c - chassis +# l - card +# p - port +# +# Returns : +# 0 - the port is owned by other user +# 1 - the ixLogin user name matches the ownership owner +# 2 - the port does not belong to any user +# +######################################################################## +proc isMine {chassis card port} \ +{ + set isMine 0 + + global ixTcl_notAvailable + + if [port get $chassis $card $port] { + errorMsg "Error getting port $chassis $card $port" + set retCode 1 + } else { + set owner [port cget -owner] + if {[string tolower $owner] == [string tolower [session cget -userName]]} { + set isMine 1 ;# this is my port + } elseif {[string tolower $owner] == ""} { + set isMine 2 ;# this is unowned port + } else { + set isMine 0 ;# this is owned by other + } + } + + return $isMine +} + + + + diff --git a/dep/tclclient/ixTcl1.0/Generic/packetBuilder.tcl b/dep/tclclient/ixTcl1.0/Generic/packetBuilder.tcl new file mode 100644 index 00000000..9679b4ea --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/packetBuilder.tcl @@ -0,0 +1,922 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: packetBuilder.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 02-19-1998 DS Genesis +# +# Description: This file contains common utility functions used to +# build the data packets for various protocols. +# +######################################################################## + + + +#################################################################### +# Utility procs used in building packets +#################################################################### + + + +######################################################################## +# Procedure: calculateChecksum +# +# This command calculates a checksum for the data argument +# +# Argument(s): +# data data to calculate checksum for +# +######################################################################### +proc calculateChecksum {data} \ +{ + set checksum 0 + + foreach {hi low} $data \ + { + set word [format 0x%02s%02s $hi $low] + set compliment $word + set checksum [expr $checksum + $compliment] + } + + set checksum [expr (($checksum >> 16) + ($checksum & 0xffff))] + set checksum [expr ~($checksum + ($checksum >> 16))] + + return [long2octet $checksum] +} + + +######################################################################## +# Procedure: oid2octet +# +# This command converts an oid into an octet list for use in an SNMP +# packet. +# +# Argument(s): +# oid oid to convert +# +######################################################################### +proc oid2octet {oid} \ +{ + set myOid [oid2dot $oid] ;# just in case, doesn't hurt + while {([string length $myOid] > 0)} \ + { + set dot [string first "." $myOid] + if {($dot < 0)} \ + { + lappend octet [format %02x $myOid] + break; + } \ + else \ + { + lappend octet [format %02x [string range $myOid 0 [expr $dot-1]]] + set myOid [string range $myOid [expr $dot+1] end] + } + } + set octet [join [lreplace $octet 0 1 2b]] + return $octet +} + + +######################################################################## +# Procedure: buildLLCHeader +# +# This command builds the LLC header for an 802.2 Ethernet frame. +# +# Argument(s): +# +######################################################################### +proc buildLLCHeader {} \ +{ + set DSAP e0 + set SSAP e0 + set control 03 + + set llcHeader [concat $DSAP $SSAP $control] + + return $llcHeader +} + + +######################################################################## +# Procedure: buildIpHeader +# +# This command builds the IP header for an IP frame including +# checksum calculation. +# +# Argument(s): +# sourceIP IP address of source, in octet format +# destinationIP IP address of destination, in octet format +# dataLength Length of the data only if UDP; else length of +# all data except IP header. +# TTL time to live +# protocol Protocol for IP header, ie, 1 = ICMP, 11 - UDP, etc +# flags Flags for IP header +# options 32-bit options + padding, not required +# +######################################################################### +proc buildIpHeader {sourceIP destinationIP \ + dataLength TOS TTL protocol flags options} \ +{ + set ipType {08 00} + set ipVersion 45 + set id {00 00} + set headerChecksum {00 00} + set TTL [format %02x $TTL] + set TOS [format %02x $TOS] + + if {($protocol == 11)} \ + { + set totalLength [expr $dataLength + $::kHeaderLength(udp) + $::kHeaderLength(ip)] + } \ + else \ + { + set totalLength $dataLength + } + + if {[llength $options] != 0} { + for {set i [llength $options]} {$i < 4} {incr i} { + set options [concat $options 00] + } + set ipVersion 46 + } + + set totalLength [expr $totalLength + [llength $options]] + + set ipHeader [concat $ipVersion $TOS [long2octet $totalLength] $id \ + $flags $TTL $protocol $headerChecksum \ + $sourceIP $destinationIP] + + if {[llength $options] > 0} { + set ipHeader [concat $ipHeader $options] + } + + set headerChecksum [calculateChecksum $ipHeader] + set ipHeader [concat $ipType [join [lreplace $ipHeader 10 11 $headerChecksum]]] + + return $ipHeader +} + +######################################################################## +# Procedure: buildRipBlock +# +# This command builds one block for a RIP packet +# +# Argument(s): +# ipAddress IP address to use in RIP packet +# Metric metric value to use in RIP packet +# +# Note: +# ipAddress should be passed in as a string of the format: +# "192.42.172.0" (class C) or "192.42.0.0" (class B) etc. +# Metric should be passed in as an integer, 1-15 or 16 for unreachable. +# (ref. rfc1058) +######################################################################### +proc buildRipBlock {ipAddress Metric {familyId "00 02"}} \ +{ + if {($Metric > 16)} \ + { + puts "Invalid metric value. Must be between 1-16." + return + } + + set Metric [format %02x $Metric] + + return [concat $familyId {00 00} \ + [host2addr $ipAddress] \ + {00 00 00 00 00 00 00 00} \ + {00 00 00} $Metric] +} + +######################################################################## +# Procedure: buildVidHeader +# +# This procedure builds the VID of a vlan-tagged frame. +# +# Argument(s): +# userPriority - user priority field of the TCI, eight priority levels, 0-7 +# CFIformat - Canonical Format Indicator - if set to true, all MAC +# addresses in frame is in Canonical format +# VID - twelve-bit VLAN identifier that uniquely identifies +# the VLAN to which the frame belongs. +# +######################################################################### +proc buildVidHeader {userPriority CFIformat VID} \ +{ + set tpid {81 00} + + if {$userPriority > 7 || $userPriority < 0} { + logMsg "Invalid userPriority value. Parameter set to 0." + set userPriority 0 + set retCode 1 + } + + switch $CFIformat { + true { + set CFIformat 1 + } + false { + set CFIformat 0 + } + default { + logMsg "Invalid CFIformat value. Parameter set to false." + set CFIformat 0 + set retCode 1 + } + } + + if {$VID > 0xfff || $VID < 0} { + logMsg "Invalid VID value. Parameter set to 0x00." + set VID 0 + set retCode 1 + } + + set tci [long2octet [expr ((($userPriority << 1) + $CFIformat) << 12) + $VID]] + + return [concat $tpid $tci] +} + + +#################################################################### +# Packet building procs +#################################################################### + +######################################################################## +# Procedure: buildIPXPacket +# +# This procedure builds the header and data portion of an IPX packet +# +# Argument(s): +# packetType type of packet (1 byte) +# 0x00 - unknown packet type +# 0x01 - routing information packet (RIP) +# 0x04 - service advertising packet (SAP) +# 0x05 - sequenced packet +# 0x11 - netware core protocol packet +# 0x14 - propagated packet +# destNetwork destination network (4 bytes) +# destNode destination node (6 bytes) +# destSocket destination socket (2 bytes) +# 0x451 - NetWare core protocol +# 0x452 - Service advertising packet +# 0x453 - Routing information packet +# 0x455 - NetBIOS packet +# 0x456 - Diagnostic packet +# 0x457 - Serialization packet +# 0x4000 - 0x7fff - dynamic sockets +# 0x8000 - 0xffff - well-known sockets; assigned by Novell +# sourceNetwork source network (4 bytes) +# sourceNode source node (6 bytes) +# sourceSocket source socket (2 bytes, see destSocket for description) +# dataLength length of the data +# data data to follow IPX header (length = dataLength bytes) +# +######################################################################### +proc buildIPXPacket {packetType destNetwork destNode destSocket \ + sourceNetwork sourceNode sourceSocket dataLength data} \ +{ + set IPXtype {81 37} + set checksum {ff ff} + set transportControl 00 + + if {[llength $destSocket] == 1} { + set destSocket [concat [format %02x [expr (($destSocket >> 8) & 0xff)]] \ + [format %02x [expr $destSocket & 0xff]]] + } + + if {[llength $sourceSocket] == 1} { + set sourceSocket [concat [format %02x [expr (($sourceSocket >> 8) & 0xff)]] \ + [format %02x [expr $sourceSocket & 0xff]]] + } + + set packetType [format %02x $packetType] + + set packetLength [expr $::kIPXHeaderLength + $dataLength] + + set length [concat [format %02x [expr (($packetLength >> 8) & 0xff)]] \ + [format %02x [expr $packetLength & 0xff]]] + + set IPXheader [concat $checksum $length $transportControl $packetType \ + $destNetwork $destNode $destSocket \ + $sourceNetwork $sourceNode $sourceSocket] + set IPXpacket [concat $IPXheader $data] + + if {[protocol cget -ethernetType] == 1} { + set ipxFrame [concat $IPXtype $IPXpacket] + } else { + set enLength [llength $IPXpacket] + set length [concat [format %02x [expr (($enLength >> 8) & 0xff)]] \ + [format %02x [expr $enLength & 0xff]]] + set ipxFrame [concat $length $IPXpacket] + } + + return $ipxFrame +} + +######################################################################## +# Procedure: buildIPXData +# +# This procedure builds the data portion of an IPX packet +######################################################################## +proc buildIPXData {frameSize} \ +{ + set ipxdataList {} + set currLen 0 + set typeLen 2 + set dasaLen 12 + set crcLen 4 + + while {$currLen < [expr $frameSize - $::kIPXHeaderLength - $typeLen - $dasaLen - $crcLen]} { + set ipxdataList [lappend ipxdataList "B8"] + incr currLen + } + return $ipxdataList +} + +######################################################################## +# Procedure: buildServerEntry +# +# This procedure builds one server entry (64 bytes) for a SAP packet, +# For a SAP request, only the service type field will be used to build +# the entry. +# +# Argument(s): +# serverType - type of server (2 bytes) +# serverName - server name (SAP response only, 48 bytes) +# netAddress - network address on which server resides (4 bytes) +# nodeAddress - address of the node on which server resides (2 bytes) +# socketNum - socket number server will receive service requests (2 bytes) +# hops - number of hops to reach server (2 bytes) +# +######################################################################### +proc buildServerEntry {operation serviceType serverName netAddress nodeAddress socketNum hops} \ +{ + set entry $serviceType + + if {($operation == $::kSapOperation(response)) || ($operation == $::kSapOperation(getNearestServerResponse))} { + set entry [concat $entry $serverName $netAddress $nodeAddress $socketNum $hops] + } else { + set emptyList {} +# for {set i 0} {$i < 62} {incr i} { +# set emptyList [lappend emptyList "00"] +# } +# set entry [concat $entry $emptyList] + } + return $entry +} + +######################################################################## +# Procedure: buildSapPacket +# +# This procedure builds a SAP packet. +# For a SAP request, only the service type field will be used to build +# the server entry. +# +# Argument(s): +# operation - type of operation the SAP packet performs +# 1 - request +# 2 - response +# 3 - get nearest server request +# 4 - get nearest server response +# serverEntry - server information (64 bytes) +# +######################################################################### +proc buildSapPacket {operation serviceType serverName netAddress nodeAddress socketNum hops} \ +{ + + set oper [format "00 %02x" $operation] + + if {[llength $socketNum] == 1} { + set socketNum [concat [format %02x [expr (($socketNum >> 8) & 0xff)]] \ + [format %02x [expr $socketNum & 0xff]]] + } + + set serverEntry [buildServerEntry $operation $serviceType $serverName $netAddress $nodeAddress \ + $socketNum $hops] + set sapFrame [concat $oper $serverEntry] + return $sapFrame +} + +######################################################################## +# Procedure: buildNetworkEntry +# +# This procedure builds one network entry (8 bytes) for a RIPX packet. +# +# Argument(s): +# networkNumber - network number. Set to all F's for requests (4 bytes) +# hops - number of routers to pass through to reach the network +# number (2 bytes) +# ticks - how much time it takes to reach the network number +# (2 bytes) +# +######################################################################### +proc buildNetworkEntry {networkNumber hops ticks} \ +{ + return [concat $networkNumber [format "00 %02x" $hops] [format "00 %02x" $ticks]] +} + +######################################################################## +# Procedure: buildRipxPacket +# +# This procedure builds a RIP packet used in IPX. +# +# Argument(s): +# operation - type of operation the SAP packet performs (2 bytes) +# 1 - request +# 2 - response +# networkEntry - network information (8 bytes) +# +######################################################################### +proc buildRipxPacket {operation networkNumber hops ticks} \ +{ + set oper [format "00 %02x" $operation] + set nwEntry [buildNetworkEntry $networkNumber $hops $ticks] + return [concat $oper $nwEntry] +} + +######################################################################## +# Procedure: buildArpPacket +# +# This procedure builds the data portion of an ARP packet +# +# Argument(s): +# sourceMAC source MAC address, list format +# sourceIP IP address of the source, in text format +# dutIP IP address of the DUT, in text format +# +######################################################################### +# ARP request on ethernet +proc buildArpPacket {sourceMAC sourceIP dutIP} \ +{ + set ethernetType {08 06} + set destMAC {00 00 00 00 00 00} + set hwType {00 01} + set protocolType {08 00} + set hwAddressLen 06 ;# length of MAC address + set prAddressLen 04 ;# length of IP address + set opcode {00 01} + + return [concat $ethernetType $hwType $protocolType $hwAddressLen $prAddressLen \ + $opcode $sourceMAC [host2addr $sourceIP] $destMAC [host2addr $dutIP]] +} + + +######################################################################## +# Procedure: buildRipPacket +# +# This procedure builds the data portion of a routing update frame. Note- +# up to 25 IP addresses may be specified in one datagram. +# +# Argument(s): +# sourceMAC source MAC address, list format +# sourceIP IP address of source, in text format +# destinationIP IP address of destination, in text format +# RipCommand Command for RIP frame: 01 - request +# 02 - response +# IpList List of RIP blocks to build RIP packet from - +# use buildRipBlocks +# +######################################################################### +proc buildRipPacket {sourceMAC sourceIP destinationIP RipCommand IpList {ttl 0x40} {ripVersion 02}} \ +{ + # Determine number of items in IpList first... + set IpListLength [llength $IpList] + if {($IpListLength > 25)} \ + { + puts "Too many IP addresses specified for RIP" + return + } + + # Determine data length for IP header & UDP header + set RipLength 20 + set dataLength [expr ($IpListLength * $RipLength) + $::kHeaderLength(rip)] + + # Build IP header for RIP packet + set TOS 00 ;# includes precedence bits + set protocol 11 + set flags {00 00} + set options {} + + # make the destination IP address into a broadcast type... + set destinationIP [host2addr $destinationIP] + if {( [llength $destinationIP] == 0 )} \ + { + puts "Invalid destination IP address." + return + } + set ipHeader [buildIpHeader [host2addr $sourceIP] $destinationIP \ + $dataLength $TOS $ttl $protocol $flags $options] + + # build UDP part + set sourcePort {02 08} ;# source port 208 = RIP + set destPort {02 08} + set udpLength [expr $dataLength + $::kHeaderLength(udp)] + set udpLength [concat [format %02x [expr (($udpLength >> 8) & 0xff)]] \ + [format %02x [expr $udpLength & 0xff]]] + set udpChecksum {00 00} + + # build data part of UDP + set RipCommand [format %02x $RipCommand] ;# just to clean it up + set data [concat $RipCommand [format %02x $ripVersion] {00 00}] + + foreach {ipItem} $IpList \ + { + set data [concat $data $ipItem] + } + + set udpData [concat $sourcePort $destPort $udpLength $udpChecksum $data] + set pseudoIpHdr [concat [host2addr $sourceIP] $destinationIP 00 $protocol $udpLength] + set udpChecksum [calculateChecksum $pseudoIpHdr] + + # replace udpChecksum in udpData + return [concat $ipHeader [join [lreplace $udpData 6 7 $udpChecksum]]] +} + + +######################################################################## +# Procedure: buildUdpEchoPacket +# +# This procedure builds the data portion of a test packet for use +# w/Bradner's test suite +# +# Argument(s): +# sourceIP IP address of source, in text format +# destIP IP address of destination, in text format +# frameLength total length of frame, including IP & UDP headers +# +######################################################################### +proc buildUdpEchoPacket {sourceIP destIP frameLength} \ +{ + # a couple of constants... + set addressLength 6 + set crcLength 4 + set typeLength 2 + + set dataLength [expr $frameLength - $addressLength*2 - $typeLength - \ + $::kHeaderLength(ip) - $::kHeaderLength(udp) - $crcLength] + + set TOS 00 ;# includes precedence bits + set TTL 0x0a + set protocol 11 ;# UDP + set flags {00 00} + set options {} + set ipHeader [buildIpHeader [host2addr $sourceIP] [host2addr $destIP] \ + $dataLength $TOS $TTL $protocol $flags $options] + + # build UDP part + set sourcePort {C0 20} + set destPort {00 07} ;# ethernet Echo + set udpLength [concat {00} [format %02x [expr $dataLength + $::kHeaderLength(udp)]]] + set udpChecksum {00 00} + + set data {} + for {set x 0} {$x < $dataLength} {incr x} { + lappend data [format %02x [expr $x & 0xff]] + } + + set udpHeader [concat $sourcePort $destPort $udpLength $udpChecksum] + set pseudoIpHeader [concat [host2addr $sourceIP] [host2addr $destIP] 00 $protocol $udpLength] + set udpChecksum [calculateChecksum [concat $pseudoIpHeader $udpHeader $data]] + set udpHeader [join [lreplace $udpHeader 6 end $udpChecksum]] + + return [concat $ipHeader $udpHeader $data] +} + + +######################################################################## +# Procedure: buildIgmpPacket +# +# This procedure builds the data portion of an IP-multicast (IGMP) +# packet. +# +# Argument(s): +# sourceIP IP address of source, in text format +# destIP IP address of destination, in text format +# version v1 or v2 IGMP +# type IGMP message type: +# 0x11 - Membership query +# 0x16 - Version 2 membership report +# 0x17 - Leave group +# 0x12 - Version 1 membership report (provided +# for backwards capability only) +# respTime Maximum allowed time before sending a responding +# report, in units of 1/10 second. Valid only for +# type = 0x11 (membership query) +# groupAddr IP multicast group address, set to zero for sending +# a general membership query. +# +######################################################################### + +proc buildIgmpPacket {sourceIP destIP version type respTime groupAddr} \ +{ + set TOS 0xc0 ;# includes precedence bits + set TTL 0x01 + set protocol 02 ;# IGMP + set IgmpHdrLength [expr 8 + $::kHeaderLength(ip)] + set flags {00 00} + + switch $version { + default - + v1 { + set options {} + } + v2 { + set options {94 04} + } + } + set ipHeader [buildIpHeader [host2addr $sourceIP] [host2addr $destIP] \ + $IgmpHdrLength $TOS $TTL $protocol $flags $options] + + # build IGMP part + set type [format %02x $type] + set respTime [format %02x $respTime] + set igmpChecksum {00 00} + if {$groupAddr == 0} { + set groupAddr 0.0.0.0 + } + + set igmpHeader [concat $type $respTime $igmpChecksum [host2addr $groupAddr]] + + set igmpChecksum [calculateChecksum $igmpHeader] + set igmpHeader [join [lreplace $igmpHeader 2 3 $igmpChecksum]] + + return [concat $ipHeader $igmpHeader] +} + + +######################################################################## +# Procedure: buildIpPriorityPacket +# +# This procedure builds the data portion of a test UDP packet for Priority +# Queue test. +# +# Argument(s): +# sourceIP IP address of source, in text format +# destIP IP address of destination, in text format +# frameLength total length of frame, including IP & UDP headers +# precedence +# +######################################################################### + +proc buildIpPriorityPacket {sourceIP destIP frameLength TOS} \ +{ + # a couple of constants... + set addressLength 6 + set crcLength 4 + set typeLength 2 + + set dataLength [expr $frameLength - $addressLength*2 - $typeLength - \ + $::kHeaderLength(ip) - $::kHeaderLength(udp) - $crcLength] + + set TOS [format %02x $TOS] ;# includes precedence bits + set TTL 0x0a + set protocol 11 ;# UDP + set flags {00 00} + set options {} + set ipHeader [buildIpHeader [host2addr $sourceIP] [host2addr $destIP] \ + $dataLength $TOS $TTL $protocol $flags $options] + + # build UDP part + set sourcePort {C0 20} + set destPort {00 07} ;# ethernet Echo + set udpLength [concat {00} [format %02x [expr $dataLength + $::kHeaderLength(udp)]]] + set udpChecksum {00 00} + + for {set x 0} {$x < $dataLength} {incr x} { + lappend data [format %02x [expr $x & 0xff]] + } + + set udpHeader [concat $sourcePort $destPort $udpLength $udpChecksum] + set pseudoIpHeader [concat [host2addr $sourceIP] [host2addr $destIP] 00 $protocol $udpLength] + set udpChecksum [calculateChecksum [concat $pseudoIpHeader $udpHeader $data]] + set udpHeader [join [lreplace $udpHeader 6 end $udpChecksum]] + + return [concat $ipHeader $udpHeader $data] +} + + + +######################################################################## +# Procedure: buildVlanTagPacket +# +# This procedure builds the data portion of a vlan tag packet. +# +# Argument(s): +# userPriority - user priority field of the TCI, eight priority levels, 0-7 +# CFIformat - Canonical Format Indicator - if set to true, all MAC +# addresses in frame is in Canonical format +# VID - twelve-bit VLAN identifier that uniquely identifies +# the VLAN to which the frame belongs. +# data - remaining data for packet +# +######################################################################### +proc buildVlanTagPacket {userPriority CFIformat VID data} \ +{ + return [concat [buildVidHeader $userPriority $CFIformat $VID] $data] +} + + +######################################################################## +# Procedure: buildSnmpPacket +# +# This procedure builds the data portion of an SNMP management frame. +# Only 'get' requests are supported. +# +# Argument(s): +# sourceIP IP address of source, in text format +# destIP IP address of destination, in text format +# sourcePort Source port to aim SNMP packet at +# communityName community name, in text format (usually 'public') +# oid oid to get +# +######################################################################### +proc buildSnmpPacket {sourceIP destIP sourcePort communityName oid } \ +{ + # build SNMPv1 part first... + set asn1Header 30 + set snmpVersion {02 01 00} + set community 04 + set NullValue {05 00} ;# cause we only do gets... + + set snmpGet a0 + set snmpRequestID {02 02 00 00} + set snmpErrorIndex {02 01 00 02 01 00} + + # begin building varbind list + set oid [oid2octet $oid] ;# turn oid into an octet list + set oidLength [llength $oid] + + set VarBind [concat [list 06 [format %02x $oidLength]] $oid] + + set VarBindLength [list 30 [format %02x [expr $oidLength + 4]]] + set VarBind [concat $VarBindLength $VarBind] + + set VarBindList [list 30 [format %02x [expr [llength $VarBind] + 2]]] + + set VarBind [concat $snmpRequestID $snmpErrorIndex $VarBindList $VarBind] + set VarBind [concat $snmpGet [format %02x [expr [llength $VarBind] + 2]] $VarBind] + + # Finish by building pdu + set communityName [string tolower $communityName] + set pdu [concat $snmpVersion $community [format %02x [llength $communityName]] \ + $communityName $VarBind] + + set pdu [concat $asn1Header [format %02x [expr [llength $pdu] + 2]] $pdu $NullValue] + + + # build UDP part next + set destPort {00 a1} ;# SNMP port 161 + set udpLength [concat {00} [format %02x [expr [llength $pdu] + $::kHeaderLength(udp)]]] + set udpChecksum {00 00} + set sourcePort [list [format %02x [expr $sourcePort >> 8]] \ + [format %02x [expr $sourcePort & 0xff]]] + set udp [concat $sourcePort $destPort $udpLength $udpChecksum $pdu] + set udpChecksum [calculateChecksum $udp] + set udp [join [lreplace $udp 6 7 $udpChecksum]] + + set TOS 00 ;# includes precedence bits + set TTL 80 + set protocol 11 ;# UDP + set flags {00 00} + set options {} + set ipHeader [buildIpHeader [host2addr $sourceIP] [host2addr $destIP] \ + [llength $pdu] $TOS $TTL $protocol $flags $options] + + return [concat $ipHeader $udp] +} + +######################################################################## +# Procedure: buildIcmpPacket +# +# This procedure builds the data portion of an icmp/ping packet. +# +# Argument(s): +# sourceIP IP address of source, in text format +# destIP IP address of destination, in text format +# dataLength length of ping +# icmpEcho == 1 if this is this an Echo frame, 0 if response +# icmpSequence sequence number for this ping, 0 is valid. +# +######################################################################### +proc buildIcmpPacket {sourceIP destIP dataLength icmpEcho icmpSequence} \ +{ + set start 97 + set max [expr $start + 22] + + if {($icmpEcho == 1)} \ + { + set icmpEcho 08 + } \ + else \ + { + set icmpEcho 00 + } + + set icmpCode 00 + set icmpChecksum {00 00} + set icmpId {01 00} + + set icmpSequence [long2octet $icmpSequence] + + for {set x 0} {$x < $dataLength} {incr x } { + if {($start > $max)} {set start 97} + lappend data [format %02x $start] + incr start + } + + set icmp [concat $icmpEcho $icmpCode $icmpChecksum \ + $icmpId $icmpSequence $data] + set icmpChecksum [calculateChecksum $icmp] + set icmp [join [lreplace $icmp 2 3 $icmpChecksum]] + + set TOS 00 ;# includes precedence bits + set TTL 0x0a + set protocol 01 ;# ICMP + set flags {00 00} + set options {} + set ipHeader [buildIpHeader [host2addr $sourceIP] [host2addr $destIP] \ + [expr [llength $icmp] + $::kHeaderLength(ip)] \ + $TOS $TTL $protocol $flags $options] + + return [concat $ipHeader $icmp] +} + + +######################################################################## +# Procedure: buildBpduPacket +# +# This procedure builds the data portion of a 802.1d BPDU packet. +# +# Argument(s): +# rootID root id in IEEE 802.1d BPDU packet +# bridgeID bridge id in IEEE 802.1d BPDU packet +# +######################################################################### +proc buildBpduPacket { {rootID {00 10 FF E4 1C 0D}} {bridgeID {00 10 FF E4 1C 0D}} {protocolID 0} {versionID 0} {bpduType 0} {bitField 0} \ + {rootPriority 0} {rootPathCost 0} {bridgePriority 32768} {portID 128} \ + {messageAge 17920} {maxAge 20} {helloTime 2} {forwardDelay 15} } \ +{ + set bpduData {} + + set llcHeader {42 42 03} + set protocolID [long2octet $protocolID] ;# 2 byte + set versionID [long2octet $versionID 1] ;# 1 byte + set bpduType [long2octet $bpduType 1] ;# 1 byte + set bitField [long2octet $bitField 1] ;# 1 byte + set rootPriority [long2octet $rootPriority] ;# 2 byte + set rootPathCost [long2octet $rootPathCost 4] ;# 4 byte + set bridgePriority [long2octet $bridgePriority] ;# 2 byte + set portID [long2octet $portID 1] ;# 1 byte + set messageAge [long2octet $messageAge] ;# 2 byte + set maxAge [long2octet $maxAge] ;# 2 byte + set helloTime [long2octet $helloTime] ;# 2 byte + set forwardDelay [long2octet $forwardDelay] ;# 2 byte + + set otherData {00 10 54 65 73 74 5f 52 6f} + set bpduData [concat $llcHeader $protocolID $versionID $bpduType $bitField $rootPriority \ + $rootID $rootPathCost $bridgePriority $bridgeID $portID $messageAge \ + $maxAge $helloTime $forwardDelay $otherData] + + + set length [long2octet [llength $bpduData]] + + return [concat $length $bpduData ] +} + + + + + + diff --git a/dep/tclclient/ixTcl1.0/Generic/parseArgs.tcl b/dep/tclclient/ixTcl1.0/Generic/parseArgs.tcl new file mode 100644 index 00000000..c4bb6288 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/parseArgs.tcl @@ -0,0 +1,87 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: parseArgs.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 10-11-2004 Hasmik +# +# Description: This file contains common procs used for parsing +# +# +################################################################################## + + + +######################################################################## +# Procedure: ixGetArgument +# +# Description: This command will return a value from an argument list +# for a given named argument +# +# Arguments: +# argList - A list of arguments. +# argToFind - The name of the option to find in the argList +# +######################################################################## +proc ixGetArgument { argList argToFind } \ +{ + + set retValue "" + + if {[llength $argList] == 0 } { + errorMsg "Error - empty argument list" + return $retValue + } + + # Put on the dash if it is missing. + if {[string index $argToFind 0] != "-"} { + set argToFind [format "-%s" $argToFind] + } + + set index [lsearch -exact $argList $argToFind] + + if {$index != -1} { + # Just in case they passed a list with the argument, then nothing + # after it, this will return null in that case. + if {[catch {lindex $argList [expr $index + 1]} retValue]} { + set retValue "" + } + } + + return $retValue +} + + diff --git a/dep/tclclient/ixTcl1.0/Generic/platform.tcl b/dep/tclclient/ixTcl1.0/Generic/platform.tcl new file mode 100644 index 00000000..7aba7d2d --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/platform.tcl @@ -0,0 +1,88 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: platform.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 09-29-2000 DS +# +# Description: This file contains platform-independent stuff +# +################################################################################## + + +############################################################ +# Procedure : isUNIX +# +# Description: This proc tells if current OS is Windows. +# Output : 1 if it is UNIX, 0 otherwise. +# +############################################################ +proc isUNIX {} \ +{ + global tcl_platform + + set retCode 0 + + if {$tcl_platform(platform) == "unix"} { + set retCode 1 + } + + return $retCode +} + + +############################################################ +# Procedure : isWindows +# +# Description: This proc tells if current OS is Windows. +# Output : 1 if it is UNIX, 0 otherwise. +# +############################################################ + +proc isWindows {} \ +{ + global tcl_platform + + set retCode 0 + + if {$tcl_platform(platform) == "windows"} { + set retCode 1 + } + + return $retCode +} + + diff --git a/dep/tclclient/ixTcl1.0/Generic/random.tcl b/dep/tclclient/ixTcl1.0/Generic/random.tcl new file mode 100644 index 00000000..e6e68739 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/random.tcl @@ -0,0 +1,109 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: random.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 02-03-2000 HSH +# +# Description: This file contains common procs used for generating random +# numbers +# +################################################################################## + + +######################################################################## +# Set the default randomSeed +# Now use a random number to be the randomSeed +######################################################################## + +global randomSeed + +set randomSeed [expr int( [expr rand()] * 100 ) ] + + +######################################################################## +# Procedure: RandomRange +# Should let range equal to "the total number of data" ( do not subtract 1 )!! +# +# range - the range of random numbers +# +######################################################################## +proc RandomRange { range } \ +{ + + return [ expr int( [ Random ] * $range ) ] +} + +######################################################################## +# Procedure: Random +# This gives a random decimal num. +# +######################################################################## + +proc Random {} \ +{ + global randomSeed + set randomSeed [ expr ( $randomSeed * 9301 + 49297 ) %233280 ] + return [ expr $randomSeed / double( 233280 ) ] +} + +######################################################################## +# Procedure: RandomInit +# +# +######################################################################## +proc RandomInit {} \ +{ + global randomSeed + set randomSeed [clock seconds] +} + + +######################################################################## +# Procedure: RandomFromTo +# This gives a random num within the given range of numbers. +# +######################################################################## +proc RandomFromTo {from to} \ +{ + global randomSeed + + set randomSeed [expr ($randomSeed*9301 + 49297) % 233280] + set random [expr $randomSeed/double(233280)] + + return [expr int($random * ($to - $from + 1)) + $from] +} + diff --git a/dep/tclclient/ixTcl1.0/Generic/scriptmateBackCompat.tcl b/dep/tclclient/ixTcl1.0/Generic/scriptmateBackCompat.tcl new file mode 100644 index 00000000..8c99c3f1 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/scriptmateBackCompat.tcl @@ -0,0 +1,65 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: scriptmateBackCompat.tcl +# +# This file is used to create backwards compatibility for sample scripts created prior to +# IxOS 3.70. Post of this version, a package require Scriptmate is needed. This will handle +# attempting to define the commands that used to be defined by IxTclHal and now are defined +# Scriptmate +# +# Copyright © IXIA +# All Rights Reserved. +# +############################################################################################# + +namespace eval scriptmateBackwardsCompatibility {} + + +proc scriptmateBackwardsCompatibility::createAllCommands { } { + + foreach cmd {addr back2back bcast cableModem congest dataVerify dtm errframe floss flow gapcheck imix \ + ipmulticast latency tunnel mesh qost randomFS tput tputerror tputjitter tputnat tputl2l3 tputlat \ + tputmfs tputvlan ttl wip results internalModem user bgpSuite dslats ospfSuite rsvpSuite ldpSuite \ + l2VpnSuite l3VpnSuite} { + + proc $cmd { args } { + if {[catch {package require Scriptmate}]} { + return "Command is not supported by IxTclHal, it is part of Scriptmate" + } else { + return [eval $cmd $args] + } + } + } +} + + diff --git a/dep/tclclient/ixTcl1.0/Generic/socket.tcl b/dep/tclclient/ixTcl1.0/Generic/socket.tcl new file mode 100644 index 00000000..50d60a2c --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/socket.tcl @@ -0,0 +1,718 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: socket.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# +# Description: +# +############################################################################### + +############################################################################### +# Procedure: serverSocketAccept +# +# Description: This function accepts a connection request from client and +# spawns a new socket that will be used to communicate with client +# +# Arguments: +# socket - the name of the socket to configure +# addr - the ip address or hostname of client +# port - the port number of the server socket +############################################################################### +proc serverSocketAccept {socket addr port} \ +{ + debugMsg "enter serverSocketAccept $socket from $addr port $port" + + fconfigure $socket -buffering none + fconfigure $socket -blocking 0 + fileevent $socket readable [list readsocket $socket] + + debugMsg "exit serverSocketAccept" +} + + +############################################################################### +# Procedure: readSocket +# +# Description: This function reads a line from client. It is invoked +# everytime a message arrived from client. +# +# Arguments: +# socket - the socket that is communicating with the client. +############################################################################### +proc readsocket {socket} \ +{ + global socketArray + + debugMsg "enter readSocket $socket" + + if {[eof $socket] || [catch {gets $socket line}]} { + + closeSocket $socket + debugMsg "Close $socket" + + } else { + + debugMsg "line=$line" + + if {[string compare $line "quit"] == 0} { + close $socketArray(serverSocket) + catch {unset socketArray(serverSocket)} + } + + if [isLogSocket $socket] { + + if { ![regexp {client response:|runningStatus:|this is log socket} $line] } { + + if [fblocked $socket] { + set line [read -nonewline $socket] + updateLog $line 0 + } else { + updateLog $line + } + + } else { + + handleEvent $socket $line + } + + } elseif {[isCommandSocket $socket]} { + fileTransferServer::handleCommand $line + + } elseif {[isDataSocket $socket]} { + + fileTransferServer::handleData $line + + } else { + + handleEvent $socket $line + } + } + + update idletask + debugMsg "exit readSocket" +} + + +############################################################################### +# Procedure: handleEvent +# +# Description: This function invokes the appropriate action according to the +# contents of the input from client. +# +# Arguments: +# socket - the socket that received the line. +# line - the character string received from the client +############################################################################### +proc handleEvent {socket line} \ +{ + global currContext + global socketArray + + debugMsg "enter handleEvent socket=$socket line=$line" + + set commandPattern "" + catch { regexp {(.*): (.*)} $line match commandPattern portMapList } + + + if {[regexp {client response} $line]} { + + # Nothing to do + + } elseif {$line == "closeSocket"} { + + closeSocket $socket + + } elseif {$line == "runningStatus:currentTestFinished"} { + + updateLog "\n****** Current test(s) finished ******" + + runStop + forceToStop + + setSF08StartButtonState normal + + displayResults + + } elseif {$line == "runningStatus:currentTestFailed"} { + + updateLog "\n****** Current test(s) failed ******" + + runStop + forceToStop + + } elseif {$line == "runningStatus:currentTestStopped"} { + + updateLog "\n****** Current test(s) stopped ******" + + runStop + forceToStop + + setSF08StartButtonState normal + + } elseif {$line == "runningStatus:currentTestStoppedByUser"} { + + updateLog "\n****** Current test(s) stopped by user ******" + + forceToStop + + setSF08StartButtonState normal + + } elseif {[regexp {removefile} $line]} { + + set file [lindex [split $line "?"] 1] + debugMsg "file to be removed is $file" + + if [file exists $file] { + catch {file delete $file} + } + + } elseif {$line == "progress dialog is ready"} { + + progressDialogReady + + } elseif {$line == "this is test command socket"} { + + set currContext(commandSocketReady) 1 + set socketArray(testCommandSocket) $socket + + } elseif {$line == "Background wish is ready"} { + + backgroundWishReady + + } elseif {$line == "this is log socket"} { + + set socketArray(logSocket) $socket + + } elseif {$line == "this is command socket"} { + + set socketArray(commandSocket) $socket + + } elseif {$line == "this is data socket"} { + + set socketArray(dataSocket) $socket + } elseif { $commandPattern == "portlist" } { + + debugMsg "[pid]>>>>>>>>> portMapList:$portMapList" + testConfig::setCurrentTestMap $portMapList + } + + debugMsg "exit handleEvent" +} + + +############################################################################### +# Procedure: generatePort +# +# Description: This function generates a port for then server side socket. +# It is based on the current pid. +# +# Returns the generated port number. +############################################################################### +proc generatePort {} \ +{ + debugMsg "enter generatePort" + + set port [pid] + set len [string length $port] + debugMsg "pid=$port" + + if {$len > 4} { + set port [string range $port [expr $len - 4] [expr $len - 1]] + } + + if {[string compare $port 0] != 0} { + set port [string trimleft $port 0] + } + + if {$port < 4000} { + incr port 4000 + } + + debugMsg "exit generatePort port=$port" + return $port +} + + +############################################################################### +# Procedure: putsClient +# +# Description: This function sends a line to client. +# +# Arguments: +# line - The string of characters that is to be sent to the client side. +############################################################################### +proc putsClient {line} \ +{ + global socketArray + + debugMsg "enter putsClient line=$line" + + if {([info exists socketArray(testCommandSocket)] == 0) || \ + ($socketArray(testCommandSocket) == -1)} { + debugMsg "exit putsClient on testCommandSocket does not exist" + + } else { + catch {puts $socketArray(testCommandSocket) $line} + catch {flush $socketArray(testCommandSocket)} + debugMsg "exit putsClient" + } +} + + +############################################################################### +# Procedure: createServerSocket +# +# Description: This function creates a server side socket on the specified +# port. If port is not specified, the port number will be +# generated using pid and set to SCRIPTMATE_PORT as an environment +# variable. The spawned child process will retrieve the port +# number from the environment and create a client side socket. +# +# Arguments: +# port - Optional. The port number to use. Defaults to -1 if not given. +# retry - Optional. Increase the port number by 1 until a port is available +# and server socket is created when this argument is true. +# Otherwise give up if the port is not available. +# Returns: +# port - The port number of server socket. +# +############################################################################### +proc createServerSocket {{port -1} {retry true}} \ +{ + global env socketArray + + debugMsg "enter createServerSocket" + + if {$port == -1} { + set port [generatePort] + } + + while {[catch {socket -server serverSocketAccept $port} socketArray(serverSocket)] == 1} { + if {$retry == "true"} { + incr port + debugMsg "port=$port" + } else { + set port -1 + break + } + } + + debugMsg "exit createServerSocket $port" + return $port +} + + +############################################################################### +# Procedure: closeSocket +# +# Description: Close either the given socket, or if none given, close the +# global new socket +# +# Arguments: +# socket - Optional. The socket to close +############################################################################### +proc closeSocket {socket} \ +{ + global socketArray + + debugMsg "enter closeSocket $socket" + + if {$socket != -1} { + # Close the given socket + catch {close $socket} + + foreach name [array name socketArray] { + if {$socketArray($name) == $socket} { + catch {unset socketArray($name)} + break + } + } + } + + debugMsg "exit closeSocket" +} + + +############################################################################### +# Procedure: closeServerSocket +# +# Description: Close the global main socket +############################################################################### +proc closeServerSocket {} \ +{ + global socketArray + + debugMsg "enter closeServerSocket" + + if {[info exists socketArray(serverSocket)]} { + catch {close $socketArray(serverSocket)} + catch {unset socketArray(serverSocket)} + } + + debugMsg "exit closeServerSocket" +} + +############################################################################### +# Procedure: isTestCommandSocket +# +# Description: Determine if the given socket is the same as the test command socket +# +# Arguments: +# socket - the socket to check +# +# Returns 1 when it is the same socket, and 0 when it is not the same. +############################################################################### +proc isTestCommandSocket {socket} \ +{ + global socketArray + + debugMsg "enter isTestCommandSocket $socket" + + set retCode 0 + + if [info exists socketArray(testCommandSocket)] { + if {$socket == $socketArray(testCommandSocket)} { + set retCode 1 + } + } + + debugMsg "exit isTestCommandSocket $retCode" + return $retCode +} + +############################################################################### +# Procedure: isLogSocket +# +# Description: Determine if the given socket is the same as the log socket +# +# Arguments: +# socket - the socket to check +# +# Returns 1 when it is the same socket, and 0 when it is not the same. +############################################################################### +proc isLogSocket {socket} \ +{ + global socketArray + + debugMsg "enter isLogSocket $socket" + + set retCode 0 + + if [info exists socketArray(logSocket)] { + if {$socket == $socketArray(logSocket)} { + set retCode 1 + } + } + + debugMsg "exit isLogSocket $retCode" + return $retCode +} + +############################################################################### +# Procedure: isCommandSocket +# +# Description: Determine if the given socket is the same as the command socket +# +# Arguments: +# socket - the socket to check +# +# Returns 1 when it is the same socket, and 0 when it is not the same. +############################################################################### +proc isCommandSocket {socket} \ +{ + global socketArray + + debugMsg "enter isCommandSocket $socket" + + set retCode 0 + + if [info exists socketArray(commandSocket)] { + if {$socket == $socketArray(commandSocket)} { + set retCode 1 + } + } + + debugMsg "exit isCommandSocket $retCode" + return $retCode +} + +############################################################################### +# Procedure: isDataSocket +# +# Description: Determine if the given socket is the same as the data socket +# +# Arguments: +# socket - the socket to check +# +# Returns 1 when it is the same socket, and 0 when it is not the same. +############################################################################### +proc isDataSocket {socket} \ +{ + global socketArray + + debugMsg "enter isDataSocket $socket" + + set retCode 0 + + if [info exists socketArray(dataSocket)] { + if {$socket == $socketArray(dataSocket)} { + set retCode 1 + } + } + + debugMsg "exit isDataSocket $retCode" + return $retCode +} + +############################################################################### +# Procedure: createClientSocket +# +# Description: Create a client socket for the given port name. +# +# Arguments: +# port - Optional. The port number to use for the socket being created. +# +# Returns the name of the socket created +############################################################################### +proc createClientSocket {{port -1}} \ +{ + global ixgClientSocket + + debugMsg "enter createClientSocket" + + set socket [createClientSocketCreate localhost $port] + + if {$socket != -1} { + fconfigure $socket -buffering line + fileevent $socket readable [list readClientSocket $socket] + #puts $socket "this is test command socket" + } + + set ixgClientSocket $socket + + update idletask + + debugMsg "exit createClientSocket $socket" + return $socket +} + + +############################################################################### +# Procedure: closeClientSocket +# +# Description: Close the given socket. If no socket given, close the global +# ixgClientSocket +# +# Arguments: +# socket - the name of the socket to close +############################################################################### +proc closeClientSocket {{socket -1}} \ +{ + global ixgClientSocket + + if {$socket != -1} { + catch {close $socket} + } else { + catch {close $ixgClientSocket} + } +} + + +############################################################################### +# Procedure: closeAllSockets +# +# Description: Close the two global sockets +############################################################################### +proc closeAllSockets {} \ +{ + global socketArray + + foreach name [array name socketArray] { + closeSocket $socketArray($name) + } +} + + +############################################################################### +# Procedure: readClientSocket +# +# Description: Read the data from the given socket +# +# Arguments: +# socket - the socket to get the data from +############################################################################### +proc readClientSocket {socket} \ +{ + if {[eof $socket] || [catch {gets $socket line}]} { + debugMsg "readClientSocket: child eof line" + close $socket + } else { + debugMsg "readClientSocket: $line" + handleCommand $line + } +} + + +############################################################################### +# Procedure: createClientSocketCreate +# +# Description: Create a client socket given a hostname and port id. +# +# Arguments: +# host - the hostname to use +# port - the port number to use +# +# Returns the name of the socket created +############################################################################### +proc createClientSocketCreate {host port} \ +{ + global env + + if {$port == -1} { + if {[info exists env(SCRIPTMATE_PORT)]} { + set port $env(SCRIPTMATE_PORT) + } else { + debugMsg "Could not find env(SCRIPTMATE_PORT), failed creating socket." + return -1 + } + } + + set socketName [socket $host $port] + fconfigure $socketName -blocking 0 + fconfigure $socketName -buffering line + return $socketName +} + + +############################################################################### +# Procedure: putsServer +# +# Description: +# +# Arguments: +# line - the character stream that is being sent to the server side. +# +# Returns 0 on success and 1 on failure +############################################################################### +proc putsServer {line} \ +{ + global ixgClientSocket + + set retCode 0 + + if {([info exists ixgClientSocket] == 0) || ($ixgClientSocket == -1)} { + set retCode 1 + debugMsg "in putsServer, ixgClientSocket is bad" + + } else { + debugMsg "in putsServer before line=$line is sent to server" + if {[catch {puts $ixgClientSocket $line} retCode]} { + + # Stop the shell if the child socket is closed + exit + } + catch {flush $ixgClientSocket} + } + + return $retCode +} + + +############################################################################### +# Procedure: handleCommand +# +# Description: Handle the command sent to the client +# +# Arguments: +# line - the character stream to handle +############################################################################### +proc handleCommand {line} \ +{ + putsServer "client response: $line newline" + + if {$line == "stopTest"} { + stopTest + + } elseif { $line == "closeSession" || $line == "stopTestNow" } { + # kill current tcl shell + closeAllSockets + exit + + } elseif {[regexp {progressDialog} $line] == 1} { + + set msg [lindex [split $line :] 1] + setMsgWinMessage $msg + + } elseif {$line == "createInterpreter"} { + + # Do nothing + + } elseif {[regexp {source\ ?} $line]} { + + regsub {source\ ?} $line "" filename + + if { [catch { eval "source \"$filename\"" } retCode] } { + + logMsg $retCode + + } else { + + if { $retCode == 0 } { + logMsg "runningStatus:currentTestFinished" + } else { + logMsg "runningStatus:currentTestFailed" + } + } + + logOff + + } elseif {$line == "stopSF08Test"} { + + setStopTestFlag 1 + logOff + + } elseif { [regexp {connectToTclServer} $line] } { + set serverName [lindex $line 1] + tclServer::connectToTclServer $serverName errMsg + + } + + debugMsg "handleCommand:Exit" + +} + + diff --git a/dep/tclclient/ixTcl1.0/Generic/statistics.tcl b/dep/tclclient/ixTcl1.0/Generic/statistics.tcl new file mode 100644 index 00000000..c10a23d6 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/statistics.tcl @@ -0,0 +1,1222 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: statistics.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 12-30-1998 DS +# +# Description: +# This file contains common procs used to access/calculate statistics +# +######################################################################## + + +######################################################################## +# Procedure: checkTransmitDone +# +# This command polls the TX rate counters & returns the number of frames +# transmitted +# +# Argument(s): +# chassis chassis ID +# lm Load Module number +# port port number +# +######################################################################## +proc checkTransmitDone {chassis lm port} \ +{ + set retCode $::TCL_OK + set numTxFrames 0 + + while {[stat getTransmitState $chassis $lm $port] && ($::ixStopTest != 1)} { + after 250 + } + + # just a wait to make sure stats are all updated... + after 400 + + set txList [list [list $chassis $lm $port]] + requestStats txList + + if [statList get $chassis $lm $port] { + errorMsg "Error getting statList for [getPortId $chassis $lm $port]." + set retCode $::TCL_ERROR + } + + if { !$retCode } { + # we only need this cause this proc returns num frames sent... + if [catch {statList cget -scheduledFramesSent} numTxFrames ] { + if [catch {statList cget -framesSent} numTxFrames ] { + set numTxFrames 0 + } else { + set numTxFrames [mpexpr $numTxFrames - [statList cget -protocolServerTx]] + if {[isNegative $numTxFrames]} { + set numTxFrames 0 + } + } + } + } + + return $numTxFrames +} + + +######################################################################## +# Procedure: checkAllTransmitDone +# +# This command polls the TX rate counters for all ports in the list/map +# +# Argument(s): +# TxRxArray - list or map array containing ports +# +######################################################################## +proc checkAllTransmitDone {TxRxArray {duration 0}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set timeout [expr $duration + 1] + set currentTime [mpexpr [clock clicks]/1000.] + + set txPorts [getTxPorts txRxArray] + + foreach port $txPorts { + scan $port "%d %d %d" c l p + + while {[stat getTransmitState $c $l $p] == $::statActive && ($::ixStopTest != 1)} { + after 250 + if {$duration > 0} { + if {[mpexpr [clock clicks]/1000. - $currentTime] > $timeout} { + set retCode $::TCL_ERROR + break + } + } + } + if { $retCode } { + break + } + } + + return $retCode +} + + + +######################################################################## +# Procedure: requestStats +# +# This command request stats from a group of ports +# +# Argument(s): +# TxRxArray - list or map array containing ports +# +######################################################################## +proc requestStats {TxRxArray} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + statGroup setDefault + foreach port [getAllPorts txRxArray] { + scan $port "%d %d %d" c l p + + if [statGroup add $c $l $p] { + errorMsg "Error adding port [getPortId $c $l $p] to statGroup" + set retCode $::TCL_ERROR + continue + } + } + + if [statGroup get] { + errorMsg "Error getting stats for ports in statGroup" + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: collectTxStats +# +# This command polls the TX counters +# +# Argument(s): +# txList - list of transmit ports +# TxNumFrames - array containing the number of frames that should have been transmitted +# TxActualFrames - array containing the actual transmitted stats (returned val) +# +######################################################################## +proc collectTxStats {txList TxNumFrames TxActualFrames {TotalTxFrames ""} {verbose true} } \ +{ + upvar $TxNumFrames txNumFrames + upvar $TxActualFrames txActualFrames + + if {[info exists TotalTxFrames]} { + upvar $TotalTxFrames totalTxFrames + } + + set retCode 0 + set totalTxFrames 0 + + if {$verbose == "true"} { + logMsg "Collecting transmit statistics ..." + } + + checkAllTransmitDone txList + requestStats txList + + # Loop through all receive ports and find the longest name. Then the log messages can be formatted well. + # Keep the names in a temporary array for use in the next loop. This is so unix will not be calling twice + # for the name. + set nameLen 0 + foreach txMap $txList { + scan $txMap "%d %d %d" tx_c tx_l tx_p + set tempTxArray($tx_c,$tx_l,$tx_p) [getPortId $tx_c $tx_l $tx_p] + set newLen [string length $tempTxArray($tx_c,$tx_l,$tx_p)] + if {$newLen > $nameLen} { + set nameLen $newLen + } + } + + foreach txMap $txList { + scan $txMap "%d %d %d" tx_c tx_l tx_p + + if [statList get $tx_c $tx_l $tx_p] { + errorMsg "Error getting stats for [getPortId $tx_c $tx_l $tx_p]." + set txActualFrames($tx_c,$tx_l,$tx_p) 0 + continue + } + + # there's a new stat for the txs-family so that we don't have to subtract out the protocol stats, yea!! + if [catch {statList cget -scheduledFramesSent} numTxFrames] { + # if this port doesn't support nifty new stat, then just use config'd value + set numTxFrames $txNumFrames($tx_c,$tx_l,$tx_p) + + # only make use of the protocolServerTx stats if the user aborted the test whilst transmitting + if {$::ixStopTest} { + if [catch {statList cget -framesSent} numTxFrames ] { + set numTxFrames 0 + } + if [catch {statList cget -protocolServerTx} numProtocolServerFrames ] { + set numProtocolServerFrames 0 + } + set numTxFrames [mpexpr ($numTxFrames - $numProtocolServerFrames ) ] + # Since 32 bit counter (mpexpr) is used here, if we get a 32 bit long number (in binary) whose most significiant + # bit is 1, it will be recognized as a negtive number. + # So we use regexp to determine wheather numTxFrames is a negative number instead of using "$numTxFrames < 0" + if { [regexp {^-[0-9]+$} $numTxFrames] } { + set numTxFrames 0 + } + } + } + set txActualFrames($tx_c,$tx_l,$tx_p) $numTxFrames + + if {$txActualFrames($tx_c,$tx_l,$tx_p) < $txNumFrames($tx_c,$tx_l,$tx_p)} { + if {$verbose == "true"} { + logMsg "All $txNumFrames($tx_c,$tx_l,$tx_p) frames not transmitted on [getPortId $tx_c $tx_l $tx_p] - check the device" + } + set retCode $::TCL_ERROR + } elseif {$txActualFrames($tx_c,$tx_l,$tx_p) > $txNumFrames($tx_c,$tx_l,$tx_p)} { + if {$verbose == "true"} { + logMsg "Transmitted more frames than expected on [getPortId $tx_c $tx_l $tx_p]" + } + } + + if {$verbose == "true"} { + logMsg [format "%-${nameLen}s: Total frames transmitted: $txActualFrames($tx_c,$tx_l,$tx_p)" $tempTxArray($tx_c,$tx_l,$tx_p)] + } + + mpincr totalTxFrames $txActualFrames($tx_c,$tx_l,$tx_p) + } + + return $retCode +} + + +######################################################################## +# Procedure: collectRxStats +# +# This command polls the RX counters +# +# Argument(s): +# rxList - list of receive ports +# RxNumFrames - array containing the returned rx stats +# TotalRxFrames - total received frames +# printError - if no frames received, print error message +# +######################################################################## +proc collectRxStats {rxList RxNumFrames {TotalRxFrames ""} {printError yes} {receiveCounter userDefinedStat2}} \ +{ + upvar $RxNumFrames rxNumFrames + if {[info exists TotalRxFrames]} { + upvar $TotalRxFrames totalRxFrames + } + + set retCode 0 + set totalRxFrames 0 + + logMsg "Collecting receive statistics ..." + + set retCode [requestStats rxList] + + # Loop through all receive ports and find the longest name. Then the log messages can be formatted well. + set nameLen 0 + foreach rxMap [lnumsort $rxList] { + scan $rxMap "%d %d %d" rx_c rx_l rx_p + set tempRxArray($rx_c,$rx_l,$rx_p) [getPortId $rx_c $rx_l $rx_p] + set newLen [string length $tempRxArray($rx_c,$rx_l,$rx_p)] + if {$newLen > $nameLen} { + set nameLen $newLen + } + } + + foreach rxMap [lnumsort $rxList] { + scan $rxMap "%d %d %d" rx_c rx_l rx_p + + # count the Rx Frames + if [statList get $rx_c $rx_l $rx_p] { + errorMsg "Error getting Rx statistics for [getPortId $rx_c $rx_l $rx_p]" + } + + if [catch {statList cget -$receiveCounter} rxNumFrames($rx_c,$rx_l,$rx_p)] { + errorMsg "$receiveCounter is not a valid counter for port [getPortId $rx_c $rx_l $rx_p]" + set rxNumFrames($rx_c,$rx_l,$rx_p) 0 + } + + # if no frames received, there must be a connection error... dump out + if {$rxNumFrames($rx_c,$rx_l,$rx_p) == 0} { + if {$printError == "yes"} { + logMsg "\n\n\tError !!" + logMsg "\tZero packets received on port [getPortId $rx_c $rx_l $rx_p]" + logMsg "\tCheck the connections." + } + set retCode 1 + } + + logMsg [format "%-${nameLen}s: Total frames received : $rxNumFrames($rx_c,$rx_l,$rx_p)" \ + $tempRxArray($rx_c,$rx_l,$rx_p)] + + mpincr totalRxFrames $rxNumFrames($rx_c,$rx_l,$rx_p) + } + + return $retCode +} + + +######################################################################## +# Procedure: collectVlanStats +# +# This command polls the Vlan counters +# +# Argument(s): +# vlanList - list of receive ports +# VlanNumFrames - array containing the returned Vlan stats +# TotalVlanFrames - total received frames +# +######################################################################## +proc collectVlanStats {vlanList VlanNumFrames {TotalVlanFrames ""}} \ +{ + upvar $VlanNumFrames vlanNumFrames + if {[info exists TotalVlanFrames]} { + upvar $TotalVlanFrames totalVlanFrames + } + + set retCode 0 + set totalVlanFrames 0 + + debugMsg "Collecting vlan tagged frame statistics ..." + + set retCode [requestStats vlanList] + + foreach rxMap [lnumsort $vlanList] { + scan $rxMap "%d %d %d" rx_c rx_l rx_p + + # count the vlan tagged frames + if [statList get $rx_c $rx_l $rx_p] { + errorMsg "Error getting vlan tagged frame statistics for [getPortId $rx_c $rx_l $rx_p]" + } else { + if [catch {statList cget -vlanTaggedFramesRx} vlanNumFrames($rx_c,$rx_l,$rx_p)] { + errorMsg "***** WARNING:Invalid statistic (vlanTaggedFramesRx) for the port:[getPortString $rx_c $rx_l $rx_p]\n" + set vlanNumFrames($rx_c,$rx_l,$rx_p) 0 + set retCode 1 + } + + mpincr totalVlanFrames $vlanNumFrames($rx_c,$rx_l,$rx_p) + } + } + return $retCode +} + +######################################################################## +# Procedure: collectDataIntegrityStats +# +# This command polls the Data Integrity counters +# +# Argument(s): +# rxPortList - list of receive ports +# Errors - array containing the error stats +# RxFrames - received data integrity frames per rx port +# TotalRxFrames - total received data integrity frames +# +######################################################################## +proc collectDataIntegrityStats {rxPortList Errors RxFrames {TotalRxFrames ""} {TotalErrorFrames ""}} \ +{ + upvar $Errors errors + upvar $RxFrames rxFrames + + if {[string length $TotalRxFrames] != 0 } { + upvar $TotalRxFrames totalRxFrames + } + + if {[string length $TotalErrorFrames] != 0 } { + upvar $TotalErrorFrames totalErrorFrames + } + + set retCode $::TCL_OK + set totalRxFrames 0 + set totalErrorFrames 0 + + logMsg "Collecting data integrity statistics ..." + + set retCode [requestStats rxPortList] + + foreach rxMap $rxPortList { + scan $rxMap "%d %d %d" rx_c rx_l rx_p + + # count the data integrity frames + if [statList get $rx_c $rx_l $rx_p] { + errorMsg "Error getting data integrity frame statistics for [getPortId $rx_c $rx_l $rx_p]" + } else { + + if {[catch {statList cget -dataIntegrityFrames} rxFrames($rx_c,$rx_l,$rx_p)]} { + errorMsg "***** WARNING:Invalid statistic (dataIntegrityFrames) for the port:[getPortString $rx_c $rx_l $rx_p]\n" + set rxFrames($rx_c,$rx_l,$rx_p) 0 + set retCode $::TCL_ERROR + } + mpincr totalRxFrames $rxFrames($rx_c,$rx_l,$rx_p) + + if {[catch {statList cget -dataIntegrityErrors} errors($rx_c,$rx_l,$rx_p)]} { + errorMsg "***** WARNING:Invalid statistic (dataIntegrityErrors) for the port:[getPortString $rx_c $rx_l $rx_p]\n" + set errors($rx_c,$rx_l,$rx_p) 0 + set retCode $::TCL_ERROR + } + mpincr totalErrorFrames $errors($rx_c,$rx_l,$rx_p) + } + } + return $retCode +} + +######################################################################## +# Procedure: collectSequenceStats +# +# This command polls the Sequence counters +# +# Argument(s): +# rxPortList - list of receive ports +# Errors - array containing the error stats +# RxFrames - received sequence frames per rx port +# TotalRxFrames - total received sequence frames +# +######################################################################## +proc collectSequenceStats {rxPortList Errors RxFrames {TotalRxFrames ""} {TotalErrorFrames ""}} \ +{ + upvar $Errors errors + upvar $RxFrames rxFrames + + if {[info exists TotalRxFrames]} { + upvar $TotalRxFrames totalRxFrames + } + + if {[info exists TotalErrorFrames]} { + upvar $TotalErrorFrames totalErrorFrames + } + + set retCode $::TCL_OK + set totalRxFrames 0 + set totalErrorFrames 0 + + logMsg "Collecting sequence statistics ..." + + set retCode [requestStats rxPortList] + + foreach rxMap $rxPortList { + scan $rxMap "%d %d %d" rx_c rx_l rx_p + + # count the data integrity frames + if [statList get $rx_c $rx_l $rx_p] { + errorMsg "Error getting data integrity frame statistics for [getPortId $rx_c $rx_l $rx_p]" + } else { + + if {[catch {statList cget -sequenceFrames} rxFrames($rx_c,$rx_l,$rx_p)]} { + errorMsg "***** WARNING:Invalid statistic (dataIntegrityFrames) for the port:[getPortString $rx_c $rx_l $rx_p]\n" + set rxFrames($rx_c,$rx_l,$rx_p) 0 + set retCode $::TCL_ERROR + } + mpincr totalRxFrames $rxFrames($rx_c,$rx_l,$rx_p) + + if {[catch {statList cget -sequenceErrors} errors($rx_c,$rx_l,$rx_p)]} { + errorMsg "***** WARNING:Invalid statistic (dataIntegrityErrors) for the port:[getPortString $rx_c $rx_l $rx_p]\n" + set errors($rx_c,$rx_l,$rx_p) 0 + set retCode $::TCL_ERROR + } + mpincr totalErrorFrames $errors($rx_c,$rx_l,$rx_p) + } + } + return $retCode +} + +######################################################################## +# Procedure: collectErroredFramesStats +# +# This command collects the counters for the errored frames received +# +# Argument(s): +# rxPortList - receive port list +# ErrorredFrames - array containing the number of errored frames per rx port +# errorList - list that contains errors, example {oversize undersize} +# +# NOTE: This proc is used by custom code, don't remove it +# +######################################################################## +proc collectErroredFramesStats {rxPortList ErrorredFrames errorList} \ +{ + upvar $ErrorredFrames errorredFrames + + set retCode 0 + set retCode [requestStats rxPortList] + + foreach rxMap $rxPortList { + scan $rxMap "%d %d %d" rx_c rx_l rx_p + + if [statList get $rx_c $rx_l $rx_p] { + errorMsg "Error getting statList for [getPortId $rx_c $rx_l $rx_p]" + } else { + foreach errType $errorList { + if [catch {statList cget -$errType} statValue] { + logMsg "\n\n***** WARNING: Invalid statistic ($errType) for the port:[getPortString $rx_c $rx_l $rx_p] \n" + set errorredFrames($errType,$rx_c,$rx_l,$rx_p) "N/A" + } else { + set errorredFrames($errType,$rx_c,$rx_l,$rx_p) [statList cget -$errType] + } + } + } + } + + return $retCode +} + +######################################################################## +# Procedure: collectQosStats +# +# This command polls the QoS counters +# +# Argument(s): +# rxList - list of receive ports +# RxQosNumFrames - array containing the returned rx stats per priority +# TotalQosFrames - array containing the total Qos frames, per priority +# TotalRxFrames - total received frames +# printError - if no frames received, print error message +# +######################################################################## +proc collectQosStats {rxList RxQosNumFrames {TotalQosFrames ""} {TotalRxFrames ""} {printError yes}} \ +{ + upvar $RxQosNumFrames rxQosNumFrames + + if [info exists TotalQosFrames] { + upvar $TotalQosFrames totalQosFrames + } + + if [info exists TotalRxFrames] { + upvar $TotalRxFrames totalRxFrames + } + + set retCode 0 + + set numPriorities 8 + + # init the totals first + for {set i 0} {$i < $numPriorities} {incr i} { + set totalQosFrames($i) 0 + } + + set retCode [requestStats rxList] + + set totalRxFrames 0 + + # look at each qost stat + foreach rxMap $rxList { + scan $rxMap "%d %d %d" rx_c rx_l rx_p + + if [statList get $rx_c $rx_l $rx_p] { + errorMsg "Error getting Rx statistics for [getPortId $rx_c $rx_l $rx_p]" + set retCode 1 + } + + set totalRx($rx_c,$rx_l,$rx_p) 0 + + for {set priority 0} {$priority < $numPriorities} {incr priority} { + if [catch {statList cget -qualityOfService$priority} rxQosNumFrames($priority,$rx_c,$rx_l,$rx_p)] { + errorMsg "***** WARNING:Invalid statistic (qualityOfService$priority) for the port:[getPortString $rx_c $rx_l $rx_p]\n" + set rxQosNumFrames($priority,$rx_c,$rx_l,$rx_p) 0 + set retCode 1 + } + mpincr totalQosFrames($priority) $rxQosNumFrames($priority,$rx_c,$rx_l,$rx_p) + mpincr totalRxFrames $rxQosNumFrames($priority,$rx_c,$rx_l,$rx_p) + mpincr totalRx($rx_c,$rx_l,$rx_p) $rxQosNumFrames($priority,$rx_c,$rx_l,$rx_p) + + } + + if {$totalRx($rx_c,$rx_l,$rx_p) == 0 && $printError == "yes"} { + logMsg "\n\n\tError !!" + logMsg "\tZero packets received on port [getPortId $rx_c $rx_l $rx_p]" + logMsg "\tCheck the connections." + } + + logMsg "[getPortId $rx_c $rx_l $rx_p]: Total frames received: $totalRx($rx_c,$rx_l,$rx_p)" + } + + return $retCode +} + + +######################################################################## +# Procedure: collectStats +# +# This command polls the RX counters for the specified stat +# +# Argument(s): +# rxList - list of receive ports +# statNameList - list name of stats to poll, return total (need cget name) +# RxNumFrames - array containing the returned rx stats +# TotalRxFrames - total received frames +# +######################################################################## +proc collectStats {rxList statNameList RxNumFrames {TotalRxFrames ""} {verbose verbose} } \ +{ + upvar $RxNumFrames rxNumFrames + if {[info exists TotalRxFrames]} { + upvar $TotalRxFrames totalRxFrames + } + + set retCode 0 + set totalRxFrames 0 + + logMsg "Collecting [join $statNameList] statistics ..." + set retCode [requestStats rxList] + + foreach rxMap [lnumsort $rxList] { + scan $rxMap "%d %d %d" rx_c rx_l rx_p + + # count the Rx Frames + if [statList get $rx_c $rx_l $rx_p] { + errorMsg "Error getting Rx statistics for [getPortId $rx_c $rx_l $rx_p]" + set retCode 1 + } else { + set rxNumFrames($rx_c,$rx_l,$rx_p) 0 + foreach statName $statNameList { + # make sure we're using the proper stat name!! + if {[string first stat $statName] == 0} { + set statName [string trimleft $statName stat] + set firstChar [string tolower [string index $statName 0]] + append firstChar [string range $statName 1 end] + set statName $firstChar + } + + if [catch {statList cget -$statName} value] { + if {$verbose == "verbose"} { + errorMsg "***** WARNING:Invalid statistic ($statName) for the port:[getPortString $rx_c $rx_l $rx_p]\n" + } + set value 0 + set retCode 1 + } + + mpincr rxNumFrames($rx_c,$rx_l,$rx_p) $value + } + mpincr totalRxFrames $rxNumFrames($rx_c,$rx_l,$rx_p) + } + } + + return $retCode +} + + +######################################################################## +# Procedure: getNumErroredFrames +# +# This command gets the counter that contains the number of errored frames +# received +# +# Argument(s): +# chassis chassis ID +# lm Load Module number +# port port number +# error allErrors OR {oversize|undersize|alignment|dribble|badCRC} +# +######################################################################## +proc getNumErroredFrames {chassis lm port {error allErrors}} \ +{ + set numRxFrames 0 + + set errList {fragments undersize oversize fcsErrors symbolErrors \ + alignmentErrors dribbleErrors collisions lateCollisions \ + collisionFrames excessiveCollisionFrames oversizeAndCrcErrors \ + symbolErrorFrames synchErrorFrames} + + if [stat get statAllStats $chassis $lm $port] { + errorMsg "Error getting statistics for $chassis,$lm,$port" + } else { + if {$error != "allErrors"} { + set errList $error + } + foreach error $errList { + if [catch {stat cget -$error} msg] { + errorMsg $msg + } else { + catch {mpincr numRxFrames [stat cget -$error]} + } + } + } + + return $numRxFrames +} + + +######################################################################## +# Procedure: checkLinkState +# +# This command checks the link state of all ports in parallel and labels +# the ones that are down. Then it polls the links that are down for two +# seconds and returns 1 if any port is still down and a 0 if all ports are +# up. +# +# Arguments(s): +# PortArray array or list of ports, ie, ixgSortMap +# PortsToRemove list containing the ports to be removed +# +######################################################################## +proc checkLinkState {PortArray {PortsToRemove ""} {message messageOn}} \ +{ + upvar $PortArray portArray + upvar $PortsToRemove portsToRemove + + set retCode $::TCL_OK + + if {$message == "messageOn"} { + set logger "logMsg" + } else { + set logger "debugMsg" + } + + eval $logger "Checking link states on ports ..." + + after 1000 ;# give the port some time to begin it's in autonegotiate mode or PPP + + set portList [getAllPorts portArray] + set validLinkUpStates [list $::linkUp $::pppUp $::linkLoopback $::forcedLinkUp] + + # go through all the ports and label the ones whose links are down + foreach portMap $portList { + scan $portMap "%d %d %d" tx_c tx_l tx_p + debugMsg "checking link state on [getPortId $tx_c $tx_l $tx_p]" + + if {[lsearch $validLinkUpStates [stat getLinkState $tx_c $tx_l $tx_p]] < 0} { + set linkState($tx_c,$tx_l,$tx_p) 1 + debugMsg "Link on Tx port [getPortId $tx_c $tx_l $tx_p] is down." + } + } + + # the linkState array are all the ports whose links are down. Now poll + # them a few times until they are all up or return. + set loopCount [expr [advancedTestParameter cget -linkStateTimeout] * 2] + for {set ctr 0} {$ctr < $loopCount} {incr ctr} { + foreach downlink [array names linkState] { + scan $downlink "%d,%d,%d" c l p + + if {[lsearch $validLinkUpStates [stat getLinkState $c $l $p]] >= 0} { + debugMsg "Link on port [getPortId $c $l $p] is now up." + unset linkState($c,$l,$p) + } + } + if {[llength [array names linkState]] == 0} { + break + } else { + after 500 + } + } + + set portsToRemove [getTxPorts linkState] + + if {[llength [array names linkState]] == 0} { + eval $logger "Links on all ports are up." + } else { + logMsg "Link on these ports are down:" + foreach downlink [array names linkState] { + scan $downlink "%d,%d,%d" c l p + logMsg [getPortId $c $l $p] + } + set retCode $::TCL_ERROR + } + + after [advancedTestParameter cget -dutDelay] + + return $retCode +} + + +######################################################################## +# Procedure: checkPPPState +# +# This command checks the PPP state of all PoS ports in parallel and labels +# the ones that are down. Then it polls the links that are down for two +# seconds and returns 1 if any port is still down and a 0 if all ports are +# up. +# +# Arguments(s): +# PortArray array or list of ports, ie, ixgSortMap +# +######################################################################## +proc checkPPPState {PortArray {message messageOn}} \ +{ + upvar $PortArray portArray + + set retCode $::TCL_OK + + if {$message == "messageOn"} { + set logger "logMsg" + } else { + set logger "debugMsg" + } + + eval $logger "Checking PPP states on ports ..." + + after 1000 ;# give the port some time to begin it's in autonegotiate mode or PPP + + set portList [getAllPorts portArray] + set validLinkUpStates [list $::pppUp ] + + # go through all the ports and label the ones whose links are down + foreach portMap $portList { + scan $portMap "%d %d %d" c l p + + if [IsPOSPort $c $l $p] { + debugMsg "checking link state on [getPortId $c $l $p]" + + if {[lsearch $validLinkUpStates [stat getLinkState $c $l $p]] < 0} { + set linkState($c,$l,$p) [stat getLinkState $c $l $p] + debugMsg "Link on Tx port [getPortId $c $l $p] is down" + } + } + } + + # the linkState array are all the ports whose links are down. Now poll + # them a few times until they are all up or return. + set loopCount [expr [advancedTestParameter cget -linkStateTimeout] * 4] + for {set ctr 0} {$ctr < $loopCount} {incr ctr} { + foreach downlink [array names linkState] { + scan $downlink "%d,%d,%d" c l p + set state [stat getLinkState $c $l $p] + + # if the state has transitioned to something else, print it & check it... + if {$state != $linkState($c,$l,$p)} { + if {$state == $::pppUp} { + debugMsg "Link on port [getPortId $c $l $p] is now up." + unset linkState($c,$l,$p) + } else { + set linkState($c,$l,$p) $state + } + } + } + if {[llength [array names linkState]] == 0} { + break + } else { + after 250 + } + } + + if {[llength [array names linkState]] == 0} { + if {$message == "messageOn"} { + logMsg "Links on all ports are up." + } else { + debugMsg "Links on all ports are up." + } + } else { + logMsg "Link on these ports are down:" + foreach portItem [array names linkState] { + logMsg $portItem + } + set retCode $::TCL_ERROR + } + + return $retCode +} + + +######################################################################## +# Procedure: getRunningRate +# +# Description: Gets the running rates of a list of ports +# +# Argument(s): +# portList - list of ports or array contains tx and rx ports +# RunningRate - array w/rate values +# sampleNum - for keeping track of different sample times +# +######################################################################## +proc getRunningRate {portList RunningRate args {sampleNum 1}} \ +{ + upvar $RunningRate runningRate + + set retCode $::TCL_OK + + requestStats portList + + # even if we got an error, go ahead & fill array so we don't crash anyone... + foreach portMap $portList { + scan $portMap "%d %d %d" c l p + + if [statList getRate $c $l $p] { + errorMsg "Error getting rates on port $c,$l,$p" + set retCode $::TCL_ERROR + } + + foreach counter [join $args] { + set failCount 0 + set tempCounter $counter + + if [catch {statList cget -$counter} currRate($counter)] { + + switch $counter { + + "framesReceived" { + set tempCounter atmAal5FramesReceived + if [catch {statList cget -atmAal5FramesReceived} currRate($counter)] { + incr failCount + } + } + "scheduledFramesSent" { + set tempCounter framesSent + if [catch {statList cget -framesSent} currRate($counter)] { + incr failCount + } + } + } + + if { $counter == $tempCounter } { + incr failCount + } + } + + if { $failCount } { + errorMsg "$tempCounter is not a valid rate counter" + set currRate($counter) 0 + } + } + + if {[llength $args] == 1} { + set runningRate($c,$l,$p,$sampleNum) $currRate($args) + debugMsg "TX: runningRate($c,$l,$p,$sampleNum) = $runningRate($c,$l,$p,$sampleNum)" + } else { + foreach counter $args { + set runningRate($c,$l,$p,$counter,$sampleNum) $currRate($counter) + debugMsg "TX: runningRate($c,$l,$p,$counter,$sampleNum) = $runningRate($c,$l,$p,$counter,$sampleNum)" + } + } + } + + return $retCode +} + + + +################################################################################## +# Procedure: getRunRatePerSecond +# +# Description: Collects the rate during transmission at every second. +# +# Argument(s): +# TxRxArray - array of ports to transmit, ie. one2oneArray +# TxRateArray - array containing the running rate for tx ports +# RxRateArray - array containing the receive rate for rx ports +# duration - duration of tx +# +# Note: We need to get rid of this method when we get rid of cable modem suite +# +################################################################################## +proc getRunRatePerSecond {TxRxArray TxRateArray RxRateArray duration} \ +{ + upvar $TxRxArray txRxArray + upvar $TxRateArray txRateArray + upvar $RxRateArray rxRateArray + + set retCode 0 + set count 0 + + set txPortList [getTxPorts txRxArray] + set rxPortList [getRxPorts txRxArray] + + if [createDialog "Transmit Status"] { + set cmd logMsg + } else { + set cmd writeDialog + } + + set currentTime [clock seconds] + + for {set timeCtr 1} {$timeCtr <= $duration} {incr timeCtr} { + $cmd "Transmitted $timeCtr of $duration seconds" + mpincr count + + if [getRunningRate $txPortList txRateArray scheduledFramesSent $count] { + set retCode 1 + } + if {$retCode == 0 && [getRunningRate $rxPortList rxRateArray framesReceived $count]} { + set retCode 1 + } + + while {[expr {[clock seconds] - $currentTime}] < 1} { + update idletasks + after 20 + } + set currentTime [clock seconds] + } + debugMsg "txRateArray: [array get txRateArray] " + debugMsg "rxRateArray: [array get rxRateArray]" + + incr timeCtr -1 + if {$duration != $timeCtr} { + logMsg "******* Test terminated by user after $timeCtr seconds" + } + + logMsg "Done transmitting for $duration seconds...\n" + + # destroy the dialog box if it is created + if {$cmd == "writeDialog"} { + destroyDialog + } + + return $retCode +} + + + +################################################################################## +# Procedure: collectRates +# +# Description: Collects the rate during transmission at the following points: +# 1. Half way during tx +# 2. 15% of half duration before the half way point +# 3. 15% of half duration after the half way point +# +# Then get the average of these rates for the QoS counters!!. +# +# Argument(s): +# AvgRateArray - array containing the average running rate for tx/rx ports +# statNameList - name of the stats that rates will be collected +# RxRateArray - array containing the average rx running rate for the corresponding +# rateType: rxRate (regular) or qosRate (qos counters) +# duration - duration of tx +# rateType - the type of the rate will be collected +# +################################################################################## +proc collectRates { TxRxArray AvgRateArray duration {rateType rxRate} { RxRateArray ""} } \ +{ + upvar $TxRxArray txRxArray + upvar $AvgRateArray avgRateArray + upvar $RxRateArray rxRateArray + + set retCode $::TCL_OK + set sampleCount 0 + set numSamples 3 + set count 1 + set txStartTime [clock seconds] + set runTime 0 + set sampleIndex 0 + set txPortList [getTxPorts txRxArray] + set rxPortList [getRxPorts txRxArray] + + if {$rateType == "qosRate" } { + set statNameList [list qualityOfService0 qualityOfService1 qualityOfService2 qualityOfService3 \ + qualityOfService4 qualityOfService5 qualityOfService6 qualityOfService7] + } else { + set statNameList [list userDefinedStat2] + + } + + # it may take very long time to get all Qos stats, so we want to start earlier... + set sampleDuration2 [ixMath::max [mpexpr $duration/2] 5] + set sampleDuration1 [mpexpr $sampleDuration2 - round((.15 * $sampleDuration2))] + set sampleDuration3 [mpexpr $sampleDuration2 + round(ceil((.15 * $sampleDuration2)))] + debugMsg "***** sampleDuration2 = $sampleDuration2, sampleDuration1 = $sampleDuration1, sampleDuration3 = $sampleDuration3" + set sampleTimes [list $sampleDuration1 $sampleDuration2 $sampleDuration3 -1] + + if [createDialog "Transmit Status"] { + set cmd logMsg + } else { + set cmd writeDialog + } + + set nextSample [lindex $sampleTimes $sampleIndex] + + set percentToDuration 0 + set sampleCount 0 + while {$runTime <= $duration} { + set currentTime [clock seconds] + set percentToDuration [mpexpr int(ceil(double($runTime)/$duration*100))] + + $cmd "Transmitted $percentToDuration% of $duration seconds" + + if { ($nextSample > 0) && ($runTime >= $nextSample) } { + + set startSample $runTime + + if {[getRunningRate $txPortList txRunningRate scheduledFramesSent $count]} { + set retCode $::TCL_ERROR + } + + if {[getRunningRate $rxPortList rxRunningRate $statNameList $count]} { + set retCode $::TCL_ERROR + } + + set runTime [expr ([clock seconds] - $txStartTime)] + + if {$runTime > $duration } { + logMsg "\nWARNING!!!: Sampled rate values maybe incorrect. Increase test duration." + } + + incr count + incr sampleIndex + set nextSample [lindex $sampleTimes $sampleIndex] + incr sampleCount + } + + while {[expr {[clock seconds] - $currentTime}] < 1} { + update idletasks + after 20 + } + set runTime [expr ([clock seconds] - $txStartTime)] + } + + if { $sampleCount < $numSamples } { + # We don't want to miss the last transmit status + $cmd "Transmitted 100% of $duration seconds" + } + + logMsg "Done transmitting for $duration seconds...\n" + + if {$duration > $runTime} { + logMsg "******* Test terminated by user after $runTime seconds" + } + + + # destroy the dialog box if it is created + if {$cmd == "writeDialog"} { + destroyDialog + } + + if {!$retCode } { + + # get the average running rate for transmitted frame, received frame & qos rates + foreach txMap $txPortList { + scan $txMap "%d %d %d" tx_c tx_l tx_p + set total 0 + for {set i 1} {$i <= $sampleCount} {incr i} { + if {![info exists txRunningRate($tx_c,$tx_l,$tx_p,$i)]} { + logMsg "Running rate for [getPortId $tx_c $tx_l $tx_p] sample $i not calculated during transmission" + set retCode $::TCL_ERROR + } else { + mpincr total $txRunningRate($tx_c,$tx_l,$tx_p,$i) + } + } + + if {$rateType == "qosRate" } { + if [catch {mpexpr $total/$sampleCount} avgRateArray($tx_c,$tx_l,$tx_p)] { + set avgRateArray($tx_c,$tx_l,$tx_p) 0 + } + } else { + + if [catch {mpexpr $total/$sampleCount} avgRateArray(TX,$tx_c,$tx_l,$tx_p)] { + set avgRateArray(TX,$tx_c,$tx_l,$tx_p) 0 + } + } + debugMsg "avgRateArray:[array get avgRateArray]" + } + + if {$rateType == "qosRate" } { + foreach rxMap $rxPortList { + scan $rxMap "%d %d %d" rx_c rx_l rx_p + + for {set priority 0} {$priority < 8} {incr priority} { + set total 0 + for {set i 1} {$i <= $sampleCount} {incr i} { + set counter qualityOfService$priority + if {![info exists rxRunningRate($rx_c,$rx_l,$rx_p,$counter,$i)]} { + logMsg "QOS Running rate, priority $priority for [getPortId $rx_c $rx_l $rx_p] sample $i not calculated during transmission." + set retCode $::TCL_ERROR + } else { + mpincr total $rxRunningRate($rx_c,$rx_l,$rx_p,$counter,$i) + } + } + if [catch {mpexpr round($total/$sampleCount)} rxRateArray($rx_c,$rx_l,$rx_p,$priority)] { + rxRateArray($rx_c,$rx_l,$rx_p,$priority) 0 + } + } + } + } else { + foreach rxMap $rxPortList { + scan $rxMap "%d %d %d" rx_c rx_l rx_p + set total 0 + for {set i 1} {$i <= $sampleCount} {incr i} { + if {![info exists rxRunningRate($rx_c,$rx_l,$rx_p,$i)]} { + logMsg "Running rate for [getPortId $rx_c $rx_l $rx_p] sample $i not calculated during transmission." + set retCode $::TCL_ERROR + } else { + mpincr total $rxRunningRate($rx_c,$rx_l,$rx_p,$i) + } + } + if [catch {mpexpr $total/$sampleCount} avgRateArray(RX,$rx_c,$rx_l,$rx_p)] { + set avgRateArray(RX,$rx_c,$rx_l,$rx_p) 0 + } + } + } + } + + return $retCode +} + + diff --git a/dep/tclclient/ixTcl1.0/Generic/stringUtils.tcl b/dep/tclclient/ixTcl1.0/Generic/stringUtils.tcl new file mode 100644 index 00000000..6ac77997 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/stringUtils.tcl @@ -0,0 +1,632 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: stringUtils.tcl +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 03-07-2001 DS Genesis +# +# Description: This file contains string helpers, including wrappers +# for string commands not supported in 8.0 +# +######################################################################## + + +################################################################################## +# Procedure: capitalizeString +# +# This command returns the string w/the first char toupper +# +# Argument(s): +# str string to capitalize +# +################################################################################## +proc capitalizeString {str} \ +{ + if {[info tclversion] > 8.0} { + set capString [string totitle $str] + } else { + set capString [format "%s%s" [string toupper [string index $str 0]] [string range $str 1 end]] + } + return $capString +} + + + +################################################################################## +# Procedure: stringRepeat +# +# Helper proc to create a string consists of specialized charaters or strings. +# +# Argument(s): +# string - the string to repeat, eg. "*", "," ... +# repeatCount - number of times to repeat the string +# +################################################################################## +proc stringRepeat {string repeatCount} \ +{ + if {$repeatCount <= 0} { + return "" + } + + # string repeat is a new option added in Tcl8.1 + if {[info tclversion] > 8.0} { + set outPutString [string repeat $string $repeatCount] + + } else { + regsub -all {[ ]} [format "%$repeatCount\s" " "] $string outPutString + } + + return $outPutString +} + + +################################################################################## +# Procedure: stringIsInteger +# +# Helper proc to verify if a string is an integer value or not. +# +# Argument(s): +# string - the string to check +# +# Return value: +# 1 if true +# 0 if false +# +################################################################################## +proc stringIsInteger {string} \ +{ + # string is integer is a new option added in Tcl8.1 + if {[info tclversion] > 8.0} { + set retCode [string is integer $string] + } else { + set retCode 1 + if [catch {format %d $string}] { + set retCode 0 + } + } + + return $retCode +} + + +################################################################################## +# Procedure: stringIsDouble +# +# Helper proc to verify if a string is an floating value or not. +# +# Argument(s): +# string - the string to check +# +# Return value: +# 1 if true +# 0 if false +# +################################################################################## +proc stringIsDouble {string} \ +{ + # string is integer is a new option added in Tcl8.1 + if {[info tclversion] > 8.0} { + set retCode [string is double $string] + } else { + set retCode 1 + if [catch {format %f $string}] { + set retCode 0 + } + } + + return $retCode +} + + +################################################################################## +# Procedure: stringSubstitute +# +# Helper proc to replace a string w/in another string. +# +# Argument(s): +# string1 - string to look in +# old - the string to replace +# new - replace w/this string +# +# Return value: +# new string +# +################################################################################## +proc stringSubstitute {string1 old new} \ +{ + set oldLength [string length $old] + set index [string first $old $string1] + if {$index < 0} { + set newString $string1 + } else { + + # string is integer is a new option added in Tcl8.1 + if {[info tclversion] > 8.0} { + set newString [string replace $string1 $index [expr $index + $oldLength - 1] $new] + } else { + set newString [format %s%s%s [string range $string1 0 [expr $index - 1]] $new \ + [string range $string1 [expr $oldLength + $index] end]] + } + } + + return $newString +} + +################################################################################## +# Procedure: stringUnderscore +# +# Description: Given an alpha-numeric string, build the same string underscored. +# Tabs are left in-tact by default, but may be ignored. +# +# Given the string: +# "Now is the time for all good people to come to the aid of their universe" +# +# Returns: +# "Now is the time for all good people to come to the aid of their universe" +# ======================================================================== +# +# OR... +# +# Given a string with tabs: +# "This is a\ttab." +# +# Returns: +# "This is a\ttab." +# =========\t==== +# +# Looks like: +# +# "This is a tab." +# ========= ==== + +# +# Input: theString: string to underscore +# underscore: default is "=" +# tabs: ignore OR observe (default) +# +# Output: underscored string +# +################################################################################## +proc stringUnderscore {theString {underscore "="} {tabs "observe"}} { + + if [string length $theString] { + + # Strip out tabs if ignoring. + if {$tabs != "observe"} { + regsub -all \t $theString " " theString + } + if {[info tclversion] > 8.0} { + regsub -all {[[:alnum:][:punct:] ]} $theString $underscore underscores + } else { + regsub -all {[-a-zA-z0-9 ;:"/?.<>!@#$%&*()+']} $theString $underscore underscores + } + + return $theString\n$underscores\n + } + +} + +################################################################################## +# Procedure: stringTitle +# +# Description: Convert a string to a title, ie all words capitalized. +# +# Given the string: +# "Now is the time for all good people to come to the aid of their universe" +# +# Returns: +# "Now is the Time For All Good People to Come to the Aid of Their Universe" +# +# +# Input: theString: string to convert +# whichWords: significant (default) OR all +# +# Output: new string +# +# +# This capitalizes all words: +# +# +################################################################################## +proc stringTitle {theString {whichWords "significant"}} \ +{ + + if {$whichWords == "significant"} { + set title "" + foreach i $theString { + switch -- $i { + the - + is - + to - + of - + and { + append title "$i " + } + + default { + append title "[stringToUpper $i 0 0] " + } + } + } + set title [string range $title 0 [expr [string length $theString] -1]] + } else { + regsub -all {(.+?\M)} $theString {[stringToUpper \1 0 0] } title + set title [string trim [subst $title]] + } + + return $title +} + + + + +################################################################################## +# Procedure: stringSplitToTitle +# +# Description: Splits a string into a title, ie all words capitalized. +# +# Given the string: +# "headerBytesReceived" +# Returns: +# "Header Bytes Received" +# +# +# Input: theString: string to convert +# +# Output: new string +# +# +# This capitalizes all words: +# +# +################################################################################## +proc stringSplitToTitle {theString} \ +{ + regsub -all {[A-Z]} $theString " &" newText + set theString [stringToUpper $newText 0 0] + + return $theString +} + +################################################################################## +# Procedure: stringJoinFromTitle +# +# Description: Joins a title into a string, the first letter of each word (except +# the first is kept/made uppercase, for example: +# +# Given the string: +# "Site Level Aggregation Id" +# Returns: +# "siteLevelAggregationId" +# +# +# Input: theString: string to convert +# +# Output: new string +# +################################################################################## +proc stringJoinFromTitle {theString} \ +{ + set theString [string tolower [stringTitle $theString all] 0 0] + regsub -all { } $theString "" theString + + return $theString +} + + + + +################################################################################## +# Procedure: stringToUpper +# +# Description: Return string in upper case +# +# +# +# Input: theString: string to convert +# index1 : If it is specified, it refers to the first char index in the string to start modifying. +# index2 : If it is specified, it refers to the char index in the string to stop at. +# +# Output: new string +# +# +################################################################################## +proc stringToUpper {theString {index1 -1} {index2 -1}} \ +{ + if {[info tclversion] > 8.0} { + if { $index1 == -1 && $index2 == -1 } { + set newString [string toupper $theString] + } elseif { $index2 == -1 } { + set newString [string toupper $theString $index1] + } else { + set newString [string toupper $theString $index1 $index2] + } + + } else { + if {$index1 == -1 && $index2 == -1 } { + set newString [string toupper $theString] + } elseif { $index2 < $index1 && $index2 != -1} { + set newString $theString + } else { + if {$index2 == -1} { + set index2 $index1 + } + set part1 [string range $theString 0 [expr $index1 - 1]] + set part2 [string range $theString $index1 $index2] + set part3 [string range $theString [expr $index2 + 1] [string length $theString]] + set newString [append part1 [string toupper $part2] $part3] + + } + } + return $newString +} + +######################################################################################## +# Procedure: stringMap +# +# Description: Given a translation map, perform character tranlation (same function +# as [string map] available in 8.3. +# +# Arguments: value: string of characters +# +# Returns: retValue: string with characters mapped to the corresponding +# characters in the map. Note that characters not in the +# map are returned unaltered. +# +# +######################################################################################## +proc stringMap {{map ""} value} \ +{ + set retValue "" + + array set translation $map + + set length [string length $value] + for {set i 0} {$i < $length} {incr i} { + + set translatedValue [string index $value $i] + if {[info exists translation($translatedValue)]} { + set translatedValue $translation($translatedValue) + } + append retValue $translatedValue + } + + return $retValue +} + + +######################################################################################## +# Procedure: stringCompare +# +# Description: Compare the given strings with option that are all specified by the +# argument "args". +# +# Arguments: args - a list that contains options and strings to be compared +# +# +# Returns: retValue - the result of the comparison. +# +# +######################################################################################## +proc stringCompare {args} \ +{ + set retValue 1 + + if { [info tclversion] > 8.0 } { + + if [catch {eval "string compare $args"} result] { + errorMsg $result + } else { + set retValue $result + } + + } else { + + set flagNocase 0 + set flagLength 0 + + foreach arg $args { + + switch -- $arg { + "-nocase" { + set flagNocase 1 + } + "-length" { + set flagLength 1 + set state length + } + default { + + if { [info exists state] && ($state == "length") } { + + set length $arg + catch {unset state} + + if { ![stringIsInteger $length] } { + errorMsg "expected integer but got \"$length\"" + break + } + + } elseif { [info exists string1] == 0 } { + set string1 $arg + } elseif { [info exists string2] == 0 } { + set string2 $arg + } + } + } + } + + if { [info exists string1] && [info exists string2] } { + + if { $flagLength == 1 } { + + if { $length > 0 } { + set index [expr $length - 1] + set string1 [string range $string1 0 $index] + set string2 [string range $string2 0 $index] + } else { + set string1 "" + set string2 "" + } + } + + if { $flagNocase == 1 } { + set string1 [string tolower $string1] + set string2 [string tolower $string2] + } + + if [catch {eval "string compare \"$string1\" \"$string2\""} result] { + errorMsg $result + } else { + set retValue $result + } + + } else { + errorMsg "wrong # args: should be \"stringCompare ?-nocase? ?-length int? string1 string2\"" + } + + return $retValue + } +} + +################################################################################## +# Procedure: stringFormatNumber +# +# Description: Take a string on numbers (doesn't support special characters yet) +# and insert commas appropriately. +# +# Note: normally the value to be converted is in the parameter +# string, when called by a GUI callback function, the value is +# in args +# +# Handles: -/+ in initial position +# comma insertion +# removal of non-numeric data +# integer with decimal +# +# Arguments: string +# args +# +# Returns: formatted string +# +################################################################################## +proc stringFormatNumber { value {args {}} } \ +{ + set retValue 0 + + if {[llength $args] > 0} { + set value [lindex $args 1] + } + + # Check if there is any numeric characters in the string + regsub -all {[^0-9]} $value "" temp + + if { [string length $temp] > 0 } { + set retValue "" + + # Remember sign. + if {[string compare [string index $value 0] "-"] == 0} { + set sign $::true + } else { + set sign $::false + } + + # Remove non-numeric characters (except ".") + regsub -all {[^0-9\.]} $value "" value + + set useDecimal [expr [scan [split $value .] "%s %s" integer decimal] > 1 ? $::true:$::false] + + # remove leading '0's before adding commas from INTEGER PORTION ONLY!! + set integer [string trimleft $integer '0'] + if {[string length $integer] == 0} { + set integer 0 + } + + if {[string length $integer] > 0} { + + set repetition [mpexpr int([string length $integer] / 3)] + incr repetition -[expr [expr [mpexpr [string length $integer] % 3]>0] ? 0:1] + + for {set i $repetition} {$i > 0} {incr i -1} { + set startIndex [expr [string length $integer] - 3] + set triplet [string range $integer $startIndex end] + set integer [stringReplace $integer $startIndex end] + set retValue ",$triplet$retValue" + } + } + + set retValue "$integer$retValue" + if {$useDecimal} { + set retValue [join [list $retValue $decimal] .] + } + + if {$sign} { + set retValue [format "%s%s" "-" $retValue] + } + } + + return $retValue +} + + +################################################################################## +# Procedure: stringReplace +# +# Description: Helper proc to replicate the string replace command which is not supported in tcl 8.0 +# +# Argument(s): oldString - the original string value +# first - the index of the starting point for the replacement +# last - the index of the ending point for the replacement, we will accept end, but not end minus a value +# newString - (optional, defaults to null) the new string to use +# +# Returns: Returns a new string created by replacing characters first through last with newString, or nothing. +################################################################################## +proc stringReplace {oldString first last {newString ""}} \ +{ + # string replace is a new option added in Tcl8.1 + if {[info tclversion] > 8.0} { + set newString [string replace $oldString $first $last $newString] + } else { + if {$last == "end"} { + set last [expr [string length $oldString] - 1] + } + set newString [format %s%s%s [string range $oldString 0 [expr $first - 1]] $newString \ + [string range $oldString [expr $last + 1] end]] + } + + return $newString +} + + diff --git a/dep/tclclient/ixTcl1.0/Generic/switchLearn.tcl b/dep/tclclient/ixTcl1.0/Generic/switchLearn.tcl new file mode 100644 index 00000000..85222208 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/switchLearn.tcl @@ -0,0 +1,2030 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: switchLearn.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 08-13-1998 Hardev Soor +# +# Description: This file contains commands that send learn frames +# for various protocols to the switch (DUT). +# +######################################################################################## + +set ::udfList {1 2 3 4} + +######################################################################################## +#:::::::::::: MAC Layer Learn frames ::::::::::::: +######################################################################################## + +######################################################################################## +# Procedure: send_learn_frames +# +# Description: This command sends directed learn frames to allow the DUT to learn the +# mac addresses of the sending ports +# +# Argument(s): +# PortArray array of ports, ie., one2oneArray for a one2one configuration +# +######################################################################################## +proc send_learn_frames {PortArray {RemovedPorts ""} {staggeredStart true}} \ +{ + upvar $PortArray portArray + if {[string length $RemovedPorts] > 0} { + upvar $RemovedPorts removedPorts + } + + set retCode 0 + set removedPorts 0 + + set broadcastMac $::kBroadcastMacAddress + + set enable802dot1qTag [protocol cget -enable802dot1qTag] + + # set the stream parameters. + set preambleSize 8 + + stream setDefault + stream config -rateMode usePercentRate + stream config -name "LearnStream" + stream config -framesize [learn cget -framesize] + stream config -dma stopStream + stream config -gapUnit gapNanoSeconds + stream config -enableIbg false + stream config -enableIsg false + + # set frameType to "08 00" + if {[protocol cget -ethernetType] == $::ethernetII} { + stream config -frameType [advancedTestParameter cget -streamFrameType] + } + + set sentList "" + # note - send one port at portArray time to allow time for learning; + # remember, learn frames are sent from Rx to Tx... + foreach txMap [lnumsort [array names portArray]] { + scan $txMap "%d,%d,%d" tx_c tx_l tx_p + + if [IsPOSPort $tx_c $tx_l $tx_p] { + set txSA $broadcastMac + } else { + if [port get $tx_c $tx_l $tx_p] { + errorMsg "Port [getPortId $tx_c $tx_l $tx_p] has not been configured yet." + set retCode $::TCL_ERROR + continue + } + set txSA [port cget -MacAddress] + } + + foreach rxMap $portArray($txMap) { + scan [join $rxMap] "%d %d %d" rx_c rx_l rx_p + + if {![IsPOSPort $rx_c $rx_l $rx_p] && [lsearch $sentList [list $rx_c $rx_l $rx_p]] < 0} { + lappend sentList [list $rx_c $rx_l $rx_p] + + if {[port get $rx_c $rx_l $rx_p] == 1} { + errorMsg "Port [getPortId $rx_c $rx_l $rx_p] has not been configured yet." + continue + } + + set learnPercentRate [expr double([learn cget -rate])/[calculateMaxRate $rx_c $rx_l $rx_p [learn cget -framesize]]*100.] + stream config -percentPacketRate $learnPercentRate + + stream config -da $txSA + + stream config -sa [port cget -MacAddress] + stream config -numSA [port cget -numAddresses] + + if {[stream cget -numSA] > 1} { + stream config -saRepeatCounter increment + } + + set numFrames($rx_c,$rx_l,$rx_p) [expr [learn cget -numframes] * [port cget -numAddresses]] + stream config -numFrames $numFrames($rx_c,$rx_l,$rx_p) + + if [catch {expr int(ceil(double($numFrames($rx_c,$rx_l,$rx_p))/[learn cget -rate]))} duration] { + errorMsg "$duration" + set retCode $::TCL_ERROR + set duration 1 + } + + disableUdfs {1 2 3 4} + + if {![vlanUtils::isPortTagged $rx_c $rx_l $rx_p] && $enable802dot1qTag} { + protocol config -enable802dot1qTag false + } + + if [stream set $rx_c $rx_l $rx_p 1] { + errorMsg "Error setting stream for learning frames on [getPortId $rx_c $rx_l $rx_p] 1." + set retCode $::TCL_ERROR + continue + } + } + } + } + + if {[llength $sentList] > 0} { + logMsg "Configuring learn frames ..." + + # zero stats everywhere to avoid confusion later... + zeroStats portArray + + if {$retCode == 0} { + logMsg "Sending learning frames to all ports..." + + writeConfigToHardware sentList + if [startTx sentList $staggeredStart] { + set retCode $::TCL_ERROR + } + } + + if {$duration < 1} { + set duration 1 + } + + after [learn cget -waitTime] + # Wait for all frames to be transmitted.. + writeWaitMessage "Transmit Status" "Transmitting learn frames" $duration + + statGroup setDefault + foreach rxMap $sentList { + scan $rxMap "%d %d %d" rx_c rx_l rx_p + + if [statGroup add $rx_c $rx_l $rx_p] { + errorMsg "Error adding port [getPortId $rx_c $rx_l $rx_p] to statGroup" + set retCode $::TCL_ERROR + continue + } + } + + # wait the min refresh for 10/100 cards to let the stats update... + after 800 + set tempSentList $sentList + set retry 20 + + while {([llength $tempSentList] > 0) && ($retry > 0)} { + if [statGroup get] { + errorMsg "Error getting stats for statGroup" + set retCode $::TCL_ERROR + } + + foreach rxMap $tempSentList { + scan $rxMap "%d %d %d" rx_c rx_l rx_p + + if [statList get $rx_c $rx_l $rx_p] { + errorMsg "Error getting Tx statistics for [getPortId $rx_c $rx_l $rx_p]" + set retCode $::TCL_ERROR + continue + } + + if [catch {statList cget -scheduledFramesSent} txNumFrames ] { + if [catch {statList cget -framesSent} txNumFrames ] { + set txNumFrames 0 + } else { + if [catch {statList cget -protocolServerTx} numProtocolServerFrames ] { + set numProtocolServerFrames 0 + } + set txNumFrames [mpexpr $txNumFrames - $numProtocolServerFrames] + if {[isNegative $txNumFrames]} { + set txNumFrames 0 + } + } + } + + if { $txNumFrames == $numFrames($rx_c,$rx_l,$rx_p) } { + set portIndex [lsearch $tempSentList [list $rx_c $rx_l $rx_p]] + if {$portIndex != -1} { + set tempSentList [lreplace $tempSentList $portIndex $portIndex] + } + } + } + if { [llength $tempSentList] == 0 } { + set retry 0 + } else { + incr retry -1 + } + } + + if {[llength $tempSentList] > 0} { + foreach rxMap $tempSentList { + scan $rxMap "%d %d %d" rx_c rx_l rx_p + logMsg "All Learn frames not sent on port [getPortId $rx_c $rx_l $rx_p]" + set retCode $::TCL_ERROR + } + } + + logMsg "Learning frames sent..." + } + + if {[fastpath cget -enable] == "true"} { + if [send_fastpath_frames portArray] { + errorMsg "Error sending fastpath frames" + set retCode $::TCL_ERROR + } + } + + protocol config -enable802dot1qTag $enable802dot1qTag + stream setDefault + + return $retCode +} + + +######################################################################################## +#:::::::::::: IP Layer Learn frames ::::::::::::: +######################################################################################## + +######################################################################################## +# Procedure: OLDsend_arp_frames +# +# Description: This command send the arp frames from rx to tx +# +# Argument(s): +# PortArray array of ports, ie., one2oneArray for a one2one configuration +# +######################################################################################## +proc OLDsend_arp_frames {PortArray {RemovedPorts ""}} \ +{ + upvar $PortArray portArray + if {[string length $RemovedPorts] > 0} { + upvar $RemovedPorts removedPorts + } + + set retCode 0 + + # This code has been added for "Send MAC Only" option + if {[learn cget -type] == "mac" } { + if [send_learn_frames portArray] { + return -code error -errorinfo "Error sending MAC learning frames" + } + return $retCode + } + + set removedPorts 0 + + disableUdfs $::udfList + filter setDefault + stream setDefault + udf setDefault + + # set the stream parameters. + set preambleSize 8 + set framesize [learn cget -framesize] + set enable802dot1qTag [protocol cget -enable802dot1qTag] + set duration [expr [learn cget -numframes] / [learn cget -rate]] + + stream config -rateMode usePercentRate + stream config -name "ArpStream" + stream config -framesize $framesize + stream config -dma stopStream + stream config -numFrames [learn cget -numframes] + stream config -fcs good + stream config -gapUnit gapNanoSeconds + + filter config -captureFilterPattern any + filter config -captureFilterError errGoodFrame + filter config -captureFilterFrameSizeEnable false + filter config -captureFilterFrameSizeEnable false + filter config -captureTriggerError errGoodFrame + + # zero stats everywhere to avoid confusion later... + zeroStats portArray + + # note - send one port at portArray time to allow time for learning; arp frames should + # be sent from tx->DUT & rx->DUT. + + set rxList [lnumsort [getAllPorts portArray]] + + set tempAppName [protocol cget -appName] + protocol config -appName Arp + + foreach txMap $rxList { + scan $txMap "%d %d %d" tx_c tx_l tx_p + + if [IsPOSPort $tx_c $tx_l $tx_p] { + set index [lsearch $rxList $txMap] + set rxList [lreplace $rxList $index $index] + continue + } + + if {[port get $tx_c $tx_l $tx_p] == 1 || [ip get $tx_c $tx_l $tx_p] == 1} { + errorMsg "Port [getPortId $tx_c $tx_l $tx_p] has not been configured yet." + set retCode 1 + continue + } + + set txSA [port cget -MacAddress] + set numTx [port cget -numAddresses] + + set txDA $::kBroadcastMacAddress + set txIP [ip cget -sourceIpAddr] + set txDutIP [ip cget -destDutIpAddr] + + + arp config -operation arpRequest + arp config -sourceProtocolAddr $txIP + arp config -destProtocolAddr $txDutIP + arp config -sourceHardwareAddr $txSA + arp config -destHardwareAddr $txDA + + if {$numTx > 1} { + arp config -sourceHardwareAddrMode arpIncrement + arp config -sourceHardwareAddrRepeatCount $numTx + arp config -sourceProtocolAddrMode arpIncrement + arp config -sourceProtocolAddrRepeatCount $numTx + stream config -numFrames [expr $numTx * [learn cget -numframes]] + if {[expr [stream cget -numFrames]/[learn cget -rate]] > $duration} { + set duration [expr [stream cget -numFrames]/[learn cget -rate]] + } + } + + if [arp set $tx_c $tx_l $tx_p] { + errorMsg "Error setting Arp for port [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + + set learnPercentRate [expr double([learn cget -rate])/[calculateMaxRate $tx_c $tx_l $tx_p $framesize]*100.] + stream config -percentPacketRate $learnPercentRate + + stream config -da $txDA + stream config -numDA 1 + + stream config -sa $txSA + stream config -numSA $numTx + if {$numTx > 1} { + stream config -saRepeatCounter increment + } + + stream config -patternType repeat + stream config -dataPattern allZeroes + + if {$enable802dot1qTag && ![vlanUtils::isPortTagged $tx_c $tx_l $tx_p]} { + protocol config -enable802dot1qTag false + } + + if [stream set $tx_c $tx_l $tx_p 1] { + errorMsg "Error setting stream 1 on [getPortId $tx_c $tx_l $tx_p] for ARP frames." + set retCode 1 + } + + protocol config -enable802dot1qTag $enable802dot1qTag + + # set up the pattern filter + filterPallette config -pattern1 [host2addr $txDutIP] + filterPallette config -patternOffset1 28 + if [filterPallette set $tx_c $tx_l $tx_p] { + errorMsg "Error setting filter pallette for [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + } + + # set the filter parameters on the receive port + debugMsg " configuring filters" + filter config -captureFilterEnable true + filter config -captureTriggerEnable true + if [filter set $tx_c $tx_l $tx_p] { + errorMsg "Error setting filters on [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + } + } + protocol config -appName $tempAppName + + if {[llength $rxList] == 0} { + return $retCode + } + + disableArpResponse rxList + + if [setCaptureMode rxList] { + set retCode 1 + } + + writeConfigToHardware rxList + + if {[checkLinkState rxList ]} { + set retCode 1 + } + + if [startCapture rxList] { + errorMsg "Error starting capture." + set retCode 1 + } + + foreach txMap $rxList { + scan $txMap "%d %d %d" tx_c tx_l tx_p + + if [arp get $tx_c $tx_l $tx_p] { + errorMsg "Error getting Arp on [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + } + + logMsg "sending ARP frame from [getPortId $tx_c $tx_l $tx_p], SrcPort: [arp cget -sourceProtocolAddr] to DestPort: [arp cget -destProtocolAddr]" + } + set retCode [startStaggeredTx rxList] + + # wait the min refresh for 10/100 cards to let the stats update... + after 800 + + after [learn cget -waitTime] + # Wait for all frames to be transmitted.. + for {set timeCtr 1} {$timeCtr <= $duration} {incr timeCtr} { + logMsg "Transmitted arp frames $timeCtr of $duration seconds" + after 1000 + } + + checkAllTransmitDone rxList + + foreach txMap $rxList { + scan $txMap "%d %d %d" tx_c tx_l tx_p + + # look for ARP responses + set maxArp 100 + if [captureBuffer get $tx_c $tx_l $tx_p 1 $maxArp] { + errorMsg "Error getting capture buffer for [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + set found 0 + set nFrame 1 + while {[captureBuffer getframe $nFrame] != 1 && $nFrame <= $maxArp} { + debugMsg "Getting frame $nFrame from Buffer ....." + set capframe [captureBuffer cget -frame] + if {([arp decode $capframe] == 0) && ([arp cget -operation] == $::arpReply)} { + set found 1 + break + } + incr nFrame + } + + if {$found == 0} { + logMsg "No ARP response frames received on [getPortId $tx_c $tx_l $tx_p]" + if {[learn cget -removeOnError] == "true"} { + logMsg "Removing port $txMap from map..." + if [array exists portArray] { + if [info exists portArray($c,$l,$p)] { + unset portArray($c,$l,$p) + set removedPorts 1 + } + foreach txMap [array names portArray] { + scan $txMap "%d,%d,%d" tx_c tx_l tx_p + + foreach rxMap $portArray($tx_c,$tx_l,$tx_p) { + set index [lsearch $portArray($tx_c,$tx_l,$tx_p) "$c $l $p"] + + if {$index >= 0} { + set portArray($tx_c,$tx_l,$tx_p) [lreplace $portArray($tx_c,$tx_l,$tx_p) $index $index] + set removedPorts 1 + } + if {[llength $portArray($tx_c,$tx_l,$tx_p)] <= 0} { + unset portArray($tx_c,$tx_l,$tx_p) + } + } + } + } else { + set index [lsearch [getAllPorts portArray] "$c $l $p"] + if {$index >= 0} { + set portArray [lreplace [getAllPorts portArray $index $index]] + set removedPorts 1 + } + } + + # remember to set an error if we removed all ports!! + if {![info exists portArray]} { + set retCode 1 + } else { + if {[llength [array get portArray]] <= 0} { + set retCode 1 + } + } + } else { + set retCode 1 + } + continue + } + + if [port get $tx_c $tx_l $tx_p] { + errorMsg "Error getting port [getPortId $tx_c $tx_l $tx_p] for storing DestMacAddress." + set retCode 1 + continue + } + port config -DestMacAddress [arp cget -sourceHardwareAddr] + if [port set $tx_c $tx_l $tx_p] { + errorMsg "Error setting port [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + } + logMsg "Got ARP RESPONSE on [getPortId $tx_c $tx_l $tx_p], DUT MAC address for [getPortId $tx_c $tx_l $tx_p]: [arp cget -sourceHardwareAddr]" + } + + if {[learn cget -waitTime] > 1000} { + logMsg "Waiting for DUT to settle down after learning for [expr [learn cget -waitTime]/1000] second(s)..." + } + + after [learn cget -waitTime] + + if {$retCode == 0 && [ipfastpath cget -enable] == "true"} { + if [send_ipfastpath_frames portArray] { + errorMsg "Error sending IP fastpath frames" + set retCode 1 + } + } elseif {$retCode == 0 && [fastpath cget -enable] == "true"} { + if [send_fastpath_frames portArray] { + errorMsg "Error sending IP fastpath frames" + set retCode 1 + } + } + + stream setDefault + + return $retCode +} + + +######################################################################################## +# Procedure: send_arp_frames +# +# Description: This command uses the protocol server to configure arp, send out arp +# requests & enable arp response +# +# Argument(s): +# PortArray array of ports, ie., one2oneArray for a one2one configuration +# +######################################################################################## +proc send_arp_frames {PortArray {RemovedPorts ""} {resetInterfaces true}} \ +{ + upvar $PortArray portArray + if {[string length $RemovedPorts] > 0} { + upvar $RemovedPorts removedPorts + } + + set retCode 0 + set numInterfaces 1 + set arpList [getAllPorts portArray] + + if [configureArp portArray arpList write $numInterfaces $resetInterfaces] { + errorMsg "Error configuring ARP" + set retCode 1 + } + if {[llength $arpList] != 0} { + + if {[sendArp portArray $arpList removedPorts] || $retCode } { + errorMsg "Error sending ARP" + set retCode 1 + } + + if {$retCode == 0 && [ipfastpath cget -enable] == "true"} { + if [send_ipfastpath_frames portArray] { + errorMsg "Error sending IP fastpath frames" + set retCode 1 + } + } elseif {$retCode == 0 && [fastpath cget -enable] == "true"} { + if [send_fastpath_frames portArray] { + errorMsg "Error sending IP fastpath frames" + set retCode 1 + } + } + } + return $retCode +} + + +######################################################################################## +# Procedure: configureArp +# +# Description: This command configures the protocol server for ARP +# +# Argument(s): +# PortArray - array of ports, ie., one2oneArray for a one2one configuration +# NOTE:This array is passed for internal modem configuration purposes +# ArpList +# +######################################################################################## +proc configureArp {PortArray ArpList {write write} {numInterfaces 1} {resetInterfaces true}} \ +{ + upvar $PortArray portArray + upvar $ArpList arpList + + set retCode 0 + + #set rxList [getRxPorts portArray] + set enable802dot1qTag [protocol cget -enable802dot1qTag] + + foreach txMap $arpList { + scan $txMap "%d %d %d" tx_c tx_l tx_p + + if [IsPOSPort $tx_c $tx_l $tx_p] { + set index [lsearch $arpList $txMap] + set arpList [lreplace $arpList $index $index] + continue + } + + ipAddressTable setDefault + ipAddressTableItem setDefault + arpServer setDefault + protocolServer setDefault + + if {![catch {hasInternalModem portArray $tx_c $tx_l $tx_p} internalModemFlag] && $internalModemFlag } { + configureArpInternalModem portArray $tx_c $tx_l $tx_p + + } else { + if {[port get $tx_c $tx_l $tx_p] == 1 || [ip get $tx_c $tx_l $tx_p] == 1} { + errorMsg "Port [getPortId $tx_c $tx_l $tx_p] has not been configured yet." + set retCode 1 + continue + } + set txSA [port cget -MacAddress] + set numTx [port cget -numAddresses] + + ipAddressTable config -defaultGateway [ip cget -destDutIpAddr] + + ipAddressTableItem config -fromIpAddress [ip cget -sourceIpAddr] + ipAddressTableItem config -fromMacAddress $txSA + ipAddressTableItem config -numAddresses $numTx + + if {[vlanUtils::isPortTagged $tx_c $tx_l $tx_p] && $enable802dot1qTag} { + if [vlan get $tx_c $tx_l $tx_p] { + errorMsg "Error getting vlan parameters for $tx_c $tx_l $tx_p" + set retCode 1 + } + ipAddressTableItem config -enableVlan true + ipAddressTableItem config -vlanId [vlan cget -vlanID] + } + + if [ipAddressTableItem set] { + errorMsg "Error setting ipAddressTableItem" + set retCode 1 + continue + } + if [ipAddressTable addItem] { + errorMsg "Error adding ipAddressTable item" + set retCode 1 + continue + } + } + + if [ipAddressTable set $tx_c $tx_l $tx_p] { + errorMsg "Error setting ipAddressTable on port [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + + # if this is a receive port, then make sure we learn as well as get the DUT mac addr... + #if {[lsearch $rxList [list $tx_c $tx_l $tx_p]] >= 0} { + # arpServer config -mode arpGatewayAndLearn + #} else { + # # we only need the gateway address if we're not a receiver... + # arpServer config -mode arpGatewayOnly + #} + + # because we want to use requestRepeatCount, so need to ARP and Learn for all ports + arpServer config -mode arpGatewayAndLearn + arpServer config -retries [learn cget -retries] + arpServer config -rate [learn cget -rate] + arpServer config -requestRepeatCount [learn cget -numframes] + + if [arpServer set $tx_c $tx_l $tx_p] { + errorMsg "Error setting arpServer on port [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + protocolServer config -enableArpResponse true + if [protocolServer set $tx_c $tx_l $tx_p] { + errorMsg "Error setting protocolServer on port [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + } + + if {[interfaceTable::configurePort $tx_c $tx_l $tx_p $::ipV4 $numInterfaces nowrite $resetInterfaces]} { + errorMsg "Error: Unable to set interface table on port [getPortId $tx_c $tx_l $tx_p]" + set retCode $::TCL_OK + } + } + + if {[llength $arpList] > 0 && $retCode == 0 && $write == "write"} { + writeConfigToHardware arpList + } + + return $retCode +} + + +######################################################################################## +# Procedure: sendArp +# +# Description: This command uses the protocol server to send out arp requests & enable +# arp response +# +# Argument(s): +# PortArray array of ports, ie., one2oneArray for a one2one configuration +# +# Returns: TCL_OK or TCL_ERROR +# +######################################################################################## +proc sendArp { PortArray arpList {RemovedPorts ""} } \ +{ + + upvar $PortArray portArray + #upvar $ArpList arpList + if {[string length $RemovedPorts] > 0} { + upvar $RemovedPorts removedPorts + } + + set retCode $::TCL_OK + + if {[info exists removedPorts]} { + unset removedPorts + } + set removedPorts 0 + + if {[clearArpTable arpList]} { + errorMsg "Error clearing arp table" + set retCode $::TCL_ERROR + } + + set numAddress [advancedTestParameter cget -numAddressesPerPort] + set duration [expr ceil (double([learn cget -numframes]) / [learn cget -rate])] + + if {[issuePortGroupCommand resetStatistics arpList]} { + errorMsg "Error: Unable to issue port group commands: resetStatistics" + set retCode $::TCL_ERROR + } + set numFrames $numAddress + + if {[advancedTestParameter cget -verifyAllArpReply] == "true"} { + set numFrames [expr $numAddress * [learn cget -numframes]] + } + + if {[expr ceil (double($numFrames)/[learn cget -rate])] > $duration } { + set duration [expr $numFrames/[learn cget -rate]] + } + + if {[transmitArpRequest arpList]} { + errorMsg "Error transmitting arp request" + set retCode $::TCL_ERROR + } + + if {$duration > 2 } { + logMsg "Transmiting ARP frames for $duration seconds..." + # Wait for at least 1 frame per address gets transmitted.. + set retCode [writeWaitMessage "Transmit ARP Status" "Transmitting" $duration destroy] + } + + if {[learn cget -waitTime] > 1000 } { + logMsg "Waiting for DUT to settle down after learning for [expr [learn cget -waitTime]/1000] second(s)..." + } + + after [learn cget -waitTime] + + if {$numAddress > 1 } { + if {[verifyAllArpFramesSent $arpList]} { + errorMsg "Error verifying all Arp frames sent." + return $::TCL_ERROR + } + } + + if {[advancedTestParameter cget -verifyAllArpReply] == "true"} { + if {[verifyArpReply $arpList]} { + errorMsg "Error verifying all Arp replies" + return $::TCL_ERROR + } + } + + set retries [learn cget -retries] + while {[llength $arpList] > 0 && $retries > 0} { + incr retries -1 + + foreach txMap [lnumsort $arpList] { + scan $txMap "%d %d %d" tx_c tx_l tx_p + + if {[ipAddressTable get $tx_c $tx_l $tx_p]} { + errorMsg "Error getting ipAddressTable on [getPortId $tx_c $tx_l $tx_p]" + set retCode $::TCL_ERROR + continue + } + # look for ARP responses + if {[arpServer get $tx_c $tx_l $tx_p]} { + errorMsg "Error getting arpServer from [getPortId $tx_c $tx_l $tx_p]" + set retCode $::TCL_ERROR + continue + } + + if {![catch {hasInternalModem portArray $tx_c $tx_l $tx_p} internalModemFlag] && $internalModemFlag} { + if {[internalModemCheckArpResponse portArray $tx_c $tx_l $tx_p]} { + set retCode $::TCL_ERROR + } else { + set mapIndex [lsearch $arpList $txMap] + set arpList [lreplace $arpList $mapIndex $mapIndex] + set retCode $::TCL_OK + } + } else { + set gateway [ipAddressTable cget -defaultGateway] + + + if {[arpServer getEntry $gateway]} { + logMsg "Waiting for ARP response from $gateway on [getPortId $tx_c $tx_l $tx_p] ..." + after [learn cget -waitTime] + continue + } + if {[arpAddressTableEntry get]} { + errorMsg "Error getting arpAddressTableEntry" + set retCode $::TCL_ERROR + continue + } + set dutMacAddress [arpAddressTableEntry cget -macAddress] + + if {[port get $tx_c $tx_l $tx_p]} { + errorMsg "Error getting port [getPortId $tx_c $tx_l $tx_p] for storing DestMacAddress." + set retCode $::TCL_ERROR + continue + } + port config -DestMacAddress $dutMacAddress + if {[port set $tx_c $tx_l $tx_p]} { + errorMsg "Error setting port [getPortId $tx_c $tx_l $tx_p]" + set retCode $::TCL_ERROR + } + logMsg "Got ARP RESPONSE on [getPortId $tx_c $tx_l $tx_p], Gateway: $gateway, DUT MAC address for [getPortId $tx_c $tx_l $tx_p]: $dutMacAddress" + set mapIndex [lsearch $arpList $txMap] + set arpList [lreplace $arpList $mapIndex $mapIndex] + } + } + } + + foreach txMap [lnumsort $arpList] { + scan $txMap "%d %d %d" c l p + if {![catch {hasInternalModem portArray $c $l $p} internalModemFlag] && $internalModemFlag} { + getInternalModemNoArpResponse portArray $c $l $p + } else { + if {[ipAddressTable get $c $l $p]} { + errorMsg "Error getting ipAddressTable on [getPortId $c $l $p]" + set retCode $::TCL_ERROR + continue + } + set gateway [ipAddressTable cget -defaultGateway] + logMsg "No ARP response received from $gateway on $txMap" + } + + } + + if {[llength $arpList] > 0 && $retCode == 0} { + if {[learn cget -removeOnError] == "true"} { + removePorts portArray $arpList + set removedPorts 1 + } else { + set retCode $::TCL_ERROR + } + } + + return $retCode +} + + +######################################################################################## +# Procedure: verifyAllArpFramesSent +# +# Description: This command verifies all the ARP frames sent for all the addresses +# +# Argument(s): +# portList - list of ports +# +######################################################################################## +proc verifyAllArpFramesSent { portList } \ +{ + + set retCode $::TCL_OK + + set numAddress [advancedTestParameter cget -numAddressesPerPort] + if {[advancedTestParameter cget -verifyAllArpReply] == "true"} { + set numFrames [expr $numAddress * [learn cget -numframes]] + } else { + set numFrames $numAddress + } + + set retries [learn cget -retries] + while {[llength $portList] > 0 && $retries > 0} { + + set retCode [requestStats portList] + + foreach portMap $portList { + scan $portMap "%d %d %d" c l p + + if {[statList get $c $l $p]} { + errorMsg "Error getting stats for [getPortId $c $l $p]." + set retCode $::TCL_ERROR + continue + } + + if [catch {statList cget -framesSent} framesSent ] { + if [catch {statList cget -atmAal5FramesSent} framesSent ] { + set framesSent 0 + } + } + + # Since 32 bit counter (mpexpr) is used here, if we get a 32 bit long number (in binary) whose most significiant + # bit is 1, it will be recognized as a negtive number. + # So we use regexp to determine wheather numTxFrames is a negative number instead of using "$numTxFrames < 0" + if { [regexp {^-[0-9]+$} $framesSent] } { + set framesSent 0 + } + if { $framesSent >= $numFrames } { + set mapIndex [lsearch $portList $portMap] + set portList [lreplace $portList $mapIndex $mapIndex] + } + after 1000 + } + incr retries -1 + } + + foreach txMap [lnumsort $portList] { + scan $txMap "%d %d %d" c l p + + logMsg "Not all ARP frames sent for all $numAddress addresses on $txMap" + set retCode $::TCL_ERROR + } + + return $retCode +} + +######################################################################################## +# Procedure: verifyArpReply +# +# Description: This command verifies all the ARP replies - number of ARP replies from all +# the addresses +# +# Argument(s): +# portList - list of ports +# +######################################################################################## +proc verifyArpReply { portList } \ +{ + + set retCode $::TCL_OK + + set numAddress [advancedTestParameter cget -numAddressesPerPort] + + set retries [learn cget -retries] + while {[llength $portList] > 0 && $retries > 0} { + + set retCode [requestStats portList] + + foreach portMap $portList { + scan $portMap "%d %d %d" c l p + + if {[statList get $c $l $p]} { + errorMsg "Error getting stats for [getPortId $c $l $p]." + set retCode $::TCL_ERROR + continue + } + + set numRxArpReply [statList cget -rxArpReply] + + # Since 32 bit counter (mpexpr) is used here, if we get a 32 bit long number (in binary) whose most significiant + # bit is 1, it will be recognized as a negtive number. + # So we use regexp to determine wheather numTxFrames is a negative number instead of using "$numTxFrames < 0" + if { [regexp {^-[0-9]+$} $numRxArpReply] } { + set numRxArpReply 0 + } + if { $numRxArpReply >= $numAddress } { + set mapIndex [lsearch $portList $portMap] + set portList [lreplace $portList $mapIndex $mapIndex] + } + after 1000 + } + incr retries -1 + } + + foreach txMap [lnumsort $portList] { + scan $txMap "%d %d %d" c l p + + if {[ipAddressTable get $c $l $p]} { + errorMsg "Error getting ipAddressTable on [getPortId $c $l $p]" + set retCode $::TCL_ERROR + continue + } + set gateway [ipAddressTable cget -defaultGateway] + logMsg "Not all ARP responses received for all $numAddress addresses from $gateway on $txMap" + set retCode $::TCL_ERROR + } + + return $retCode +} + +######################################################################################## +#:::::::::::: IPV6 Layer Learn frames ::::::::::::: +######################################################################################## +######################################################################################## +# Procedure: send_neighborDiscovery_frames +# +# Description: Solicit and save the link-layer addresses for the ports of the +# given portArray +# +# Argument(s): PortArray: ports array, ie: one2oneArray, many2oneArray... +# RemovedPorts: return removed ports here +# +# Returns: ::TCL_OK or ::TCL_ERROR +# +######################################################################################## +proc send_neighborDiscovery_frames {PortArray {RemovedPorts ""} {resetInterfaces true}} \ +{ + upvar $PortArray portArray + if {[string length $RemovedPorts] > 0} { + upvar $RemovedPorts removedPorts + } + + set retCode $::TCL_OK + + if {![catch {namespace parent userCode}]} { + set retCode [userCode::performNeighborDiscovery portArray] + + } else { + set retCode [performNeighborDiscovery portArray removedPorts $resetInterfaces] + } + return $retCode +} + +######################################################################################## +# Procedure: performNeighborDiscovery +# +# Description: Solicit and save the link-layer addresses for the ports of the +# given portArray +# +# In this implementation, the neighbor discovery table is populated +# after a set of router solicitation/advertisement commands are +# sent and recieved. The 'source link-layer option' of the router +# advertisement is used to extract the DUT's link layer address. +# +# Argument(s): PortArray: ports array, ie: one2oneArray, many2oneArray... +# RemovedPorts: return removed ports here +# +# Returns: ::TCL_OK or ::TCL_ERROR +# +######################################################################################## +proc performNeighborDiscovery {PortArray {RemovedPorts ""} {resetInterfaces true}} \ +{ + upvar $PortArray portArray + if {[string length $RemovedPorts] > 0} { + upvar $RemovedPorts removedPorts + } + + set retCode $::TCL_OK + + set retries [learn cget -retries] + set waitTime [learn cget -waitTime] + set portList [getAllPorts portArray] + + if {[llength $portList] > 0} { + + discoveredNeighbor setDefault + if {![interfaceTable::configure portList $::ipV6 1 nowrite $resetInterfaces]} { + + set discoveryList $portList + + foreach portItem $portList { + scan $portItem "%d %d %d" tx_c tx_l tx_p + + if [IsPOSPort $tx_c $tx_l $tx_p] { + set index [lsearch $discoveryList $portItem] + set discoveryList [lreplace $discoveryList $index $index] + continue + } + } + if {[llength $discoveryList] } { + if {![set retCode [sendRouterSolicitation discoveryList]]} { + while {$retries >= 0} { + + set retCode $::TCL_OK + if {![getNeighborDiscovery discoveryList]} { + break + } else { + set retCode $::TCL_ERROR + incr retries -1 + } + after $waitTime + + sendRouterSolicitation discoveryList + } + + # Handle ports that didn't respond with Neighbor Advertisement. + if {[llength $discoveryList] > 0} { + + if {[learn cget -removeOnError] == "true"} { + removePorts portArray $discoveryList + set removedPorts $::true + } else { + errorMsg "Error: Unable to discover neighbors on ports: $discoveryList" + set retCode $::TCL_ERROR + } + } + } + } + + } else { + errorMsg "Error configuring Interface Table" + set retCode $::TCL_ERROR + } + } + + return $retCode +} + +######################################################################################## +# Procedure: SendRouterSolicitation +# +# Description: Send router solicitation to each port in the portList. +# +# This command causes the router to respond with a router advertisement +# where the source link-layer option is used to extract the link layer +# address of the DUT. +# +# Argument(s): PortArray: ports array, ie: one2oneArray, many2oneArray... +# +# Returns: ::TCL_OK or ::TCL_ERROR +# +######################################################################################## +proc sendRouterSolicitation {PortList} \ +{ + upvar $PortList portList + + set retCode $::TCL_OK + + foreach portMap $portList { + scan $portMap "%d %d %d" c l p + + if {![interfaceTable select $c $l $p]} { + + if {[interfaceTable sendRouterSolicitation]} { + errorMsg "Error: Unable to send router solicitation on $c $l $p" + set retCode $::TCL_ERROR + break + } + + } else { + errorMsg "Error: Unable to selected interface on $c $l $p" + set retCode $::TCL_ERROR + break + } + } + after [learn cget -waitTime] + + return $retCode +} + + +######################################################################################## +# Procedure: getNeighborDiscovery +# +# Description: Store the link-layer addresses solicited. +# +# Argument(s): PortList: port list +# +# Returns: ::TCL_OK or ::TCL_ERROR +# +######################################################################################## +proc getNeighborDiscovery {PortList {MacAddrArray ""} {verbose false} } \ +{ + upvar $PortList portList + upvar $MacAddrArray macAddrArray + + set retCode $::TCL_OK + + set waitTime [learn cget -waitTime] + + if { [llength $portList] > 1 } { + logMsg "\nPerforming Neighbor Discovery on ports: $portList." + } + + # Read neighbors from neighbor discovery table. + foreach portMap $portList { + scan $portMap "%d %d %d" chassId cardId portId + + # Error message for select is taken care off on the second select below + if {![interfaceTable select $chassId $cardId $portId]} { + interfaceTable requestDiscoveredTable + } + } + + # We only wait here instead of inside the loop for each port, + # make sure we wait long enough for all the ports in the list + after [learn cget -waitTime] + + foreach portMap $portList { + + scan $portMap "%d %d %d" chassId cardId portId + + set macAddrArray($chassId,$cardId,$portId) 0 + set message "" + + if {![interfaceTable select $chassId $cardId $portId]} { + + set description [interfaceTable::formatEntryDescription $chassId $cardId $portId] + if {![interfaceTable getDiscoveredList $description]} { + + + if {![discoveredList getFirstNeighbor]} { + set macAddrArray($chassId,$cardId,$portId) [discoveredNeighbor cget -macAddress] + } else { + set message "Error: Unable to get neighbor in discovery list for [getPortId $chassId $cardId $portId]" + } + + } else { + set message "Error: Unable to get neighbor discovery list for [getPortId $chassId $cardId $portId]" + } + + if {[isMacAddressValid $macAddrArray($chassId,$cardId,$portId)] == $::TCL_OK} { + + logMsg "Neighbor Discovery Complete for [getPortId $chassId $cardId $portId]: $macAddrArray($chassId,$cardId,$portId)" + if {![port get $chassId $cardId $portId]} { + port config -DestMacAddress $macAddrArray($chassId,$cardId,$portId) + } + if {[port set $chassId $cardId $portId]} { + errorMsg "Error: Unable configure [getPortId $chassId $cardId $portId] with link-layer address" + set retCode $::TCL_ERROR + } + + set index [lsearch $portList $portMap] + set portList [lreplace $portList $index $index] + + } else { + set retCode $::TCL_ERROR + } + + } else { + set message "Error: Unable to select interface table for [getPortId $chassId $cardId $portId]" + } + + if {$verbose == "true"} { + if {[string length $message] > 0} { + errorMsg $message + } + } + } + + return $retCode +} + +######################################################################################## +# Procedure: getNeighborDiscoveryPort +# +# Description: Return link-layer address of DUT. +# +# Argument(s): chassId +# cardId +# portId +# +# Returns: ::TCL_OK or ::TCL_ERROR +# +######################################################################################## +proc getNeighborDiscoveryPort {chassId cardId portId {verbose false} } \ +{ + set macAddrArray($chassId,$cardId,$portId) 0 + + set portList [list [list $chassId $cardId $portId]] + + if {[getNeighborDiscovery portList macAddrArray $verbose]} { + errorMsg "Error getting Neighbor Discovery for one or more ports." + } + + return $macAddrArray($chassId,$cardId,$portId) +} + + + +######################################################################################## +#:::::::::::: IPX Layer Learn frames ::::::::::::: +######################################################################################## + +######################################################################################## +# Procedure: sapStr2Asc +# +# Description: Converts a string into hex and pads to make it into 48 bytes. SAP frame +# has 48 bytes reserved for server name. +# +# Argument(s): +# string to convert +# +######################################################################################## + +proc sapStr2Asc {strName} \ +{ + set nameList {} + for {set i 0} {$i < [string length $strName]} {incr i} { + binary scan [string index $strName $i] tx_c val + set hexVal [format %02x [set val]] + set nameList [lappend nameList $hexVal] + } + + set currLen [string length $strName] + while {$currLen < 48} { + set nameList [lappend nameList "00"] + incr currLen + } + return $nameList +} + +######################################################################################## +# Procedure: send_ripx_frames +# +# Description: This command sends ripx frames all ports in order to advertise the +# sourceNode for IPX routing +# The algorithm of establishing an IPX connection is as follows: +# - send RIPX broadcast (requests) to the destination node's (router's) network number +# from all ports in the port map +# - the router responds to this request giving its network and node (MAC) adddress in +# the IPX header +# - to send traffic now, the sending node +# * places the destination node's (router's) network, node addresses and socket number +# in the destination address fields of the IPX header +# * places its own network, node addresses and socket number in the source address +# fields of the IPX header +# * places the node (MAC) address of the router (port) in the destination address +# address field of MAC header +# * places its own node address in the source address field of the MAC header +# +# Argument(s): +# PortArray array of ports, ie., one2oneArray for a one2one configuration +# +######################################################################################## +proc send_ripx_frames {PortArray {RemovedPorts ""}} \ +{ + global udfList + + upvar $PortArray portArray + if {[string length $RemovedPorts] > 0} { + upvar $RemovedPorts removedPorts + } + + set retCode 0 + set removedPorts 0 + + # set the stream parameters. + set preambleSize 8 + set framesize [learn cget -framesize] + + set duration [expr [learn cget -numframes] / [learn cget -rate]] + + stream setDefault + stream config -rateMode usePercentRate + stream config -dma stopStream + stream config -fcs good + stream config -enableTimestamp false + stream config -gapUnit gapNanoSeconds + + # need to turn off signature...!! + packetGroup setDefault + + udf setDefault + if [disableUdfs $udfList] { + errorMsg "Error disabling udfs in switchLearn for RIPx frames" + set retCode 1 + } + + filterPallette setDefault + + filter setDefault + filter config -captureFilterError errGoodFrame + filter config -captureTriggerError errGoodFrame + + # zero stats everywhere to avoid confusion later... + zeroStats portArray + + # note - send one port at portArray time to allow time for learning + + debugMsg "Configuring RIPX frames..." + + # send RIPX on all ports involved in this map, but just send once... + set ripxMap [getAllPorts portArray] + + # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + # >>>>>>>>>>>>>>>>>>>>>>>>>> Send RIPX Frames <<<<<<<<<<<<<<<<<<<<<<<<<<<<< + # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + # first send the RIPX broadcast request to the router asking for its addresses + foreach txMap $ripxMap { + scan $txMap "%d %d %d" tx_c tx_l tx_p + + if {[port get $tx_c $tx_l $tx_p] == 1} { + errorMsg "port [getPortId $tx_c $tx_l $tx_p] has not been configured yet." + continue + } + + set learnPercentRate [expr double([learn cget -rate])/[calculateMaxRate $tx_c $tx_l $tx_p $framesize]*100.] + stream config -percentPacketRate $learnPercentRate + + stream config -name "RIPXStream" + + stream config -da $::kBroadcastMacAddress + stream config -numDA 1 + + set numAddresses [port cget -numAddresses] + stream config -sa [port cget -MacAddress] + stream config -numSA $numAddresses + + stream config -patternType nonRepeat + stream config -dataPattern userpattern + stream config -numFrames [expr [learn cget -numframes] * $numAddresses] + if {[expr [stream cget -numFrames]/[learn cget -rate]] > $duration} { + set duration [expr [stream cget -numFrames]/[learn cget -rate]] + } + + if [ipx get $tx_c $tx_l $tx_p] { + errorMsg "Error getting IPx on [getPortId $tx_c $tx_l $tx_p] for RIPX frames." + set retCode 1 + continue + } + + # save the original setup and restore it at the end + set origSrcSocket($tx_c,$tx_l,$tx_p) [ipx cget -sourceSocket] + + # build the RIPX packet which is the data portion of the IPX packet + ipx config -packetType 1 ;# RIP type packet + + set RIPXNetworkNumber {ff ff ff ff} + set numHops 1 + set numTicks 2 + + ipx config -destNetwork {00 00 00 00} + ipx config -destNode $::kBroadcastMacAddress + ipx config -destSocket [format %d $::kRipSocket] + + ipx config -sourceNetwork {00 00 00 00} + ipx config -sourceNode [port cget -MacAddress] + if {$numAddresses > 1} { + ipx config -sourceNodeRepeatCounter $numAddresses + ipx config -sourceNodeCounterMode ipxIncrement + } else { + ipx config -sourceNodeCounterMode ipxIdle + } + ipx config -destNetworkCounterMode ipxIdle + ipx config -destNodeCounterMode ipxIdle + ipx config -destSocketCounterMode ipxIdle + + ipx config -sourceSocket [format %d $::kRipSocket] + ipx config -lengthOverride true + ipx config -length 40 + + if [ipx set $tx_c $tx_l $tx_p] { + errorMsg "Error setting IPX parameters on [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + + + # now build the IPX packet with the RIPX in it + set streamPattern [buildRipxPacket $::kRIPXOperation(request) $RIPXNetworkNumber $numHops $numTicks] + + stream config -pattern $streamPattern + stream config -framesize [learn cget -framesize] + if [stream set $tx_c $tx_l $tx_p 1] { + errorMsg "Error setting stream 1 on [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + + # need to turn off signature for ripx frames...!! + if [packetGroup setTx $tx_c $tx_l $tx_p 1] { + errorMsg "Error disabling packetGroup signatures on stream 1, [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + + # set up the pattern filter + filterPallette config -DA1 [port cget -MacAddress] + if [filterPallette set $tx_c $tx_l $tx_p] { + errorMsg "Error setting filter pallette for [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + + # set the filter parameters on the receive port + debugMsg " configuring filters" + filter config -captureFilterEnable true + filter config -captureTriggerEnable true + if [filter set $tx_c $tx_l $tx_p] { + errorMsg "Error setting filters on [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + + if [filterPallette write $tx_c $tx_l $tx_p] { + errorMsg "Error writing filterPallette to hardware for RIPX frames" + set retCode 1 + continue + } + + } + + writeConfigToHardware portArray + if [startCapture ripxMap] { + errorMsg "Error starting capture." + set retCode 1 + } + + foreach txMap [lnumsort $ripxMap] { + scan $txMap "%d %d %d" tx_c tx_l tx_p + + logMsg "sending RIPX broadcast frame from [getPortId $tx_c $tx_l $tx_p]" + } + set retCode [startStaggeredTx ripxMap] + + # wait the min refresh for 10/100 cards to let the stats update... + after 800 + + after [learn cget -waitTime] + # Wait for all frames to be transmitted.. + for {set timeCtr 1} {$timeCtr <= $duration} {incr timeCtr} { + logMsg "Transmitted $timeCtr of $duration seconds" + after 1000 + } + + checkAllTransmitDone ripxMap + + foreach txMap [lnumsort $ripxMap] { + scan $txMap "%d %d %d" tx_c tx_l tx_p + + # decode the RIPX response and get the router's node address + set maxRip 100 + + if [port get $tx_c $tx_l $tx_p] { + errorMsg "port [getPortId $tx_c $tx_l $tx_p] has not been configured yet." + continue + } + + if [captureBuffer get $tx_c $tx_l $tx_p 1 $maxRip] { + errorMsg "Error getting capture buffer from frame(s) 1 to 1 for [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + + set found 0 + set nFrame 1 + while {[captureBuffer getframe $nFrame] != 1 && $nFrame <= $maxRip} { + debugMsg "Getting frame $nFrame from Buffer ....." + set capframe [captureBuffer cget -frame] + + if {[ipx decode $capframe] == 0 && ([ipx cget -destSocket] == 1107) && ([ipx cget -destNode] == [string toupper [port cget -MacAddress]])} { + logMsg "Got RIPX RESPONSE on [getPortId $tx_c $tx_l $tx_p]" + set found 1 + + ipx config -sourceSocket $origSrcSocket($tx_c,$tx_l,$tx_p) + ipx config -destNetwork [ipx cget -sourceNetwork] + ipx config -sourceNetwork [ipx cget -destNetwork] + + set sourceNode [ipx cget -destNode] + set destNode [ipx cget -sourceNode] + ipx config -sourceNode $sourceNode + ipx config -destNode $destNode + + if [ipx set $tx_c $tx_l $tx_p] { + errorMsg "Error setting IPX parameters for [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + + if [port get $tx_c $tx_l $tx_p] { + errorMsg "Error getting port [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + port config -DestMacAddress [ipx cget -destNode] + if [port set $tx_c $tx_l $tx_p] { + errorMsg "Error setting port [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + break + } + incr nFrame + } + + if {$found == 0} { + errorMsg "No RIPX response frames received on [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 +return -code error + continue + } + } + + if {$retCode == 0 && [fastpath cget -enable] == "true"} { + if [send_fastpath_frames portArray] { + errorMsg "Error sending fastpath frames" + set retCode 1 + } + } + + stream setDefault + + logMsg "RIPX frames sent ..." + return $retCode +} + +######################################################################################## +# Procedure: send_sap_server_frames +# +# Description: This command sends IPX SAP broadcast frames in order to advertise the +# port as portArray server node for IPX routing. +# +# Argument(s): +# PortArray array of ports, ie., one2oneArray for portArray one2one configuration +# +######################################################################################## +proc send_sap_server_frames {PortArray} \ +{ + global udfList + + upvar $PortArray portArray + set retCode 0 + + # set the stream parameters. + set preambleSize 8 + set framesize [learn cget -framesize] + + stream config -rateMode usePercentRate + stream config -dma stopStream + stream config -fcs good + stream config -enableTimestamp false + stream config -numFrames [learn cget -numframes] + stream config -gapUnit gapNanoSeconds + + udf setDefault + disableUdfs $udfList + + filterPallette setDefault + + filter setDefault + filter config -captureTriggerDA addr1 + filter config -captureFilterError errGoodFrame + filter config -captureTriggerError errGoodFrame + + # zero stats everywhere to avoid confusion later... + zeroStats portArray + + # note - send one port at portArray time to allow time for learning + + debugMsg "Configuring SAP frames..." + + # send sap on all ports involved in this map, but just send once... + foreach txMap [array names portArray] { + if {![info exists sapMap($txMap]} { + set sapMap($txMap) 1 + } + + foreach rxMap $portArray($txMap) { + scan $rxMap "%d %d %d" tx_c tx_l tx_p + if {![info exists sapMap($tx_c,$tx_l,$tx_p)]} { + set sapMap($tx_c,$tx_l,$tx_p) 1 + } + } + } + + # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + # >>>>>>>>>>>>>>>>>>> SAP Broadcast - Advertise Servers <<<<<<<<<<<<<<<<<<< + # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + foreach txMap [lnumsort [array names sapMap]] { + scan $txMap "%d,%d,%d" tx_c tx_l tx_p + if {[port get $tx_c $tx_l $tx_p] == 1} { + logMsg "port [getPortId $tx_c $tx_l $tx_p] has not been configured yet." + continue + } + + if [ipx get $tx_c $tx_l $tx_p] { + errorMsg "Error getting IPx on [getPortId $tx_c $tx_l $tx_p] for SAP frames." + set retCode 1 + continue + } + # if this port is set up as SERVER, then send SAP broadcast announcing iteslf + if {[ipx cget -svrClientType] == 1} { + set txSA [port cget -MacAddress] + + set learnPercentRate [expr double([learn cget -rate])/[calculateMaxRate $tx_c $tx_l $tx_p $framesize]*100.] + stream config -percentPacketRate $learnPercentRate + + stream config -name "SapBroadcastStream" + + stream config -da $::kBroadcastMacAddress + stream config -numDA 1 + + stream config -sa $txSA + stream config -numSA 1 + + stream config -patternType nonRepeat + stream config -dataPattern userpattern + + set netAddress [ipx cget -sourceNetwork] + set nodeAddress [port cget -MacAddress] + + set hops {00 01} + ipx config -packetType 4 + + ipx config -destSocket $::kSapSocket + ipx config -sourceNode $txSA + ipx config -sourceNetwork {00 00 00 00} + ipx config -destNetwork {00 00 00 00} + + set serverName [sapStr2Asc "ixiaServer$tx_c$tx_l$tx_p"] + + if [ipx set $tx_c $tx_l $tx_p] { + errorMsg "Error setting IPX packet on [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + } + + # build the SAP broadcast packet (which is service type General Service Response) + set streamPattern [buildSapPacket $::kSapOperation(response) $::kSapServiceType(fileServer) \ + $serverName $netAddress $nodeAddress $::kSapSocket $hops] + + stream config -pattern $streamPattern + + set IPXheaderLen 30 + set DASATypeLen 14 + set CRCLen 4 + stream config -framesize [expr [llength [ipx cget -data]] + $IPXheaderLen + $DASATypeLen + $CRCLen] + + if [stream set $tx_c $tx_l $tx_p 1] { + errorMsg "Error setting stream 1 on [getPortId $tx_c $tx_l $tx_p] for SAP frames." + set retCode 1 + } + + if [stream write $tx_c $tx_l $tx_p 1] { + errorMsg "Error writing stream to hardware for SAP frames" + set retCode 1 + continue + } + + logMsg "sending SAP broadcast frame from [getPortId $tx_c $tx_l $tx_p] server, Network address: [ipx cget -sourceNetwork]" + if [startPortTx $tx_c $tx_l $tx_p] { + errorMsg "Error starting Tx on port [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + } + + # wait for portArray second portArray look for nearest SAP response on each port + after 1000 + + # make sure something got transmitted + set txNumFrames [stat cget -counterVal] + if {$txNumFrames == 0} { + errorMsg "Error transmitting SAP broadcast frames for SERVER on [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + } + } + + logMsg "SAP broadcast frames sent..." + return $retCode +} + +######################################################################################## +# Procedure: send_sapgns_frames +# +# Description: This command sends SAP GNS frames on all ports for IPX routing +# +# Argument(s): +# PortArray array of ports, ie., one2oneArray for portArray one2one configuration +# +######################################################################################## +proc send_sapgns_frames {PortArray} \ +{ + global udfList + + upvar $PortArray portArray + set retCode 0 + + # set the stream parameters. + set preambleSize 8 + set framesize [learn cget -framesize] + + stream config -rateMode usePercentRate + stream config -dma stopStream + stream config -fcs good + stream config -enableTimestamp false + stream config -numFrames [learn cget -numframes] + stream config -gapUnit gapNanoSeconds + + udf setDefault + disableUdfs $udfList + + filterPallette setDefault + + filter setDefault + filter config -captureTriggerDA addr1 + filter config -captureFilterError errGoodFrame + filter config -captureTriggerError errGoodFrame + + # zero stats everywhere to avoid confusion later... + zeroStats portArray + + # note - send one port at portArray time to allow time for learning + + debugMsg "Configuring SAP frames..." + + # send sap on all ports involved in this map, but just send once... + foreach txMap [array names portArray] { + if {![info exists sapMap($txMap]} { + set sapMap($txMap) 1 + } + + foreach rxMap $portArray($txMap) { + scan $rxMap "%d %d %d" tx_c tx_l tx_p + if {![info exists sapMap($tx_c,$tx_l,$tx_p)]} { + set sapMap($tx_c,$tx_l,$tx_p) 1 + } + } + } + + # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + # >>>>>>>>>>>>>>>>>>>> SAP Get Nearest Server Request <<<<<<<<<<<<<<<<<<<<< + # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + foreach txMap [lnumsort [array names sapMap]] { + scan $txMap "%d,%d,%d" tx_c tx_l tx_p + if {[port get $tx_c $tx_l $tx_p] == 1} { + logMsg "port [getPortId $tx_c $tx_l $tx_p] has not been configured yet." + continue + } + + set txSA [port cget -MacAddress] + + if [ipx get $tx_c $tx_l $tx_p] { + errorMsg "Error getting IPx on [getPortId $tx_c $tx_l $tx_p] for SAP frames." + set retCode 1 + continue + } + # if this port is set up as CLIENT, then send SAP Get Nearest server request + if {[ipx cget -svrClientType] == 2} { + filter config -captureFilterPattern any + filter config -captureFilterError errGoodFrame + filter config -captureFilterFrameSizeEnable false + filter config -captureFilterFrameSizeEnable false + filter config -captureTriggerError errGoodFrame + + if [startPortCapture $tx_c $tx_l $tx_p] { + errorMsg "Error starting Capture on port [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + } + + set learnPercentRate [expr double([learn cget -rate])/[calculateMaxRate $tx_c $tx_l $tx_p $framesize]*100.] + stream config -percentPacketRate $learnPercentRate + + stream config -name "SapGNSStream" + + stream config -da [port cget -DestMacAddress] + stream config -numDA 1 + + stream config -sa $txSA + stream config -numSA 1 + + stream config -patternType nonRepeat + stream config -dataPattern userpattern + + set netAddress [ipx cget -sourceNetwork] + set nodeAddress [port cget -MacAddress] + set hops {00 01} + + ipx config -packetType 4 + + ipx config -destNode $::kBroadcastMacAddress + ipx config -destSocket $::kSapSocket + + ipx config -sourceNode $txSA + ipx config -sourceSocket $::kSapSocket + + set serverName [sapStr2Asc "ixiaServer$tx_c$tx_l$tx_p"] ;# this is ignored by the router + + if [ipx set $tx_c $tx_l $tx_p] { + errorMsg "Error setting IPX packet on [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + } + + set streamPattern [buildSapPacket $::kSapOperation(getNearestServerRequest) $::kSapServiceType(fileServer) \ + $serverName $netAddress $nodeAddress $::kSapSocket $hops] + + stream config -pattern $streamPattern + stream config -framesize 64 + + if [stream set $tx_c $tx_l $tx_p 1] { + errorMsg "Error setting stream 1 on [getPortId $tx_c $tx_l $tx_p] for SAP GNS frames." + set retCode 1 + } + + + if [stream write $tx_c $tx_l $tx_p 1] { + errorMsg "Error writing stream to hardware for SAP GNS frames" + set retCode 1 + continue + } + + logMsg "sending SAP GNS request frame from [getPortId $tx_c $tx_l $tx_p] client, Network address: [ipx cget -sourceNetwork]" + if [startPortTx $tx_c $tx_l $tx_p] { + errorMsg "Error starting Tx on port [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + } + + # wait for portArray second portArray look for nearest SAP response on each port + after 1000 + + # make sure something got transmitted + set txNumFrames [stat cget -counterVal] + if {$txNumFrames == 0} { + errorMsg "Error transmitting SAP GNS frames for CLIENT on [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + + # decode the SAP GNS response and get the server's net address + if [capture get $tx_c $tx_l $tx_p] { + errorMsg "Error getting capture data on [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + + set numCapturedFrames [capture cget -nPackets] + debugMsg "numCapturedFrames = $numCapturedFrames" + + if {$numCapturedFrames < 1} { + logMsg "No SAP GNS response frames received on [getPortId $tx_c $tx_l $tx_p]" + } else { + # look for SAP GNS responses + if [captureBuffer get $tx_c $tx_l $tx_p 1 $numCapturedFrames] { + errorMsg "Error getting capture buffer from frame(s) 1 to 1 for [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + set found 0 + for {set nFrame 1} {$nFrame <= $numCapturedFrames} {incr nFrame} { + debugMsg "Getting frame $nFrame from Buffer ....." + if [captureBuffer getframe $nFrame] { + errorMsg "Error getting frame $nFrame from capture buffer for [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + set capframe [captureBuffer cget -frame] + debugMsg $capframe + + if [ipx get $tx_c $tx_l $tx_p] { + errorMsg "Error getting IPx on [getPortId $tx_c $tx_l $tx_p] for SAP frames." + set retCode 1 + continue + } + + set ipxPacketType [lindex $capframe 19] + set sapOperType [lrange $capframe 44 45] + set destSocket [lrange $capframe 106 107] + set serverType [lrange $capframe 46 47] + set serverNetAddr [lrange $capframe 96 99] + + if {($sapOperType == "00 04") && ($destSocket == "04 52") && ($serverType == $::kSapServiceType(fileServer))} { + logMsg "Got SAP GNS RESPONSE on [getPortId $tx_c $tx_l $tx_p]" + set found 1 + break + } + } + + if {$found == 0} { + logMsg "No SAP GNS response frames received on [getPortId $tx_c $tx_l $tx_p]" + continue + } + + ipx config -destNetwork $serverNetAddr + if [ipx set $tx_c $tx_l $tx_p] { + errorMsg "Error setting IPX parameters for [getPortId $tx_c,$tx_c,$tx_p]" + } + } + } + } + + logMsg "SAP GNS frames sent..." + return $retCode +} + + + + + + diff --git a/dep/tclclient/ixTcl1.0/Generic/tclServer.tcl b/dep/tclclient/ixTcl1.0/Generic/tclServer.tcl new file mode 100644 index 00000000..55091d75 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/tclServer.tcl @@ -0,0 +1,458 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: tclServer.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 3-1-2001 Michael Githens +# +# Description: This file contains procedures that handle any connections with +# the Tcl Server. +# +# NOTE: All routines in this file must be able to stand on their own. There +# can be no dependence on ScriptMate +# +############################################################################### + +namespace eval tclServer \ +{ + variable tclServerName "" + variable tclServerConnected 0 +} +# End of tclServer namespace + +# The following are the procedures that are meant to be called from outside +# of the namespace. The rest are just helper routines for use within the +# namespace. +# +# tclServer::getTclServerName +# tclServer::setTclServerName +# tclServer::disconnectTclServer +# tclServer::connectToTclServer serverName errMsg clientPort +# tclServer::isTclServerConnected +# + + +############################################################################### +# Procedure: tclServer::getTclServerName +# +# Description: Retrieve the hostname of tcl server. +# +# Arguments: None +# +# Returns: Hostname of the tcl server. +############################################################################### +proc tclServer::getTclServerName {} \ +{ + variable tclServerName + + return $tclServerName +} + + +############################################################################### +# Procedure: tclServer::setTclServerName +# +# Description: Sets the hostname of tcl server. +# +# Arguments: hostname - the new hostname +# +# Returns: None +############################################################################### +proc tclServer::setTclServerName { hostname } \ +{ + variable tclServerName + + set tclServerName $hostname + + return +} + + +############################################################################### +# Procedure: tclServer::disconnectTclServer +# +# Description: Disconnects from the TclServer socket +# +# Results: Returns the code from the clientClose call. +############################################################################### +proc tclServer::disconnectTclServer {} \ +{ + global ixTclSvrHandle ixErrorInfo + + variable tclServerName + variable tclServerConnected + + set retCode 0 + + if {[isUNIX]} { + # We only want to see this message on unix machines. We do not want + # to see it on windows, because it is meaningless. + logMsg "Disconnecting from Tcl Server ..." + } + if {[info exists ixTclSvrHandle]} { + set retCode [clientClose $ixTclSvrHandle] + catch {unset ixTclSvrHandle} + } + set tclServerConnected 0 + set tclServerName "" + set ixErrorInfo "" + + return $retCode +} + + +############################################################################### +# Procedure: tclServer::read +# +# Description: Reads the buffer from the TclServer socket +# +# Results: +############################################################################### +proc tclServer::read {socket} \ +{ + variable buffer + + set buffer "" + set length -1 + + if {[eof $socket] || [catch {gets $socket buffer}]} { + # end-of-file or abnormal connection drop + catch {close $socket} + debugMsg "Close $socket" + return -code error -errorinfo "Socket closed prematurely.." + } else { + debugMsg "buffer=$buffer" + } +} + + +############################################################################### +# Procedure: tclServer::connectToTclServer +# +# Description: This procedure connects the remote tcl session to a +# TclServer/proxy. +# +# Side Effects: If the connection is successful, then define the remote +# commands and functions that can now be accessed. +# +# Arguments: +# serverName: Hostname or IP address of the machine to connect with. +# ErrMsg: Pass by reference. If there is an error connecting, this will contain the error message. +# clientPort: Optional. The port number to use. +# +# Results: Returns 0 on success and 1 on failure +############################################################################### +proc tclServer::connectToTclServer { serverName ErrMsg {clientPort 4555} } \ +{ + global halCommands halFuncs ixTclSvrHandle ixErrorInfo halRedefineCommands + variable tclServerConnected + variable tclServerName + + upvar $ErrMsg errMsg + set errMsg "" + + set retCode 0 + + set ixErrorInfo "" + + logMsg "Connecting to Tcl Server $serverName ..." + if {[info exists ixTclSvrHandle]} { + catch { clientClose $ixTclSvrHandle } + catch { unset ixTclSvrHandle } + } + + set clientSocket [clientOpen $serverName $clientPort] + set ixTclSvrHandle $clientSocket + + if {$clientSocket == {}} { + set tclServerConnected 0 + set errMsg "Error opening client socket." + set retCode 1 + } else { + set tclServerConnected 1 + set tclServerName $serverName + + fconfigure $clientSocket -buffering line -translation crlf + fileevent $clientSocket readable [list tclServer::read $clientSocket] + + if [catch {clientSend $ixTclSvrHandle "package req IxTclHal"} result] { + errorMsg $result + } + + remoteDefine $halCommands + remoteDefine $halFuncs + + getConstantsValue $clientSocket + + if { [isUNIX] } { + foreach {command} $halRedefineCommands { + redefineCommand $command + } + } + } + + # Warn user if using different Ixia versions on client & server. + if {[isUNIX]} { + if {[isTclServerConnected]} { + set clientVersion $::env(IXIA_VERSION) + set clientVers [scan $clientVersion "%d.%d" clientMajor clientMinor] + + set serverVers [scan [version cget -ixTclHALVersion] "%d.%d" serverMajor serverMinor] + set serverVersion [format "%d.%d" $serverMajor $serverMinor] + + if {$clientVers != 2 || $serverMajor != $clientMajor || $serverMinor < $clientMinor} { + set retCode 1 + set errMsg "WARNING: Tcl Server and Client are running incompatible Ixia Software Versions" + append errMsg "\n\tTcl Server: $serverVersion" + append errMsg "\n\tTcl Client: $clientVersion\n\n" + logMsg "$errMsg" + set ixErrorInfo $errMsg + } + } + } + + return $retCode +} + + +############################################################################### +# Procedure: tclServer::isTclServerConnected +# +# Description: Tells if the there is a connection to a tcl server +# +# Results: Returns the value of the tclServerConnected variable +############################################################################### +proc tclServer::isTclServerConnected {} \ +{ + variable tclServerConnected + return $tclServerConnected +} + + +############################################################################### +# Procedure: tclServer::setTclServer +# +# Description: Create a window that will ask for the location of the tcl +# server. Only used on Unix and Linux platforms. +# +# Arguments: +# warningMsg - the text to place next to the warning sign +############################################################################### +proc tclServer::setTclServer {warningMsg} \ +{ + variable tclServerName + variable tclServerConnected + + # Return if not on unix or linux + if {[isUNIX] == 0} { + # Always let windows think it is connected + set tclServerConnected 1 + return + } + + set tclServerDlg .tclServer + + # If it already exists, destroy it. + if {[winfo exists $tclServerDlg]} { + catch {destroy $tclServerDlg} + } + + # Create the new top level window and make it transient to the top + toplevel $tclServerDlg -class Dialog + wm withdraw $tclServerDlg + wm title $tclServerDlg "Tcl Server" + + # Create a frame and the entry for the tcl server value + set serverFrame [frame $tclServerDlg.frame] + set serverLabel [label $serverFrame.label -text "Hostname: " -width 10] + set serverEntry [entry $serverFrame.entry -width 20] + + # Bind the enter key to the window. It will be the same as clicking connect + bind $tclServerDlg "[namespace current]::connectButton \ + $tclServerDlg $serverEntry \"$warningMsg\"" + + # Create a warning note about the consequences of not being connected + set serverWarning [label $serverFrame.warning -text "WARNING: " \ + -fg red] + set serverMessage [label $serverFrame.message -text $warningMsg] + + # Create the connect and cancel buttons + set connectButton [button $tclServerDlg.connect -text "Connect" -width 6 \ + -command "[namespace current]::connectButton $tclServerDlg \ + $serverEntry \"$warningMsg\""] + set cancelButton [button $tclServerDlg.cancel -text "Cancel" -width 6 \ + -command "[namespace current]::cancelButton $tclServerDlg \ + $serverEntry"] + + # Grid the entry and warning into the frame + grid $serverLabel -row 0 -column 0 -padx 5 -pady 5 -sticky wens + grid $serverEntry -row 0 -column 1 -padx 10 -pady 5 -sticky wens + grid $serverWarning -row 1 -column 0 -pady 10 -sticky ens + grid $serverMessage -row 1 -column 1 -pady 10 -sticky wns + + # Grid the frame and the buttons into the dialog + grid $serverFrame -row 0 -column 0 -padx 5 -pady 5 -sticky wens \ + -columnspan 2 + grid $connectButton -row 1 -column 0 -padx 5 -pady 5 -sticky ens + grid $cancelButton -row 1 -column 1 -padx 5 -pady 5 -sticky wns + + # Make the first row and the second column expandable + grid rowconfigure $tclServerDlg 0 -weight 1 + + # Set the entry with the currently saved value + if {[string compare $tclServerName ""] != 0} { + $serverEntry insert 0 $tclServerName + } else { + $serverEntry insert 0 [testConfig::getTestConfItem serverName] + } + + # Bring up the dialog and set its size + wm deiconify $tclServerDlg + wm minsize $tclServerDlg 325 150 + wm resizable $tclServerDlg 0 0 + + # Make the user do something in this window before continuing + focus $tclServerDlg + grab $tclServerDlg + tkwait window $tclServerDlg + + return +} + + +############################################################################### +# Procedure: tclServer:connectButton +# +# Description: Called when the connect button on the set tcl server window is +# pressed. This will +# 1. If already connected, save any values that need to carry +# across to the new connection and then disconnect. +# 2. Connect to the newly chosen tcl server. +# 3. Restore any saved settings from step 1. +# +# Arguments: +# tclServerDlg - The widget name for the set tcl server dialog +# serverEntry - The entry widget that contains the tcl server data +# warningMsg - If failure to connect, the message to pass to the dialog +############################################################################### +proc tclServer::connectButton { tclServerDlg serverEntry warningMsg } \ +{ + variable tclServerName + variable tclServerConnected + + # If connected, we first need to disconnect + set continueFlag 1 + + if {$tclServerConnected} { + if { [llength [smChassisUtils::getHostName]] > 0 } { + set message "Reconnecting to Tcl Server will cause reconnecting" + append message "\nto all configured chassis.\n\nDo you want to continue?" + set answer [tk_messageBox -parent $tclServerDlg -title "Warning" -type yesno \ + -icon warning -message $message] + + if { $answer == "no" } { + catch {destroy $tclServerDlg} + set continueFlag 0 + } + } + + if { $continueFlag == 1 } { + tclServer::disconnectTclServer + } + } + + if { $continueFlag == 1 } { + + set tclServerName [$serverEntry get] + + testConfig::setTestConfItem serverName $tclServerName + + # Connect to this server + $tclServerDlg configure -cursor watch + set retcode [tclServer::connectToTclServer $tclServerName errMsg] + + if {$retcode == 0} { + setConnectChassisFlag "continue" + smGlobalChassis::reconnect + $tclServerDlg configure -cursor arrow + catch {destroy $tclServerDlg} + } else { + $tclServerDlg configure -cursor arrow + # Could not connect to tcl server, ask them again + set msg "Could not connect to Tcl Server on $tclServerName.\n" + append msg $errMsg + tk_messageBox -parent $tclServerDlg -title "Tcl Server" -type ok \ + -icon error -message $msg + tclServer::setTclServer $warningMsg + + set tclServerName "" + } + } + + return +} + + +############################################################################### +# Procedure: tclServer:cancelButton +# +# Description: Called when the cancel button on the set tcl server window is +# pressed. This will +# +# Arguments: +# tclServerDlg - The widget name for the set tcl server dialog +# serverEntry - The entry widget that contains the tcl server data +############################################################################### +proc tclServer::cancelButton { tclServerDlg serverEntry } \ +{ + variable tclServerConnected + + set tclServerName [$serverEntry get] + + # Set the server name + testConfig::setTestConfItem serverName $tclServerName + + if {[info exists tclServerConnected] == 0} { + set tclServerConnected 0 + } + + # Destroy the dialog + catch {destroy $tclServerDlg} + + return +} + + diff --git a/dep/tclclient/ixTcl1.0/Generic/testCmdControl.tcl b/dep/tclclient/ixTcl1.0/Generic/testCmdControl.tcl new file mode 100644 index 00000000..74ea02b8 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/testCmdControl.tcl @@ -0,0 +1,538 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: testCmdControl.tcl +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 01-06-1999 HS Genesis +# +# Description: This file contains the commands for all the tests. When a new test +# is added, create a new proc for that test here. +# +################################################################################## + + +######################################################################################## +# Procedure: parseCmd +# +# Description: This command parses the test parameters and calls the appropriate functions. +# +######################################################################################## +proc parseCmd {testName {method_name ""} {Args {}}} \ +{ + upvar $Args args + + set retCode 0 + + switch $method_name { + "config" - + "configure" { + configureOptions $testName args + } + "cget" { + set retCode [cgetOptions $testName args] + } + "exists" { + set retCode [existsOptions $testName args] + } + "start" { + set retCode [startOptions $testName args] + } + "registerResultVars" { + set retCode [registerResultVarsOptions $testName args] + } + "getType" { + set retCode [getParmProperty $testName type $args] + } + "getValidRange" { + set retCode [getParmProperty $testName validRange $args] + } + "getValidValues" { + set retCode [getParmProperty $testName validValues $args] + } + "getValidateProc" { + set retCode [getParmProperty $testName validateProc $args] + } + "getHelp" { + set retCode [getParmProperty $testName help $args] + } + "get" - + "set" { + set mname [format "%s::_%s" $testName $method_name] + set retCode [uplevel $mname $args] + } + default { + set mname [format "%s::%s" $testName $method_name] + + if [catch {uplevel $mname $args} retCode] { + set testMethods [format "%s%s" $testName Methods] + global $testMethods + + set methods [array names ${testMethods}] + set methods [lsort [lappend methods exists]] + + error "$testName methods : { $methods }" + return + } + } + } + return $retCode +} + + +######################################################################################## +# Procedure: configureOptions +# +# Description: This command is used to configure the parameters. +# +######################################################################################## +proc configureOptions {testName arguments} \ +{ + upvar $arguments args + + set testMethods [format "%s%s" $testName Methods] + set testParms [format "%s%s" $testName Parms] + set testConfigVals [format "%s%s" $testName ConfigVals] + + global $testMethods $testParms $testConfigVals + + set argLen [llength $args] + set type $testName + + # if no option name was passed then display all options available for config + if {$argLen == 0} { + set errMsg "Usage: $type config " + foreach op [set ${testMethods}(config)] { + set errMsg [format "%s\n -$op " $errMsg] + } + error $errMsg + return + } + + if {[string index [lindex $args 0] 0] != "-"} { + set errMsg "Usage: $type config " + foreach op [set ${testMethods}(config)] { + set errMsg [format "%s\n -$op " $errMsg] + } + error $errMsg + return + } + set opt [lindex [string trimleft [lindex $args 0] "-"] 0] + + # if a wrong option is passed then display all options available for config + if {[lsearch [set ${testMethods}(config)] $opt] == -1} { + set errMsg "Usage: $type config " + foreach op [set [set testMethods](config)] { + set errMsg [format "%s\n -$op " $errMsg] + } + error $errMsg + return + } + + if {[info exists ${testConfigVals}]} { + # if an option is passed but no value then display the values expected for this parm + if {$argLen == 1} { + set errMsg "Usage: $type config -$opt " + set valList [array names ${testConfigVals}] + if {[lsearch $valList $opt] != -1} { + foreach val [set ${testConfigVals}($opt)] { + set errMsg [format "%s<$val> " $errMsg] + } + } + set errMsg [format "%s\n" $errMsg] + error $errMsg + } + + # at this point, it is confirmed that a valid option name with a value has + # been passed. Validate the value passed now. Do this only for parms who are + # expecting only one value, not multiple. Also, check only if this parm is + # expecting a certain type or range of values. + if {$argLen == 2} { + set valList [array names ${testConfigVals}] + if {[lsearch $valList $opt] != -1} { + + # if the values are required options then it is a list of more + # than one element. If values are "one of" type, like a|b|c, + # then this is a list of 1 element. First search for the + # expected value in the list + if {[lsearch [set ${testConfigVals}($opt)] [lindex $args 1]] == -1} { + # now it may be an a|b|c type value + if {[regexp [set ${testConfigVals}($opt)] [lindex $args 1]] == 0} { + set errMsg "Invalid value. Usage: $type config -$opt " + foreach val [set ${testConfigVals}($opt)] { + set errMsg [format "%s<$val> " $errMsg] + } + set errMsg [format "%s\n" $errMsg] + error $errMsg + } + } + } + } + } + + # if more than one parm is passed then convert it to a list because it is + # one of the parms that is expecting multiple values. But first check if the + # right number of values are passed. + if {$argLen > 2} { + if {[info exists ${testConfigVals}]} { + if {[expr $argLen - 1] != [llength [set [set testConfigVals]($opt)]]} { + set errMsg "Invalid number of arguments. Usage: $type config -$opt " + foreach val [set ${testConfigVals}($opt)] { + set errMsg [format "%s<$val> " $errMsg] + } + set errMsg [format "%s\n" $errMsg] + error $errMsg + } + } + set argList {} + for {set i 1} {$i < $argLen} {incr i} { + set argList [lappend argList [lindex $args $i]] + } + set ${testParms}($opt) $argList + } else { + set ${testParms}($opt) [lindex $args 1] + } + + return 0 +} + +######################################################################################## +# Procedure: cgetOptions +# +# Description: This command is used to get the configured parameters. +# +######################################################################################## +proc cgetOptions {testName arguments} \ +{ + upvar $arguments args + + set testMethods [format "%s%s" $testName "Methods"] + set testParms [format "%s%s" $testName "Parms"] + + global $testMethods $testParms + + set type $testName + if {[llength $args] == 0} { + set errMsg "Usage: $type cget " + foreach op [set ${testMethods}(cget)] { + set errMsg [format "%s\n -$op" $errMsg] + } + error $errMsg + return + } + + if {[string index [lindex $args 0] 0] != "-"} { + set errMsg "Usage: $type cget " + foreach op [set ${testMethods}(cget)] { + set errMsg [format "%s\n -$op" $errMsg] + } + error $errMsg + return + } + set opt [lindex [string trimleft [lindex $args 0] "-"] 0] + + if {[lsearch [set ${testMethods}(cget)] $opt] == -1} { + set errMsg "Usage: $type cget " + foreach op [set ${testMethods}(cget)] { + set errMsg [format "%s\n -$op" $errMsg] + } + error $errMsg + return + } + + return [set ${testParms}($opt)] +} + +######################################################################################## +# Procedure: getParmProperty +# +# Description: This command is used to get the configured parameters. +# +######################################################################################## +proc getParmProperty {testName property parmName} \ +{ + set retCode 0 + + switch $property { + type {set propertyArray [format %sType $testName]} + validRange {set propertyArray [format %sValidRange $testName]} + validValues {set propertyArray [format %sConfigVals $testName]} + validateProc {set propertyArray [format %sValidateProc $testName]} + help {set propertyArray [format %sHelp $testName]} + default { + error "Parameter property \'$property\' does not exist." + set retCode 1 + } + } + + global $propertyArray + regsub {^-} $parmName "" parmName + + if {($retCode == 0) && [info exists ${propertyArray}($parmName)]} { + set retCode [set ${propertyArray}($parmName)] + } else { + set retCode "" + } + + return $retCode +} + +######################################################################################## +# Procedure: startOptions +# +# Description: This command starts the specified test. The frames on desired ports should +# have been configured using the "port" and "streams" commands. For this test, +# there is only one stream per port with a specific frame size and rate. +# +######################################################################################## +proc startOptions {testName arguments} \ +{ + upvar $arguments args + + set testMethods [format "%s%s" $testName "Methods"] + + global $testMethods + + set type $testName + set methods [lsort [eval set ${testMethods}(start)]] + + # this is some special stuff to preserve backwards compatibility... + switch $testName { + tput { + if {[llength $args] == 0} { + lappend args -rfc2544 + } + # Protect the depricated test command + if { $args == "-Rfc2544" } { + set args "-rfc2544" + } + } + bcast { + if {[llength $args] == 0} { + lappend args -rate + } + } + tputjitter { + if {[llength $args] == 0} { + lappend args -linearIteration + } + } + imix { + if {[llength $args] == 0} { + lappend args -linearIteration + } + } + tputvlan { + if {[llength $args] == 0} { + lappend args -one2many + } + } + cableModem { + # since the percentMaxRate vars have been obseleted, do this for backwards compatiblity + # w/the new vars + if {[cableModem cget -clientPercentMaxRate] > 0} { + cableModem config -rateSelect percentMaxRate + cableModem config -clientRate [cableModem cget -clientPercentMaxRate] + } + + if {[cableModem cget -serverPercentMaxRate] > 0} { + cableModem config -rateSelect percentMaxRate + cableModem config -serverRate [cableModem cget -serverPercentMaxRate] + } + + # Protect the depricated test + if { $args == "-LLCFiltering" } { + set args "-usb02LLCFiltering" + } + + if { $args == "-LLCTransfer" } { + set args "-usb02LLCTransfer" + } + if { $args == "-multiAddrIPFiltering" } { + set args "-usb02MultiAddrIPFiltering" + } + if { $args == "-multiAddrIPTransfer" } { + set args "-usb02MultiAddrIPTransfer" + } + if { $args == "-usb02MultiAddrIPFiltering" } { + set args "-usb02MultiAddrFiltering" + } + if { $args == "-usb02MultiAddrIPTransfer" } { + set args "-usb02MultiAddrTransfer" + } + } + } + + switch [llength $args] { + 0 { + if {[llength $methods] > 0} { + puts "Usage: $type start " + foreach op $methods { + puts " -$op" + } + return $::TCL_ERROR + } + set opt "start" + } + 1 { + if {[string index [lindex $args 0] 0] != "-"} { + puts "Usage: $type start " + foreach op $methods { + puts " -$op" + } + return $::TCL_ERROR + } + set opt [string trimleft [lindex $args 0] "-"] + + if {[lsearch $methods $opt] == -1} { + puts "Invalid ${testName} command: $methods" + return $::TCL_ERROR + } + } + default { + puts "Usage: $type start " + foreach op ${testName} { + puts " -$op " + } + return $::TCL_ERROR + } + } + + set mname [format "%s::%s" $testName $opt] + set retCode [uplevel $mname] + + return $retCode +} + + +######################################################################################## +# Procedure: registerResultVarsOptions +# +# Description: This command registers the results variables. +# +######################################################################################## +proc registerResultVarsOptions {testName arguments} \ +{ + upvar $arguments args + + set testMethods [format "%s%s" $testName "Methods"] + + global $testMethods + + set type $testName + set methods [lsort [eval set ${testMethods}(registerResultVars)]] + + switch [llength $args] { + 0 { + if {[llength $methods] > 0} { + puts "Usage: $type registerResultVars " + foreach op $methods { + puts " -$op" + } + return + } + set opt "registerResultVars" + } + 1 { + if {[string index [lindex $args 0] 0] != "-"} { + puts "Usage: $type registerResultVars " + foreach op $methods { + puts " -$op" + } + return + } + set opt [string trimleft [lindex $args 0] "-"] + + if {[lsearch $methods $opt] == -1} { + puts "Invalid ${testName} command: $methods" + return + } + set opt [format "registerResultVars_%s" $opt] + } + default { + puts "Usage: $type registerResultVars " + foreach op ${testName} { + puts " -$op " + } + return + } + } + + set mname [format "%s::%s" $testName $opt] + set ret [uplevel $mname] + + return $ret +} + + + +######################################################################################## +# Procedure: existsOptions +# +# Description: This command is used check if the options exists. +# +# If exists, return +# +######################################################################################## +proc existsOptions {testName arguments} \ +{ + upvar $arguments args + + set retCode 0 + + set testMethods [format "%s%s" $testName "Methods"] + set testParms [format "%s%s" $testName "Parms"] + + global $testMethods $testParms + + set type $testName + if {[llength $args] == 0} { + if [info exists ${testMethods}] { + set retCode 1 + } + } else { + set opt [lindex [string trimleft [lindex $args 0] "-"] 0] + + if {[lsearch [set ${testMethods}(cget)] $opt] != -1} { + set retCode 1 + } + } + + return $retCode +} + + + + + diff --git a/dep/tclclient/ixTcl1.0/Generic/trafficMix.tcl b/dep/tclclient/ixTcl1.0/Generic/trafficMix.tcl new file mode 100644 index 00000000..302727f4 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/trafficMix.tcl @@ -0,0 +1,329 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: trafficMix.tcl +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 09-15-1999 DS +# +# Description: This file contains special procs to set up multiple +# streams w/one aggregate rate +# +######################################################################## + + +######################################################################## +# Procedure: calcTrafficMix +# +# This command calculates the relative percents (number of frames) for +# a number of streams based on one aggregate percent utilization. +# +# Arguments(s): +# StreamArray - array indexed by streamID, that +# contains the framesize & percentFrameRate +# for that stream +# BurstArray - calc'd burst size for each stream +# percentUtilization - desired aggregate percent utilization +# +# Return: +# +######################################################################## +proc calcTrafficMix {StreamArray BurstArray {percentUtilization 100}} \ +{ + set retCode 0 + + upvar $StreamArray streamArray + upvar $BurstArray burstArray + + set maxFrameSize 0 + foreach streamID [array names streamArray] { + scan $streamArray($streamID) "%d %d" fs percent + lappend frameSizeList $fs + if {$fs > $maxFrameSize} { + set maxFrameSize $fs + set maxFsIndex $streamID + } + } + + foreach streamID [array names streamArray] { + scan $streamArray($streamID) "%d %d" fs percent + + set burstArray($streamID) [expr ([lindex $streamArray($maxFsIndex) 0]/double([lindex $streamArray($maxFsIndex) end]))/$fs*double($percent)] + } + + debugMsg "calcTrafficMix: burstArray:[array get burstArray]" + + return $retCode +} + + +######################################################################## +# Procedure: calcAggregateDataRate +# +# This command calculates the ifgs for an aggregrate data rate, assuming +# there is one packet per stream/one packet per fs. +# +# Arguments(s): +# +# frameSizeList - list of framesizes to use in aggregrate calculation +# bitRate - desired aggregrate bit rate +# speed - in mbps, ie., 100, 10 or 1000 mbps +# +# Return: +# ifg, in nanoseconds OR 0 if bit rate is greater than achievable wire +# speed or error calculating +# +######################################################################## +proc calcAggregateDataRate {frameSizeList bitRate speed {preambleSize 8}} \ +{ + return [calcAggregateBitRate $frameSizeList $bitRate $speed false false $preambleSize] +} + + +######################################################################## +# Procedure: calcAggregateFrameRate +# +# This command calculates the ifgs for an aggregrate frame rate, assuming +# there is one packet per stream/one packet per fs. +# +# Arguments(s): +# +# frameSizeList - list of framesizes to use in aggregrate calculation +# bitRate - desired aggregrate bit rate +# speed - in mbps, ie., 100, 10 or 1000 mbps +# +# Return: +# ifg, in nanoseconds OR 0 if bit rate is greater than achievable wire +# speed or error calculating +# +######################################################################## +proc calcAggregateFrameRate {frameSizeList bitRate speed {preambleSize 8}} \ +{ + return [calcAggregateBitRate $frameSizeList $bitRate $speed true false $preambleSize] +} + + +######################################################################## +# Procedure: calcAggregateTotalRate +# +# This command calculates the ifgs for an aggregrate total bit rate, assuming +# there is one packet per stream/one packet per fs. +# +# Arguments(s): +# +# frameSizeList - list of framesizes to use in aggregrate calculation +# bitRate - desired aggregrate bit rate +# speed - in mbps, ie., 100, 10 or 1000 mbps +# +# Return: +# ifg, in nanoseconds OR 0 if bit rate is greater than achievable wire +# speed or error calculating +# +######################################################################## +proc calcAggregateTotalRate {frameSizeList bitRate speed {preambleSize 8}} \ +{ + return [calcAggregateBitRate $frameSizeList $bitRate $speed true true $preambleSize] +} + + +######################################################################## +# Procedure: calcAggregateBitRate +# +# This command calculates the ifgs for an aggregrate bit rate, assuming +# there is one packet per stream/one packet per fs. +# +# Arguments(s): +# +# frameSizeList - list of framesizes to use in aggregrate calculation +# bitRate - desired aggregrate bit rate +# speed - in mbps, ie., 100, 10 or 1000 mbps +# +# Return: +# ifg, in nanoseconds OR 0 if bit rate is greater than achievable wire +# speed or error calculating +# +######################################################################## +proc calcAggregateBitRate {frameSizeList bitRate speed {includeCRC true} {includePreamble true} {preambleSize 8}} \ +{ + set nBits [calcTotalBits $frameSizeList $includeCRC $includePreamble $preambleSize] + if [catch {mpexpr double([llength $frameSizeList] * $bitRate)/$nBits} pps] { + logMsg "calcAggregateBitRate: Error converting bit rate to PPS" + return 0 + } + return [calcAggregatePPS $frameSizeList $pps $speed $preambleSize] +} + + +######################################################################## +# Procedure: calcAggregatePPS +# +# This command calculates the ifgs for an aggregrate pps rate, assuming +# there is one packet per stream/one packet per fs. +# +# Arguments(s): +# +# frameSizeList - list of framesizes to use in aggregrate calculation +# pps - desired aggregrate pps rate +# speed - in mbps, ie., 100, 10 or 1000 mbps +# +# Return: +# ifg, in nanoseconds OR 0 if pps rate is greater than achievable wire +# speed or error calculating +# +######################################################################## +proc calcAggregatePPS {frameSizeList pps speed {preambleSize 8}} \ +{ + set ifgNS 0 + + set nBits [calcTotalBits $frameSizeList true true $preambleSize] + set speed [mpexpr $speed*1000000.] + + if [catch {mpexpr double($nBits)/$speed} dataTime] { + logMsg "calcAggregratePPS: Error calculating data time." + return $ifgNS + } + + if [catch {mpexpr ((1./$pps) - ($dataTime/[llength $frameSizeList]))/0.000000001} ifgNS] { + logMsg "calcAggregratePPS: Error calculating ifg." + return $ifgNS + } + + if {$ifgNS < 0} { + set ifgNS 0 + } + + return [mpexpr round($ifgNS)] +} + + +######################################################################## +# Procedure: calcTotalBits +# +# This command calculates the total bits, including preamble, of the +# frames in the frameSizeList +# +# Arguments(s): +# +# frameSizeList - list of framesizes to use in aggregrate calculation +# +# Return: +# 0 if pps rate is greater than achievable wire speed. +# +######################################################################## +proc calcTotalBits {frameSizeList {includeCRC true} {includePreamble true} {preambleSize 8}} \ +{ + set nBits 0 + + if {$includePreamble != "true"} { + set preambleSize 0 + } + + set nPackets [llength $frameSizeList] + if {$nPackets > 0} { + set totalFS 0 + foreach fs $frameSizeList { + if {$includeCRC == "true"} { + incr totalFS $fs + } else { + incr totalFS [expr $fs - 4] + } + } + set nBits [mpexpr ($totalFS + ($preambleSize * $nPackets)) * 8] + } + + return $nBits +} + + + +######################################################################## +# Procedure: calcTotalStreamTime +# +# This command calculates the total time the aggregate streams will take +# if using the giving duration & percentUtilization & returns an array, +# Loopcount, that contains a loopcount number for each transmit port. +# +# Arguments(s): +# TxRxArray - array (ie., one2oneArray) or list of ports to +# transmit on +# StreamArray - array indexed by streamID, that +# contains the framesize & percentFrameRate +# for that stream +# BurstArray - calc'd burst size for each stream +# Loopcount - calc'd loopcount for each tx port +# duration - tx time +# percentUtilization - desired aggregate percent utilization +# +# Return: +# +######################################################################## +proc calcTotalStreamTime {TxRxArray StreamArray BurstArray Loopcount duration {percentUtilization 100} {numRxAddresses 1} {preambleSize 8}} \ +{ + set retCode 0 + + upvar $TxRxArray txRxArray + upvar $StreamArray streamArray + upvar $BurstArray burstArray + upvar $Loopcount loopcount + + set txList [getTxPorts txRxArray] + + foreach txMap $txList { + scan $txMap "%d %d %d" tx_c tx_l tx_p + + if [port get $tx_c $tx_l $tx_p] { + logMsg "calcTotalStreamTime: Error getting port [getPortId $tx_c $tx_l $tx_p]" + set retCode 1 + continue + } + set speed [port cget -speed] + + set totalTime 0 + foreach streamID [array names streamArray] { + scan $streamArray($streamID) "%d %d" fs percent + + set rate [mpexpr ($percentUtilization * [calculateMaxRate $tx_c $tx_l $tx_p $fs $preambleSize])/100.] + set gapInBytes [calculateGapBytes $tx_c $tx_l $tx_p $rate $fs] + + set currTime [mpexpr ((($fs + $preambleSize + $gapInBytes) * 8.) * $burstArray($streamID) * $numRxAddresses)/($speed*1000000.)] + set totalTime [mpexpr $totalTime + $currTime] + } + set loopcount($tx_c,$tx_l,$tx_p) [mpexpr round($duration / $totalTime)] + debugMsg "calcTotalStreamTime: totalTime:$totalTime, loopcount:$loopcount($tx_c,$tx_l,$tx_p)" + } + + return $retCode +} + + diff --git a/dep/tclclient/ixTcl1.0/Generic/utilWrappers.tcl b/dep/tclclient/ixTcl1.0/Generic/utilWrappers.tcl new file mode 100644 index 00000000..b03199a9 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/utilWrappers.tcl @@ -0,0 +1,357 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: utilWrappers.tcl +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 06-18-2001 HSH Genesis +# +# Description: Wrappers for utility commands from IxTclHal +# +################################################################################## + +######################################################################################## +# Procedure: ixIsBgpInstalled +# +# Description: This command checks if Bgp client installed +# +# Returned value : +# +# TRUE : If it is installed. +# FALSE : If it is NOT installed. +######################################################################################## +proc ixIsBgpInstalled {} \ +{ + return [ixUtils isBgpInstalled] +} + + +######################################################################################## +# Procedure: ixIsIsisInstalled +# +# Description: This command checks if Isis client installed +# Returned value : +# +# TRUE : If it is installed. +# FALSE : If it is NOT installed. +######################################################################################## +proc ixIsIsisInstalled {} \ +{ + return [ixUtils isIsisInstalled] +} + +######################################################################################## +# Procedure: ixIsRsvpInstalled +# +# Description: This command checks if Rsvp client installed +# Returned value : +# +# TRUE : If it is installed. +# FALSE : If it is NOT installed. +######################################################################################## +proc ixIsRsvpInstalled {} \ +{ + return [ixUtils isRsvpInstalled] +} + + +######################################################################################## +# Procedure: ixIsOspfInstalled +# +# Description: This command checks if Ospf client installed +# Returned value : +# +# TRUE : If it is installed. +# FALSE : If it is NOT installed. +######################################################################################## +proc ixIsOspfInstalled {} \ +{ + return [ixUtils isOspfInstalled] +} + + +######################################################################################## +# Procedure: ixIsRipInstalled +# +# Description: This command checks if Rip client installed +# Returned value : +# +# TRUE : If it is installed. +# FALSE : If it is NOT installed. +######################################################################################## +proc ixIsRipInstalled {} \ +{ + return [ixUtils isRipInstalled] +} + +######################################################################################## +# Procedure: ixIsArpInstalled +# +# Description: This command checks if Arp client installed +# Returned value : +# +# TRUE : If it is installed. +# FALSE : If it is NOT installed. +######################################################################################## +proc ixIsArpInstalled {} \ +{ + return [ixUtils isArpInstalled] +} + +######################################################################################## +# Procedure: ixIsIgmpInstalled +# +# Description: This command checks if Igmp client installed +# Returned value : +# +# TRUE : If it is installed. +# FALSE : If it is NOT installed. +######################################################################################## +proc ixIsIgmpInstalled {} \ +{ + return [ixUtils isIgmpInstalled] +} + + +######################################################################################## +# Procedure: ixIsVpnL2Installed +# +# Description: This command checks if VpnL2 client installed +# Returned value : +# +# TRUE : If it is installed. +# FALSE : If it is NOT installed. +######################################################################################## +proc ixIsVpnL2Installed {} \ +{ + return [ixUtils isVpnL2Installed] +} + + +######################################################################################## +# Procedure: ixIsVpnL3Installed +# +# Description: This command checks if VpnL3 client installed +# Returned value : +# +# TRUE : If it is installed. +# FALSE : If it is NOT installed. +######################################################################################## +proc ixIsVpnL3Installed {} \ +{ + return [ixUtils isVpnL3Installed] +} + + +######################################################################################## +# Procedure: ixIsMldInstalled +# +# Description: This command checks if MLD client installed +# Returned value : +# +# TRUE : If it is installed. +# FALSE : If it is NOT installed. +######################################################################################## +proc ixIsMldInstalled {} \ +{ + return [ixUtils isMldInstalled] +} + + +######################################################################################## +# Procedure: ixIsOspfV3Installed +# +# Description: This command checks if OSPF V3 client installed +# Returned value : +# +# TRUE : If it is installed. +# FALSE : If it is NOT installed. +######################################################################################## +proc ixIsOspfV3Installed {} \ +{ + return [ixUtils isOspfV3Installed] +} + + +######################################################################################## +# Procedure: ixIsPimsmInstalled +# +# Description: This command checks if PIM-SM client installed +# Returned value : +# +# TRUE : If it is installed. +# FALSE : If it is NOT installed. +######################################################################################## +proc ixIsPimsmInstalled {} \ +{ + return [ixUtils isPimsmInstalled] +} + +######################################################################################## +# Procedure: ixGetLineUtilization +# +# Description: This command returns the total port rate. +# +# Input: +# rateType - i.e. typePercentMaxRate or typeFpsRate +# +# Returned value : +# +# TRUE : If it is installed. +# FALSE : If it is NOT installed. +######################################################################################## +proc ixGetLineUtilization {chassis card port {rateType typePercentMaxRate}} \ +{ + return [ixUtils getLineUtilization $chassis $card $port $rateType] +} + +######################################################################################## +# Procedure: ixIsLdpInstalled +# +# Description: This command checks if LDP client installed +# Returned value : +# +# TRUE : If it is installed. +# FALSE : If it is NOT installed. +######################################################################################## +proc ixIsLdpInstalled {} \ +{ + return [ixUtils isLdpInstalled] +} + +######################################################################################## +# Procedure: ixIsRipngInstalled +# +# Description: This command checks if Ripng client installed +# Returned value : +# +# TRUE : If it is installed. +# FALSE : If it is NOT installed. +######################################################################################## +proc ixIsRipngInstalled {} \ +{ + return [ixUtils isRipngInstalled] +} + + +######################################################################################## +# Procedure: calculateMaxRate +# +# This proc calculates the max rate in packets per second for this interface +# +# Arguments(s): +# chassis +# card +# port +# framesize +# preambleSize, default == 8 +# +# Returned value : +# returns the max rate in PPS +# +######################################################################################## +proc calculateMaxRate {chassis card port {framesize 64} {preambleOrAtmEncap 8} } \ +{ + return [ixUtils calculateMaxRate $chassis $card $port $framesize $preambleOrAtmEncap ] +} + + +######################################################################################## +# Procedure: calculateGapBytes +# +# This proc calculates the number of bytes that fit in between each packet for this rate +# +# Arguments(s): +# chassis +# card +# port +# framerate +# framesize, default = 64 +# preambleSize, default == 8 +# +# Returned value : +# returns the number of bytes that fit in a gap space of a certain rate +# +######################################################################################## +proc calculateGapBytes {chassis card port framerate {framesize 64} {preambleSize 8}} \ +{ + return [ixUtils calculateGapBytes $chassis $card $port $framerate $framesize $preambleSize] +} + + +######################################################################################## +# Procedure: calculateFPS +# +# This proc calculates the framerate in frames per second given the percent line rate +# +# Arguments(s): +# chassis +# card +# port +# percentLineRate = 100 +# framesize, default = 64 +# preambleSize, default == 8 +# +# Returned value : +# returns framerate, in PPS +# +######################################################################################## +proc calculateFPS {chassis card port {percentLineRate 100} {framesize 64} {preambleOrAtmEncap 8} } \ +{ + return [ixUtils calculateFPS $chassis $card $port $percentLineRate $framesize $preambleOrAtmEncap] +} + + +######################################################################################## +# Procedure: calculatePercentMaxRate +# +# This proc calculates percent of line rate for this PPS value +# +# Arguments(s): +# chassis +# card +# port +# framerate +# framesize, default = 64 +# preambleSize, default == 8 +# +# Returned value : +# returns the number of bytes that fit in a gap space of a certain rate +# +######################################################################################## +proc calculatePercentMaxRate {chassis card port framerate framesize {preambleOrAtmEncap 8}} \ +{ + return [ixUtils calculatePercentMaxRate $chassis $card $port $framerate $framesize $preambleOrAtmEncap] +} + diff --git a/dep/tclclient/ixTcl1.0/Generic/utils.tcl b/dep/tclclient/ixTcl1.0/Generic/utils.tcl new file mode 100644 index 00000000..1352ce04 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/utils.tcl @@ -0,0 +1,2319 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: utils.tcl +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 05-7-1998 Hardev Soor +# +# Description: This file contains common commands used by the tests +# +######################################################################## + + +######################################################################## +# Procedure: globalSetDefault +# +# Description: This command calls the setDefault for all the IxTclHal +# commands as a form of initialization. +# +# Arguments(s): +# +######################################################################## +proc globalSetDefault {} \ +{ + + # This "if" condition was added to avoid unnecessary warning messages + # in the TCLEvent.log + if {[llength $::ixTclHal::nonDeprecatedCommands] == 0} { + + set deprecatedCommands {usb licenseManagement} + regsub -all { } $deprecatedCommands "|" deprecatedCommands + regsub -all $deprecatedCommands $::halCommands {} ::ixTclHal::nonDeprecatedCommands + } + + if [info exists ::ixTclHal::nonDeprecatedCommands] { + foreach halCmd $::ixTclHal::nonDeprecatedCommands { + if {[info commands $halCmd] != ""} { + catch {$halCmd setDefault} + } + } + } +} + + +######################################################################## +# Procedure: protocolStackSetDefault +# +# This command calls the setDefault for all the protocol stack related +# commands as a form of initialization. +# +# Arguments(s): +# +######################################################################## +proc protocolStackSetDefault {} \ +{ + global ixProtocolList + + foreach protocol $ixProtocolList { + $protocol setDefault + } +} + +######################################################################## +# Procedure: streamSet +# +# Description: This command sets the stream and prints an error message based on the +# return code +# +# Argument(s): +# chasId - chassis id +# cardId - card id +# portId - port id +# streamId - stream id +# +# Results : 0 : No error found, else error +# +######################################################################## +proc streamSet { chasId cardId portId streamId } \ +{ + set level [expr [info level] - 2] + if {$level > 0} { + set levelStr "[lindex [info level $level] 0]: " + } else { + set levelStr "Error: " + } + + set retCode [stream set $chasId $cardId $portId $streamId] + switch $retCode \ + $::ixTcl_outOfMemory { + logger message "$levelStr Error setting stream $streamId on port [getPortId $chasId $cardId $portId] - Port is out of memory" + }\ + $::ixTcl_generalError { + logger message "$levelStr Error setting stream $streamId on port [getPortId $chasId $cardId $portId]" + }\ + $::ixTcl_notAvailable { + logger message "$levelStr Port [getPortId $chasId $cardId $portId] is unavailable, check ownership." + }\ + $::ixTcl_ok -\ + default { + } + + return $retCode +} + + +######################################################################## +# Procedure: validateFramesize +# +# Description: Checks the framesize against 64-1518 valid EN type +# framesize and tests against protocol restrictions +# +# Arguments(s): framesize - framesize to validate +# +# Returns: TCL_OK or TCL_ERROR (if invalid) +# +######################################################################## +proc validateFramesize {framesize} \ +{ + set retCode $::TCL_ERROR + + switch [getProtocolName [protocol cget -name]] { + ipV6 { + set minimumFramesize [ipv6::getMinimumValidFramesize] + if {$framesize >= $minimumFramesize} { + set retCode $::TCL_OK + } + } + ip - + mac - + default { + return $::TCL_OK + } + } + + return $retCode +} + + +######################################################################## +# Procedure: validateFramesizeList +# +# Description: Checks the framesize against 64-1518 valid EN type +# framesize and tests against protocol restrictions +# +# Arguments(s): framesizeList: framesizes to validate +# +# Returns: TCL_OK or TCL_ERROR (if invalid) +# +######################################################################## +proc validateFramesizeList {framesizeList} \ +{ + set retCode $::TCL_ERROR + + foreach framesize $framesizeList { + set retCode [validateFramesize $framesize] + if {$retCode == $::TCL_ERROR} { + break + } + } + + return $retCode +} + + +######################################################################## +# Procedure: validatePreamblesize +# +# This command checks the preamble size against 2-256 valid EN preamble size +# +# Arguments(s): +# preambleSize - Preamblesize to validate +# +######################################################################## +proc validatePreamblesize {preambleSize} \ +{ + set retCode 0 + + if {$preambleSize > 256 || $preambleSize < 2} { + logMsg "Invalid preamble size, must be between 2 & 256" + set retCode 1 + } + + return $retCode +} + + +######################################################################## +# Procedure: getLearnProc +# +# This command determines which learn proc to use +# +# Arguments(s): +# portArray - port map, ie. one2oneArray, one2manyArray, etc +# +######################################################################## +proc getLearnProc {{portArray ""}} \ +{ + set learnproc send_learn_frames + + if {[learn cget -type] == "default"} { + learn config -type [getProtocolName [protocol cget -name]] + } + + switch [learn cget -type] { + default - + mac { + set learnproc send_learn_frames + } + ip { + set learnproc send_arp_frames + } + ipx { + set learnproc send_ripx_frames + } + ipV6 { + set learnproc send_neighborDiscovery_frames + } + + } + + return $learnproc +} + + +######################################################################## +# Procedure: validateProtocol +# +# Description: This command validates the protocol type for this test +# +# Arguments: protocols: list of allowable protocols for this test, +# ie. {mac ip ipx ipV6} +# +# Returns: ::TCL_OK or TCL_ERROR +# +######################################################################## +proc validateProtocol {protocols} \ +{ + set retCode $::TCL_OK + + set protocolName [getProtocolName [protocol cget -name]] + switch $protocolName { + mac - + ip - + ipx - + ipV6 { + if {[lsearch $protocols $protocolName] == -1} { + set retCode $::TCL_ERROR + } + } + default { + set retCode $::TCL_ERROR + } + } + return $retCode +} + + +######################################################################## +# Procedure: initMaxRate +# +# This command initializes an array containing the max rate values per +# all TX ports in the array +# +# Arguments(s): +# PortArray - port map, ie. one2oneArray, one2manyArray, etc +# maxRateArray - array containing the max rates for each tx port +# framesize - framesize ref for max rate +# userRateArray - array containing the actual user rate to tx +# percentRate - user-specifed percent of max rate +# preambleSize +# +######################################################################## +proc initMaxRate {PortArray maxRateArray framesize {userRateArray ""} {percentRate 100} {preambleSize 8}} \ +{ + upvar $PortArray portArray + upvar $maxRateArray maxRate + + if {[string length $userRateArray] > 0} { + upvar $userRateArray userRate + } + + set retCode $::TCL_OK + + set txRxList [getAllPorts portArray] + + foreach portMap $txRxList { + scan $portMap "%d %d %d" c l p + + set maxRate($c,$l,$p) [calculateMaxRate $c $l $p $framesize $preambleSize] + set userRate($c,$l,$p) [expr round($percentRate/100. * $maxRate($c,$l,$p))] + + if {$userRate($c,$l,$p) > $maxRate($c,$l,$p)} { + logMsg "****** WARNING: Rate $userRate($c,$l,$p) fps exceeded Maximum Rate $maxRate($c,$l,$p) for [getPortId $c $l $p]" + set userRate($c,$l,$p) $maxRate($c,$l,$p) + } + + if {$userRate($c,$l,$p) <= 0} { + logMsg "****** ERROR: Rate cannot be 0 for [getPortId $c $l $p]" + set retCode $::TCL_ERROR + } + } + + + return $retCode +} + + +######################################################################## +# Procedure: buildIpMcastMacAddress +# +# This command builds the MAC address to use when transmitting multi- +# cast packets. +# +# Arguments(s): +# groupAddress IP multicast group address +# +# NOTE:The Ethernet directly supports the sending of local multicast +# packets by allowing multicast addresses in the destination field of +# Ethernet packets. All that is needed to support the sending of +# multicast IP datagrams is a procedure for mapping IP host group +# addresses to Ethernet multicast addresses. +# +# An IP host group address is mapped to an Ethernet multicast address +# by placing the low-order 23-bits of the IP address into the low-order +# 23 bits of the Ethernet multicast address 01-00-5E-00-00-00 (hex) +# [RFC1112]. Because there are 28 significant bits in an IP host group +# address, more than one host group address may map to the same Ethernet +# multicast address. +######################################################################## +proc buildIpMcastMacAddress {groupAddress} \ +{ + set mcastIP [host2addr $groupAddress] + + # the lower 3 bytes of DA need to match the lower 23 bits of the multicast IP addr + set DA [format "%02x %02x %02x %02x %02x %02x" 01 00 0x5e \ + [expr "0x[lindex $mcastIP 1]" & 0x7f] \ + "0x[lindex $mcastIP 2]" \ + "0x[lindex $mcastIP 3]"] + + return $DA +} + + +######################################################################## +# Procedure: setPortName +# +# This command sets a character string name to a specified port +# +# Arguments(s): +# portName - name of port +# chassis +# card +# port +# +# Return: +# TCL_OK if port found +# +######################################################################## +proc setPortName {portName chassis card pt} \ +{ + set retCode 0 + + if [catch {port get $chassis $card $pt} retCode] { + global ixgPortNameMap + set ixgPortNameMap($chassis,$card,$pt) $portName + set retCode 0 + } else { + port config -name $portName + if [port set $chassis $card $pt] { + set ixgPortNameMap($chassis,$card,$pt) $portName + } + } + + return $retCode +} + + +################################################################################## +# Procedure: getPortString +# +# This command gets the port name as a string. +# +################################################################################## +proc getPortString {c l p {testCmd results}} \ +{ + set portname [getPortName $c $l $p] + + if {[$testCmd cget -portNameOption] == "both" && $portname != ""} { + set portString [format "%s.%s.%s %s" $c $l $p $portname] + } elseif {[$testCmd cget -portNameOption] == "number" && $portname != ""} { + set portString $portname + } elseif {[$testCmd cget -portNameOption] == "name" && $portname != ""} { + set portString $portname + } else { + set portString [format "%s.%s.%s" $c $l $p] + } + + return $portString +} + + +######################################################################## +# Procedure: getPortId +# +# This command gets the portID + character string name of a port +# +# Arguments(s): +# c +# l +# p +# +# Return: +# portName +# +######################################################################## +proc getPortId {c l p} \ +{ + + if [catch {port getId $c $l $p} portname] { + set portname "$c.$l.$p " + } + + return $portname +} + + +######################################################################## +# Procedure: getPortName +# +# This command gets the character string name from a specified port +# +# Arguments(s): +# chassis +# card +# port +# default - optionally, if no name was specified, name defaults +# to "$chassis.$card.$port", otherwise empty string returned. +# +# Return: +# portName +# +######################################################################## +proc getPortName {chassis card port {default default}} \ +{ + set retCode 0 + set portName "" + + global ixgPortNameMap + + if [catch {port get $chassis $card $port} retCode] { + if [catch {set portName $ixgPortNameMap($chassis,$card,$port)}] { + set portName "" + } + } else { + if [info exists ixgPortNameMap($chassis,$card,$port)] { + set portName $ixgPortNameMap($chassis,$card,$port) + setPortName $portName $chassis $card $port + unset ixgPortNameMap($chassis,$card,$port) + } + + if {$retCode != 0} { + set portName "" + } else { + set portName [port cget -name] + } + } + + if {$portName == "" && $default == "default"} { + set portName "$chassis.$card.$port" + } + + return $portName +} + + +######################################################################################## +# Procedure: setPortFactoryDefaults +# +# Description: This command sets the factory defaults on a port +# +# Argument(s): +# chassis - +# card - +# port - +# +######################################################################################## +proc setPortFactoryDefaults {chassis card port} \ +{ + set retCode [port setFactoryDefaults $chassis $card $port] + switch $retCode " + $::ixTcl_ok { + } + $::ixTcl_generalError { + errorMsg \"Error setting factory defaults on port [getPortId $chassis $card $port].\" + } + $::ixTcl_notAvailable { + errorMsg \"Port [getPortId $chassis $card $port] is unavailable, check ownership.\" + } + " + + return $retCode +} + + +######################################################################################## +# Procedure: setFactoryDefaults +# +# Description: This command sets the factory defaults on all ports in the map +# +# Argument(s): +# portList - list containing all ports to set factory defaults on, may be an array +# +######################################################################################## +proc setFactoryDefaults {portList {write nowrite}} \ +{ + set retCode $::TCL_OK + + foreach port $portList { + scan $port "%d %d %d" chassisId cardId portId + + set retCode [setPortFactoryDefaults $chassisId $cardId $portId] + if {$retCode != $::TCL_OK} { + break + } + } + + if {$retCode == $::TCL_OK && $write == "write"} { + ixWritePortsToHardware portList + } + + return $retCode +} + + +######################################################################## +# Procedure: getProtocolName +# +# This command returns the protocol as a character string name +# +# Arguments(s): +# protocol integer value of protocol, from protocol cget -name +# +# Return: +# character name of protocol or 0 if error +# +######################################################################## +proc getProtocolName {protocol} \ +{ + global kProtocol + + # this is for scripts that are sending valid ip packets, but + # we are treating it like an l2 test + if {[advancedTestParameter cget -l2DataProtocol] == "ip"} { + set name "mac" + return $name + } + + foreach {pr name} [array get kProtocol] { + if {$protocol == $pr} { + return $name + } + } + + return 0 +} + + +######################################################################## +# Procedure: getDuplexModeString +# +# This command returns the duplex mode as a character string name +# +# Arguments(s): +# duplexMode integer value of duplex mode, from port cget -duplex +# +# Return: +# character name of duplex mode or 0 if error +# +######################################################################## +proc getDuplexModeString {duplex} \ +{ + global kDuplexMode + + foreach {dp name} [array get kDuplexMode] { + if {$duplex == $dp} { + return $name + } + } + + return 0 +} + + +######################################################################## +# Procedure: disableUdfs +# +# This command disables the udfs in the list. +# +# Arguments(s): +# udfList Tcl list of udfs to disable, in the form {1 2 3 4} +# +# Return: +# 1 if error +# +######################################################################## +proc disableUdfs {udfList} \ +{ + set retCode 0 + + udf config -enable false + foreach u $udfList { + if [udf set $u] { + set retCode 1 + } + } + + return $retCode +} + + +######################################################################## +# Procedure: getIpClassName +# +# This command returns the class type of IP address as a character string name +# +# Arguments(s): +# classNum integer value of IP addr class, from ip cget -class +# +# Return: +# character name of IP addr class or 0 if error +# +######################################################################## +proc getIpClassName {classNum} \ +{ + global kIpAddrClass + + foreach {ipclass className} [array get kIpAddrClass] { + if {$classNum == $ipclass} { + return $className + } + } + + return 0 +} + + +######################################################################## +# Procedure: getMinimum +# +# This command returns the minimum value in the passed array +# +# Arguments(s): +# ValArray - array of values +# +# Return: +# minimum value in array +# +######################################################################## +proc getMinimum {ValArray} \ +{ + upvar $ValArray valArray + + foreach index [array names valArray] { + if {[info exists minimum] && $valArray($index) >= $minimum} { + continue + } + set minimum $valArray($index) + } + + if {![info exists minimum]} { + set minimum 0 + } + + return $minimum +} + + +######################################################################## +# Procedure: swapPortList +# +# This command swaps the Tx/Rx pairs +# +# Argument(s): +# portList list of ports, ie, one2oneArray, one2manyArray etc +# newList copied list +# +######################################################################## +proc swapPortList {portList newList} \ +{ + upvar $portList old + upvar $newList new + + set retCode 0 + + if [info exists new] { + unset new + } + + foreach txMap [lsort [array names old]] { + scan $txMap "%d,%d,%d" tx_c tx_l tx_p + + foreach rxMap [lsort $old($txMap)] { + scan [join $rxMap] "%d %d %d" rx_c rx_l rx_p + + set new($rx_c,$rx_l,$rx_p) [lappend new($rx_c,$rx_l,$rx_p) [list $tx_c $tx_l $tx_p]] + } + } + + return $retCode +} + + +######################################################################## +# Procedure: copyPortList +# +# This command copies the port array into another local variable +# +# Argument(s): +# portList list of ports, ie, one2oneArray, one2manyArray etc +# newList copied list +# +######################################################################## +proc copyPortList {SourceList DestList} \ +{ + upvar $SourceList sourceList + upvar $DestList destList + + array set destList [array get sourceList] + + return $::TCL_OK +} + + +######################################################################## +# Procedure: removePorts +# +# This command removes ports from the port array/list. +# +# Argument(s): +# PortList port map or list of ports, ie, one2oneArray, one2manyArray etc +# removePortList list of ports to be removed from the PortList +# +######################################################################## +proc removePorts {PortList removePortList} \ +{ + + upvar $PortList portList + + set retCode 0 + + if [array exists portList] { + foreach txMap [array names portList] { + + scan [split [join $txMap] ,] "%d %d %d" tx_c tx_l tx_p + + if {[lsearch $removePortList [list $tx_c $tx_l $tx_p]] >= 0} { + logMsg "Removing TX port [getPortId $tx_c $tx_l $tx_p] from map..." + unset portList($tx_c,$tx_l,$tx_p) + continue + } + + foreach rxMap $portList($tx_c,$tx_l,$tx_p) { + + set index [lsearch $removePortList $rxMap] + + if {$index >= 0} { + logMsg "Removing RX port [join $rxMap ,] from map..." + set rmIndex [lsearch $portList($tx_c,$tx_l,$tx_p) $rxMap] + + set portList($tx_c,$tx_l,$tx_p) [lreplace $portList($tx_c,$tx_l,$tx_p) $rmIndex $rmIndex] + + if {[llength $portList($tx_c,$tx_l,$tx_p)] <= 0} { + unset portList($tx_c,$tx_l,$tx_p) + } + + } + } + } + + if {[llength [array names portList]] == 0} { + set errMsg "Error - invalid map after removing ports!!!" + #errorMsg "Error - invalid map after removing ports!!!" + set retCode 1 + } + + } else { + + if { [llength $portList] >0 && [llength $removePortList] > 0 } { + foreach portMap $removePortList { + scan [join $portMap] "%d %d %d" c l p + + set index [lsearch $portList [list $c $l $p]] + if {$index >= 0} { + logMsg "Removing port [join [split $portMap ,] ,] from map..." + set portList [lreplace $portList $index $index] + } + } + } else { + logMsg "No ports to remove." + set retCode 1 + } + } + + return $retCode +} + + +######################################################################## +# Procedure: lnumsort +# +# This command sorts a list (like ports) into numerical order +# +# Argument(s): +# option -decending +# MyList list of stuff +# +######################################################################## +proc lnumsort {option {MyList ""}} \ +{ + if {[string index [lindex $option 0] 0] != "-"} { + set MyList $option + set sortedList [lsort -dictionary $MyList] + } else { + set sortedList [lsort -dictionary $option $MyList] + } + + return $sortedList +} + + +######################################################################## +# Procedure: mergeLists +# +# This command merges two lists +# +# Arguments(s): +# MergedList - returned merged list w/dups removed +# args - variable number of lists to be merged together +# +# +# Return values: +# If there are duplicate items in the list, returns number of dups, otherwise +# returns 0 +# +######################################################################## +proc mergeLists {MergedList args} \ +{ + upvar $MergedList mergedList + + set mergedList "" + set sortFlag 0 + set duplicate 0 + + foreach list $args { + if {$list == "sortedOrder"} { + set sortFlag 1 + continue + } + foreach item $list { + if {[lsearch $mergedList $item] >= 0} { + incr duplicate + } else { + lappend mergedList $item + } + } + } + + if {$sortFlag} { + set mergedList [lnumsort $mergedList] + } + + return $duplicate +} + + + +######################################################################## +# Procedure: host2addr +# +# Description: This command converts an IP address in form 100.101.102.103 +# to a list of hex bytes all in upper case letters. +# +# Arguments(s): ipAddr: ip address to convert +# +# Return(s): IP address in list of hex bytes +# +######################################################################## +proc host2addr {ipAddr} \ +{ + set ipHex {} + + set delimiter . + regexp {([:.])} $ipAddr match delimiter + + set protocol [expr [string match $delimiter :]?"ipV6":"ip"] + + switch $protocol { + ipV6 { + set ipHex [ipv6::host2addr $ipAddr] + } + ip { + set ipname [split $ipAddr $delimiter] + if {[llength [string trim [join $ipname]]] == 4} { + foreach i $ipname { + if {$i > 255 || $i < 0} { + set ipHex {} + break + } + set hexCharacter [format "%02X" $i] + set ipHex [linsert $ipHex end $hexCharacter] + } + } + } + } + + return $ipHex +} + + +######################################################################## +# Procedure: long2IpAddr +# +# Description Converts long word into an IP address in +# form x.y.z.a +# +# Argument(s): longword to convert +# +# Returns: Ip Address x.y.z.a OR +# 0.0.0.0 if invalid input arguments +# +######################################################################### +proc long2IpAddr {value} \ +{ + if [catch {set ipAddress "[expr {(($value >> 24) & 0xff)}].[expr {(($value >> 16) & 0xff)}].[expr {(($value >> 8 ) & 0xff)}].[expr {$value & 0xff}]"} ipAddress] { + set ipAddress 0.0.0.0 + } + + return $ipAddress +} + + +######################################################################## +# Procedure: byte2IpAddr +# +# Description Converts 4 hexideciaml bytes into an IP address in +# form x.y.z.a +# +# Argument(s): hexBytes: list of bytes to convert +# +# Returns: Ip Address x.y.z.a OR +# 0.0.0.0 if invalid input arguments +# +######################################################################### +proc byte2IpAddr {hexBytes} \ +{ + set newIpAddr "0.0.0.0" + + # Validate input parameters. + + # If given a string of 8 bytes instead of list of 4, convert it to a list. + set hexBytes [string trim $hexBytes] + set hexBytesLength [llength $hexBytes] + + if {$hexBytesLength == 1} { + + if {[string length [string trim $hexBytes]] == 8} { + + set hexBytesString $hexBytes + set hexBytes [list] + for {set i 0} {$i < 4} {incr i} { + lappend hexBytes [string range $hexBytesString 0 1] + set hexBytesString [string replace $hexBytesString 0 1] + } + + } else { + set hexBytes [list 0 0 0 0] + } + + } elseif {$hexBytesLength != 4} { + set hexBytes [list 0 0 0 0] + } + + + # Convert to decimal. + regsub -all {(.*) (.*) (.*) (.*)} $hexBytes \ + {[format "%d %d %d %d" "0x\1" "0x\2" "0x\3" "0x\4"]} newIpAddr + if {[catch {subst $newIpAddr} newIpAddr]} { + set newIpAddr [list 0 0 0 0] + } + + # If any invalid values, return 0.0.0.0 + foreach byte $newIpAddr { + if {$byte < 0 || $byte > 255} { + set newIpAddr [list 0 0 0 0] + break + } + } + set newIpAddr [join $newIpAddr .] + + debugMsg "byte2IpAddr: newIpAddr = $newIpAddr" + return $newIpAddr +} + + + + +######################################################################## +# Procedure: num2ip +# +# Description: +# This command convert a number to an IP address. +# +# Arguments(s): +# num - number +# +# Returns: +# An IP address. +# +######################################################################## +proc num2ip {num} \ +{ + set ipAddr [format "%d.%d.%d.%d" [expr {($num >> 24) & 255}] [expr {($num >> 16) & 255}] \ + [expr {($num >> 8) & 255}] [expr { $num & 255}]] + return $ipAddr +} + + + +######################################################################## +# Procedure: ip2num +# +# This command converts an IP address of the form d.d.d.d into a 32-bit +# unsigned number. +# +# Arguments(s): +# ipAddr - ip address of the form d.d.d.d +# +# Return: +# ipNum +# +######################################################################## +proc ip2num {ipAddr} \ +{ + set ipNum 0 + + if {[scan $ipAddr "%d.%d.%d.%d" a b c d] == 4} { + set ipNum [format %u [expr {($a<<24)|($b<<16)|($c<<8)|$d}]] + } + + return $ipNum +} + + +######################################################################## +# Procedure: long2octet +# +# This command converts a multi-byte number into multi octets in a list +# +# Argument(s): +# value the value to convert +# +######################################################################### +proc long2octet {value {sizeInBytes 2} } \ +{ + + switch $sizeInBytes { + 2 { + return [format "%02x %02x" \ + [expr {(($value >> 8) & 0xff)}] \ + [expr {$value & 0xff}]] + } + 3 { + return [format "%02x %02x %02x" \ + [expr {(($value >> 16) & 0xff)}] \ + [expr {(($value >> 8 ) & 0xff)}] \ + [expr {$value & 0xff}]] + } + 4 { + return [format "%02x %02x %02x %02x" \ + [expr {(($value >> 24) & 0xff)}] \ + [expr {(($value >> 16) & 0xff)}] \ + [expr {(($value >> 8 ) & 0xff)}] \ + [expr {$value & 0xff}]] + } + 1 - + default { + return [list [format %02x $value ]] + } + } +} + + +######################################################################## +# Procedure: list2word +# +# This command converts a 2-byte list into a word +# +# Argument(s): +# mylist the value to convert +# +######################################################################### +proc list2word {mylist} \ +{ + set listlength [llength $mylist] + set result 0 + + if {$listlength <= 2 && $listlength > 0} { + incr listlength -1 + set j 0 + for {set i $listlength} {$i >= 0} {incr i -1} { + incr result [expr [hextodec [lindex $mylist $i]] << ($j * 8)] + incr j + } + } + + return $result +} + +######################################################################## +# Procedure: value2Hexlist +# +# Description: This command converts a number into a hex +# +# Argument(s): +# value - a number +# width - the hex list lenght to be generated +# +######################################################################### +proc value2Hexlist { value width } \ +{ + set retValue {} + while { $width } { + set retValue [linsert $retValue 0 [format "%02x" [mpexpr $value & 255]]] + incr width -1 + set value [mpexpr $value >> 8] + } + return $retValue +} + +######################################################################## +# Procedure: hexlist2Value +# +# Description: This command converts a hex list into a number +# +# Argument(s): +# hexlist - the hex list ( example {01 02 03 04} ) +# +######################################################################### +proc hexlist2Value { hexlist } \ +{ + set retValue 0 + foreach byte $hexlist { + set retValue [mpexpr ($retValue << 8) | 0x$byte] + } + return $retValue +} + + + +######################################################################## +# Procedure: expandHexString +# +# Description: Expands a string of delimited hex values: +# +# 0 a b 1 becomes 00 0a 0b 01 +# 0:a:b:21 becomes 00:0a:0b:21 +# +# Does not verify the validity of the original hex string. +# +# Argument(s): bytesList +# delimiter +# +# Returns: expanded hex string +# +######################################################################### +proc expandHexString {byteList {delimiter :}} \ +{ + set hexList [list] + + regsub -all $delimiter $byteList " " byteList + foreach byte $byteList { + regsub -all {(.*)} $byte \ + {[format "%02x " 0x\1]} byte + append hexList [subst "$byte"] + } + regsub -all " " [string trim $hexList] $delimiter hexList + + return $hexList +} + + +######################################################################## +# Procedure: getMultipleNumbers +# +# This procedure gives two numbers that are multiples of each other but +# less than the allowed maximum number. If he "number" is a prime number +# than this procedure may not be useful. +# +# Argument(s): +# number the number whose multiple is to be found +# maxAllowedNum the maximum allowed number +# numA the multiplier +# numB the divider +# +######################################################################### +proc getMultipleNumbers {number maxAllowedNum numA numB} \ +{ + upvar $numA a + upvar $numB b + + # just pick an arbitrary number for max value for loop + for {set divider 2} {$divider <= 1000} {incr divider} { + set result [mpexpr $number/$divider] + set remainder [mpexpr $number%$divider] + if {$remainder != 0} { + continue + } + if {$result > $maxAllowedNum} { + continue + } + set a $result + set b $divider + return 0 + } + + return 1 +} + + +######################################################################## +# Procedure: hextodec +# +# This command converts a hex number to a decimal number +# +# Argument(s): +# number - hex number to convert +# +######################################################################## +proc hextodec {number} \ +{ + if [catch {format "%u" "0x$number"} retCode] { + logMsg "Invalid hex number: $number" + set retCode -1 + } + return $retCode +} + + +######################################################################## +# Procedure: dectohex +# +# This command converts a decimal number to a hex number +# +# Argument(s): +# number - decimal number to convert +# +######################################################################## +proc dectohex {number} \ +{ + if [catch {format "%x" $number} retCode] { + logMsg "Invalid decimal number: $number" + set retCode -1 + } + + return $retCode +} + + +######################################################################## +# Procedure: incrMacAddress +# +# This command increments the last three bytes (24-bit word) of the MAC +# address. +# +# Argument(s): +# macaddr mac address to increment +# amt increment the field by this number +# +######################################################################## +proc incrMacAddress {macaddr amt} \ +{ + upvar $macaddr valList + + set hexnum [format "%02x%02x%02x" "0x[lindex $valList 3]" \ + "0x[lindex $valList 4]" "0x[lindex $valList 5]"] + set decnum [hextodec $hexnum] + set decnum [incr decnum $amt] + set hexnum [format "%06x" "0x[dectohex $decnum]"] + + scan $hexnum "%02s%02s%02s" byte3 byte4 byte5 + + set valList [lreplace $valList 3 3 $byte3] + set valList [lreplace $valList 4 5 $byte4 $byte5] + return $valList +} + + +######################################################################## +# Procedure: incrIpField +# +# Description: Increments the specified byte of IP address +# +# +# Argument(s): +# ipAddress IP address whose byte to be incremented +# byteNum the byte field to be incremented +# amount increment the field by this number +# +######################################################################## +proc incrIpField {ipAddress {byteNum 4} {amount 1}} \ +{ + set one [ip2num $ipAddress] + set two [expr {$amount<<(8*(4-$byteNum))}] + + return [long2IpAddr [expr {$one + $two}]] +} + + + +######################################################################## +# Procedure: incrIpFieldHexFormat +# +# Description: Increments the specified byte of IP address. Both the input +# and returned IP address are in hex format. +# +# +# Argument(s): +# ipAddress IP address whose byte to be incremented (It's in the form +# of 4 byte hex number: ex, "4c 2e 01 05" +# +# byteNum the byte field to be incremented +# amount increment the field by this number +# +######################################################################## +proc incrIpFieldHexFormat {ipAddress {byteNum 4} {amount 1}} \ +{ + set hexIpAddr 0x[join $ipAddress ""] + + set val [format %x [expr [format %d $hexIpAddr] + [expr {$amount<<(8*(4-$byteNum))}]]] + return [long2octet [format %d "0x$val"] 4] +} + + +######################################################################## +# Procedure: assignIncrMacAddresses +# +# Description: Assigns an incrementing MAC address +# +# Argument(s): +# portList a list of sorted ports +# +######################################################################## +proc assignIncrMacAddresses {portList} \ +{ + logMsg "Assigning incrementing addresses on all ports ..." + set retCode $::TCL_OK + + scan [lindex $portList 0] "%d %d %d" c l p + set currMacAddr [join [list 00 [format "%02x %02x" $l $p ] 00 00 00 ]] + + foreach maplist $portList { + scan $maplist "%d %d %d" c l p + + if {![IsPOSPort $c $l $p] } { + if [port get $c $l $p] { + errorMsg "Error getting port [getPortId $c $l $p]" + set retCode $::TCL_ERROR + } + + port config -MacAddress $currMacAddr + if [port set $c $l $p] { + errorMsg "Error setting port [getPortId $c $l $p]" + set retCode $::TCL_ERROR + } + + logMsg "[getPortId $c $l $p] ====> MAC: $currMacAddr" + set currMacAddr [incrMacAddress currMacAddr [port cget -numAddresses]] + } + } + return $retCode +} + + +######################################################################## +# Procedure: incrHostIpAddr +# +# Description: Increments the host portion of the IP address +# NOTE: will carry!! +# +# Argument(s): +# ipAddress - ip address to increment +# amount - amount to increment by +# +######################################################################## +proc incrHostIpAddr {ipAddress {amount 1}} \ +{ + return [incrIpField $ipAddress 4 $amount] +} + + +######################################################################## +# Procedure: waitForResidualFrames +# +# Description: Waits for residual rx frames +# +# Argument(s): +# time - time to wait +# +######################################################################## +proc waitForResidualFrames {time} \ +{ + logMsg "Waiting for Residual frames to settle down for $time seconds" + for {set timeCtr 1} {$timeCtr <= $time} {incr timeCtr} { + logMsg "Waited for $timeCtr of $time seconds" + after 1000 + } +} + + +######################################################################################## +# Procedure: getPerTxArray +# +# Description: Helper proc that seperates multiple map per Tx port +# +# Arguments(s): +# TxRxArray - map, ie. one2oneArray +# PerTxArray - per Tx map, ie. one2one or one2many +# txPort - tx port +# testCmd - name of test command, ie. tput +# +######################################################################################## +proc getPerTxArray {TxRxArray PerTxArray txPort } \ +{ + upvar $TxRxArray txRxArray + upvar $PerTxArray perTxArray + + set retCode 0 + + if [info exists perTxArray] { + unset perTxArray + } + foreach rxPort $txRxArray($txPort) { + scan [join $rxPort] "%d %d %d" rx_c rx_l rx_p + set perTxArray($txPort) [lappend perTxArray($txPort) [list $rx_c $rx_l $rx_p]] + } + + return $retCode +} + + +######################################################################## +# Procedure: getTxPorts +# +# Description: Gets all the Tx ports from any map array passed. +# +# Argument(s): +# MapArray the reference to map array to be scanned +# +# Returns: +# txList List containing all Tx ports +# +######################################################################## +proc getTxPorts {MapArray} \ +{ + upvar $MapArray mapArray + + set txList {} + + if [info exists mapArray] { + if {[array exists mapArray]} { + foreach txMap [array names mapArray] { + + regsub -all "," $txMap " " txPort + + if {[lsearch $txList $txPort] == -1} { + # add this Tx port to the list + lappend txList $txPort + } + } + } else { + foreach port $mapArray { + lappend txList [join [split $port ',']] + } + } + } + + return [lsort -dictionary $txList] +} + + +######################################################################## +# Procedure: getRxPorts +# +# Description: Gets all the Tx ports from any map array passed. +# +# Argument(s): +# MapArray the reference to map array to be scanned +# +# Returns: +# rxList List containing all Rx ports +# +######################################################################## +proc getRxPorts {MapArray} \ +{ + upvar $MapArray mapArray + + set rxList {} + + if [info exists mapArray] { + + if {[array exists mapArray]} { + + foreach {txMap rxMap} [array get mapArray] { + + foreach rxPort $rxMap { + if {[lsearch $rxList $rxPort] == -1} { + # add this Rx port to the list + lappend rxList $rxPort + } + } + } + + } else { + foreach port $mapArray { + lappend rxList [join [split $port ',']] + } + } + } + + return [lsort -dictionary $rxList] +} + + +######################################################################## +# Procedure: getAllPorts +# +# Description: Gets all the ports from any map array passed. +# +# Argument(s): MapArray: Reference to map array to be scanned +# +# Returns: portList: List containing all ports +# +######################################################################## +proc getAllPorts {MapArray} \ +{ + upvar $MapArray mapArray + + set portList {} + + if [info exists mapArray] { + if {[array exists mapArray]} { + + foreach {txMap rxMap} [array get mapArray] { + + regsub -all "," $txMap " " txPort + + if {[lsearch $portList $txPort] == -1} { + # add this Tx port to the list + lappend portList $txPort + } + + foreach rxPort $rxMap { + if {[lsearch $portList $rxPort] == -1} { + # add this Rx port to the list + lappend portList $rxPort + } + } + } + } else { + foreach port $mapArray { + lappend portList [join [split $port ',']] + } + } + } + + return [lsort -dictionary $portList] +} + + +######################################################################## +# Procedure: comparePortArray +# +# This command compares two arrays; if one array contains ports that +# the other array doesn't, it will optionally remove those ports +# +# Argument(s): +# KeepArray array of ports to compare against +# CompareArray array to check +# removePorts optionally remove ports from compareArray that +# are not in keepArray +# +# Return: +# returns 1 if ports are in CompareArray that are not in KeepArray +# +######################################################################## +proc comparePortArray {KeepArray CompareArray {removePorts remove}} \ +{ + upvar $KeepArray keepArray + upvar $CompareArray compareArray + + set retCode 0 + + set keepList [getAllPorts keepArray] + + foreach txPort [array names compareArray] { + scan [split [join $txPort] ,] "%d %d %d" c l p + # if we don't find the tx port in the keepArray, remove it from the compareArray + if {[lsearch $keepList "$c $l $p"] == -1 && [info exists compareArray]} { + if {$removePorts == "remove"} { + unset compareArray($txPort) + } + set retCode 1 + continue + } + # now cycle through the rx ports & remove them if they're not in the keepArray + foreach rxPort $compareArray($txPort) { + scan [join $rxPort] "%d %d %d" c l p + + if {[lsearch $keepList "$c $l $p"] == -1} { + if {$removePorts == "remove"} { + set index [lsearch $compareArray($txPort) "$c $l $p"] + set compareArray($txPort) [lreplace $compareArray($txPort) $index $index] + } + set retCode 1 + } + if {[llength $compareArray($txPort)] <= 0} { + unset compareArray($txPort) + } + } + } + + return $retCode +} + + +######################################################################## +# Procedure: mergePortArray +# +# Description: This command merges the two array into one (TxRxArray and +# MapArray are merged into TxRxArray +# +# +# Argument(s): +# TxRxArray - first array before merge and then final array after merge +# mapArray - second array to be merged into TxRxArray +# +# +######################################################################## +proc mergePortArray { TxRxArray MapArray } \ +{ + upvar $TxRxArray txRxArray + upvar $MapArray mapArray + + set retCode 0 + + foreach txMap [array names mapArray] { + scan $txMap "%d,%d,%d" tx_c tx_l tx_p + foreach rxMap $mapArray($txMap) { + scan [join $rxMap] "%d %d %d" rx_c rx_l rx_p + + if [info exists txRxArray($txMap)] { + if {[lsearch $txRxArray($txMap) [list $rx_c $rx_l $rx_p]] < 0} { + lappend txRxArray($txMap) [list $rx_c $rx_l $rx_p] + } + } else { + set txRxArray($txMap) [list [list $rx_c $rx_l $rx_p]] + } + } + } + + return $retCode +} + + +######################################################################## +# Procedure: getAdvancedSchedulerArray +# +# This command seperates the portmap that supports adand other portmap from txRxArray +# +# Argument(s): +# +# TxRxArray - map, ie. one2oneArray +# AdvancedSchedulerArray - map for interfaces that support advanced stream scheduler +# OtherArray - map for rest of the interfaces (rest of the TxRxArray) +# +######################################################################## +proc getAdvancedSchedulerArray {TxRxArray AdvancedSchedulerArray OtherArray} \ +{ + upvar $TxRxArray txRxArray + upvar $AdvancedSchedulerArray advancedSchedulerArray + upvar $OtherArray otherArray + + set retCode 0 + + if [info exists advancedSchedulerArray] { + unset interfaceArray + } + + if [info exists otherArray] { + unset otherArray + } + + foreach txMap [lnumsort [array names txRxArray]] { + scan $txMap "%d,%d,%d" tx_c tx_l tx_p + foreach rxMap $txRxArray($tx_c,$tx_l,$tx_p) { + scan [join $rxMap] "%d %d %d" rx_c rx_l rx_p + + if [port isValidFeature $tx_c $tx_l $tx_p portFeatureAdvancedScheduler] { + if [info exist advancedSchedulerArray($tx_c,$tx_l,$tx_p)] { + if { [lsearch $advancedSchedulerArray($tx_c,$tx_l,$tx_p) [list $rx_c $rx_l $rx_p]] < 0 } { + set advancedSchedulerArray($tx_c,$tx_l,$tx_p) [lappend advancedSchedulerArray($tx_c,$tx_l,$tx_p) [list $rx_c $rx_l $rx_p]] + } + } else { + set advancedSchedulerArray($tx_c,$tx_l,$tx_p) [lappend advancedSchedulerArray($tx_c,$tx_l,$tx_p) [list $rx_c $rx_l $rx_p]] + } + } else { + if [info exist otherArray($tx_c,$tx_l,$tx_p)] { + if { [lsearch $otherArray($tx_c,$tx_l,$tx_p) [list $rx_c $rx_l $rx_p]] < 0 } { + set otherArray($tx_c,$tx_l,$tx_p) [lappend otherArray($tx_c,$tx_l,$tx_p) [list $rx_c $rx_l $rx_p]] + } + } else { + set otherArray($tx_c,$tx_l,$tx_p) [lappend otherArray($tx_c,$tx_l,$tx_p) [list $rx_c $rx_l $rx_p]] + } + } + } + } + + debugMsg "advancedSchedulerArray: [array get advancedSchedulerArray]" + debugMsg "otherArray: [array get otherArray]" + + return $retCode +} + + +######################################################################## +# +# NOTE: This proc is only used by Scriptmate, it will be removed from +# IxOs eventually +# +# Procedure: cleanUpMultiuser +# +# Description: Clears ownership and does ixLogout if applicable +# +# Argument(s): +# +######################################################################## +proc cleanUpMultiuser {} \ +{ + global multiList + + if {[info exists multiList] && ([llength $multiList] > 0)} { + ixClearOwnership $multiList + logMsg "Cleared ownership for the following ports:" + logMsg "$multiList" + } + + if {[testConfig::getLoginId] != ""} { + logMsg "[testConfig::getLoginId] - logging out." + ixLogout + } + +} + + +######################################################################## +# Procedure: cleanUp +# +# Description: Cleans up global memory & empties the chassis chain +# +# Argument(s): +# +######################################################################## +proc cleanUp {} \ +{ + global halCommands testConf + global chassisGroup ixStopTest + + # Special case that sometimes happens that a global named item existed + if {[string compare [info globals item] "item"] == 0} { + global item + catch {unset item} + } + + set ixStopTest 0 + + dhcpStopTimers + + #if {(![info exists cleanUpDone]) || ($cleanUpDone == 1)} { + # return + # } + + # destroy the chassisGroup - we don't really care if it succeeded or not... + if [info exists chassisGroup] { + if {[info commands portGroup] != ""} { + portGroup destroy $chassisGroup + } + } + + foreach item [info globals ixg*] { + global $item + catch {unset $item} + } + + + + # delete all chassis from the chain, call destructors of SWIG commands + # and forget the package + if {[info commands chassisChain] != ""} { + chassisChain removeAll + } + + if [info exists halCommands] { + foreach halCmd $halCommands { + if {[info commands $halCmd] != ""} { + debugMsg "Deleting $halCmd" + rename $halCmd "" + # remove this commands from the list + lreplace $halCommands 0 0 + } + } + + # now delete the list + unset halCommands + } + + # we need to delete the pointer refs too, because otherwise the next package req will be using stale pointers + foreach ptr [info global *Ptr] { + if [catch {unset ::$ptr} msg] {puts $msg} + } + + if [info exists testConf] { + unset testConf + } + + debugOff + logOff + + ixFileUtils::closeAll + + if {[isUNIX]} { + if [tclServer::isTclServerConnected] { + tclServer::disconnectTclServer + } + } + + package forget IxTclHal + package forget IxTclProtocol + + if [info exists defineCommand::commandList] { + foreach testCmd $defineCommand::commandList { + if { $testCmd != "results" } { + $testCmd setDefault + } + + } + } + + ixTclHal::cleanUpDone + + return +} + + +############################################################################### +# Procedure: isIpAddressValid +# +# Description: Verify that the ip address is valid. +# +# Arguments: ipAddress - the ip address to validate +# +# Returns: true if the ip address is valid, false otherwise. +############################################################################### +proc isIpAddressValid {ipAddress} { + + set retCode $::true + + if {[info tclversion] == "8.0"} { + # Advanced regular expressions are not supported in 8.0 + + # First check to see that there are four octets + if {[regexp {^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$} $ipAddress]} { + + # Now check each octet for a legitimate value + foreach byte [split $ipAddress .] { + if {($byte < 0) || ($byte > 255)} { + set retCode $::false + break + } + } + } else { + set retCode $::false + } + } else { + + # The ip address should be four octets + if {[regexp {^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$} \ + $ipAddress]} { + + # Now check each octet for a legitimate value + foreach byte [split $ipAddress .] { + if {($byte < 0) || ($byte > 255)} { + set retCode $::false + break + } + } + } else { + set retCode $::false + } + } + + return $retCode +} + + +############################################################################### +# Procedure: isMacAddressValid +# +# Description: Verify that the mac address is valid. +# +# Input: macAddress: address to validate +# +# Output: TCL_OK if address is valid, else +# TCL_ERROR +# +############################################################################### +proc isMacAddressValid {macAddress} \ +{ + set retCode $::TCL_ERROR + + regsub -all { |:} $macAddress " " macAddress + if {[llength $macAddress] == 6} { + + set retCode $::TCL_OK + foreach value $macAddress { + if {[string length $value] == 2} { + if {![regexp {[0-9a-fA-F]{2}} $value match]} { + set retCode $::TCL_ERROR + break + } + } else { + set retCode $::TCL_ERROR + break + } + } + } + + return $retCode +} + + +############################################################################### +# Procedure: isPartialMacAddressValid +# +# Description: Given a mac address, is it valid. The given address does not have +# to be complete +# +# Arguments: macAddress - the partial address to verify +# +# Returns: 1 if it is valid, 0 if not valid +############################################################################### +proc isPartialMacAddressValid { macAddress } \ +{ + set retCode 1 + + if {[info tclversion] > 8.0} { + if {![regexp {^([0-9a-fA-F]{1,2}( )*)*$} $macAddress]} { + set retCode 0 + } + } else { + if {[string length $macAddress] > 2} { + set splitChar [string index $macAddress 2] + set macAddress [split $macAddress $splitChar] + } + + foreach value $macAddress { + if {![regexp {^([0-9a-fA-F]+)$} $value]} { + set retCode 0 + } + } + } + + return $retCode +} + + +############################################################################### +# Procedure: getCommandParameters +# +# Description: Retrieve the parameters for a given command. +# +# Arguments: command - name of the command. +# +# Returns All the parameters of the specified command. +############################################################################### +proc getCommandParameters {command} \ +{ + set commandParameters {} + + if {![info exists command] || ($command == "")} { + return $commandParameters + } + + set testMethods [format "%s%s" $command "Methods"] + + global $testMethods + + foreach parm [set ${testMethods}(cget)] { + lappend commandParameters $parm + } + + return $commandParameters +} + + +############################################################################### +# Procedure: changePortLoopback +# +# Description: Retrieve the parameters for a given command. +# +# Arguments: TxRxArray - map, ie. one2oneArray +# enabled - flag to enable or disable the port loopback +# +# Output: TCL_OK if loopback enabled/disabled, else +# TCL_ERROR +############################################################################### +proc changePortLoopback {TxRxArray {enabled true} {verbose noVerbose}} \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + if {$verbose == "verbose" } { + if {$enabled == $::true} { + logMsg "Putting ports into loopback." + } else { + logMsg "Getting ports out of loopback." + } + } + + foreach txMap [getAllPorts txRxArray] { + scan $txMap "%d %d %d" c l p + + if [port get $c $l $p] { + errorMsg "Error getting port [getPortId $c $l $p]" + set retCode $::TCL_ERROR + } + port config -loopback $enabled + + # This part apply only to Gig SX card + if { $enabled } { + port config -rxTxMode $::gigLoopback + } else { + port config -rxTxMode $::gigNormal + } + + if [port set $c $l $p] { + errorMsg "Error setting port [getPortId $c $l $p]" + set retCode $::TCL_ERROR + } + } + + if [writePortsToHardware txRxArray -noProtocolServer ] { + errorMsg "Error writing port configurations." + set retCode $::TCL_ERROR + } + + return $retCode +} + + +################################################################################# +# Procedure: validateUnidirectionalMap +# +# Description: This command validates the unidirectional map +# +# Argument(s): +# TxRxArray - map, ie. one2oneArray +# +# Results : 0 : No error found +# 1 : Error found +# +################################################################################# +proc validateUnidirectionalMap {TxRxArray } \ +{ + upvar $TxRxArray txRxArray + + set retCode $::TCL_OK + + set txPortList [getTxPorts txRxArray] + set rxPortList [getRxPorts txRxArray] + + if {[map cget -echo] != "true" } { + foreach txMap $txPortList { + if { [lsearch $rxPortList $txMap] > -1 } { + logMsg "***** WARNING:Invalid map configuration, only unidirectional map is allowed." + set retCode $::TCL_ERROR + break + } + } + } + + return $retCode +} + + +############################################################################### +# Procedure: getTxRxModeString +# +# Description: Retrieve the string for the value given +# +# Arguments: modeType - txMode or rxMode +# value - integer value of the string +# +# Output: Returns the string corresponding that value +# +############################################################################### +proc getTxRxModeString { value {modeType "TX"} } \ +{ + set retString "Invalid" + + set modeType [string toupper $modeType] + + set modeTX($::portTxPacketStreams) portTxPacketStreams + set modeTX($::portTxPacketFlows) portTxPacketFlows + set modeTX($::portTxTcpSessions) portTxTcpSessions + set modeTX($::portTxTcpRoundTrip) portTxTcpRoundTrip + set modeTX($::portTxModeAdvancedScheduler) portTxModeAdvancedScheduler + set modeTX($::portTxModeBert) portTxModeBert + set modeTX($::portTxModeEcho) portTxModeEcho + set modeTX($::portTxModeBertChannelized) portTxModeBertChannelized + set modeTX($::portTxModeDccStreams) portTxModeDccStreams + set modeTX($::portTxModeDccAdvancedScheduler) portTxModeDccAdvancedScheduler + set modeTX($::portTxModeDccFlowsSpeStreams) portTxModeDccFlowsSpeStreams + set modeTX($::portTxModeDccFlowsSpeAdvancedScheduler) portTxModeDccFlowsSpeAdvancedScheduler + set modeTX($::portTxModeAdvancedSchedulerCoarse) portTxModeAdvancedSchedulerCoarse + set modeTX($::portTxModePacketStreamsCoarse) portTxModePacketStreamsCoarse + + set modeRX($::portCapture) portCapture + set modeRX($::portPacketGroup) portPacketGroup + set modeRX($::portRxModeWidePacketGroup) portRxModeWidePacketGroup + set modeRX($::portRxTcpSessions) portRxTcpSessions + set modeRX($::portRxTcpRoundTrip) portRxTcpRoundTrip + set modeRX($::portRxDataIntegrity) portRxDataIntegrity + set modeRX($::portRxFirstTimeStamp) portRxFirstTimeStamp + set modeRX($::portRxSequenceChecking) portRxSequenceChecking + set modeRX($::portRxModeBert) portRxModeBert + set modeRX($::portRxModeIsl) portRxModeIsl + set modeRX($::portRxModeBertChannelized) portRxModeBertChannelized + set modeRX($::portRxModeDcc) portRxModeDcc + set modeRX($::portRxModeEcho) portRxModeEcho + set modeRX($::portRxModeWidePacketGroup) portRxModeWidePacketGroup + set modeRX($::portRxModePrbs) portRxModePrbs + set modeRX($::portRxModeRateMonitoring) portRxModeRateMonitoring + set modeRX($::portRxModePerFlowErrorStats) portRxModePerFlowErrorStats + + + if {$modeType == "TX"} { + if [info exists modeTX($value)] { + set retString $modeTX($value) + } + } else { + set flag 0 + set modes "" + for {set i 0} {$i < [llength [array names modeRX]]} {incr i} { + set enumValue [expr 1 << $i] + + if {($enumValue > 1) && [expr $value & $enumValue] && $flag} { + append modes " | " + } + if {[expr $value & $enumValue]} { + set flag 1 + } + set enumValue [expr $value & $enumValue] + if [info exists modeRX($enumValue)] { + append modes $modeRX($enumValue) + } + } + set retString $modes + } + + return $retString +} + + +######################################################################## +# Procedure: removeStreams +# +# Description: This proc removes all the stream on ports in given map +# +# Arguments: TxRxPortList - map, ie. one2oneArray +# +# Results : 0 : No error found +# 1 : Error found +# +######################################################################## +proc removeStreams { TxRxPortList {verbose verbose} } \ +{ + upvar $TxRxPortList txRxPortList + + set retCode $::TCL_OK + set portList [getAllPorts txRxPortList] + + if {$verbose == "verbose"} { + logMsg "Removing streams on the ports...\n" + } + + foreach portItem $portList { + scan $portItem "%d %d %d" chassId cardId portId + + # since portFeaturePacketStreams is a circuit level feature, but we don't have to have + # a circuit id, since we want to clear all the streams under the port, therefore the below + # condition will cover the VCAT ports + set retValue [expr [port isValidFeature $chassId $cardId $portId portFeaturePacketStreams] || \ + [port isValidFeature $chassId $cardId $portId portFeatureAdvancedScheduler] || \ + [port isActiveFeature $chassId $cardId $portId portFeatureVcat]] + + switch $retValue { + 1 { + lappend featurePortList [list $chassId $cardId $portId] + } + 0 { + errorMsg "!WARNING: portFeaturePacketStreams is not supported on port [getPortId $chassId $cardId $portId]" + continue + } + } + + # using a temp var retValue so I don't step on retCode + set retValue [port reset $chassId $cardId $portId] + if {$retValue == $::ixTcl_notAvailable} { + errorMsg "Port [getPortId $chassId $cardId $portId] is unavailable, check ownership." + set retCode $retValue + } elseif {$retValue != $::TCL_OK} { + errorMsg "Error deleting streams on port $chassId $cardId $portId" + set retCode $retValue + } + } + + if {[info exists featurePortList] && $retCode == $::TCL_OK} { + set retCode [ixWriteConfigToHardware featurePortList -noProtocolServer ] + } + + return $retCode +} + + +############################################################################### +# Procedure: getIpV4MaskWidth +# +# Description: This proc gets ip mask as input and calculates the maskWidth. +# +# Arguments: ip mask - ipV4 format. +# +# Returns mask width. +############################################################################### +proc getIpV4MaskWidth {ipV4Mask} \ +{ + scan $ipV4Mask "%d.%d.%d.%d" b1 b2 b3 b4 + + set result [mpexpr ($b4 | $b3 << 8 | $b2 << 16 | $b1 << 24) ^ 0xFFFFFFFF] + + for {set mask 0} { $mask < 32} {incr mask} { + if { [mpexpr $result >> $mask] == 0} { + break; + } + } + set mask [expr 32 - $mask] + + return $mask +} + + +############################################################################### +# Procedure: getIpV4MaskFromWidth +# +# Description: This proc takes the mask prefix as input and calculates the ip mask. +# +# Arguments: mask width - an integer number between 0 and 32. +# +# Returns mask ip. +############################################################################### +proc getIpV4MaskFromWidth {maskWidth} \ +{ + set mask [mpexpr (0xffffffff << (32 - $maskWidth)) & 0xffffffff] + return [num2ip $mask] +} + + + diff --git a/dep/tclclient/ixTcl1.0/Generic/vlanUtils.tcl b/dep/tclclient/ixTcl1.0/Generic/vlanUtils.tcl new file mode 100644 index 00000000..c92a4834 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/vlanUtils.tcl @@ -0,0 +1,327 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: vlanUtils.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 11-02-2001 DS +# +# Description: This file contains common vlan utilities +# +######################################################################## + +proc vlanUtilsSetDefault {} { + set ::vlanUtils::untaggedPortList "" +} + + +namespace eval vlanUtils {} { + vlanUtilsSetDefault +} + + +######################################################################## +# Procedure: vlanUtils::setPortTagged +# +# Description: Sets a boolean indicated whether this port is a vlan-tagged port +# +# Arguments: +# chassis - The number of the chassis +# card - The number of the card +# port - The number of the port +# +# Returns: TCL_OK on completion +######################################################################## +proc vlanUtils::setPortTagged {chassis card port} \ +{ + variable untaggedPortList + + set retCode $::TCL_OK + + if {[catch {lsearch $untaggedPortList [list $chassis $card $port]} found]} { + set found -1 + } + + if {$found >= 0} { + set untaggedPortList [lreplace $untaggedPortList $found $found] + } + + return $retCode +} + + +######################################################################## +# Procedure: vlanUtils::setTagged +# +# Description: Sets a boolean indicated whether the ports in this list are vlan-tagged ports +# +# Arguments: portList - A list of ports to tag. A port identifier is of the list form: chassis card port. +# +# Returns: TCL_OK on success and TCL_ERROR on failure +######################################################################## +proc vlanUtils::setTagged {portList} \ +{ + set retCode $::TCL_OK + + foreach taggedPort $portList { + scan $taggedPort "%d %d %d" c l p + + if {[setPortTagged $c $l $p]} { + set retCode $::TCL_ERROR + break + } + } + + return $retCode +} + + +######################################################################## +# Procedure: vlanUtils::setPortUntagged +# +# Description: Sets a boolean indicated whether this port is a vlan-tagged port +# +# Arguments: +# chassis - The number of the chassis for the port +# card - The number of the card for the port +# port - The number of the port +# +# Returns: TCL_OK on success +######################################################################## +proc vlanUtils::setPortUntagged {chassis card port} \ +{ + variable untaggedPortList + + set retCode $::TCL_OK + + if {[catch {lsearch $untaggedPortList [list $chassis $card $port]} found]} { + set found -1 + } + + if {$found < 0} { + lappend untaggedPortList [list $chassis $card $port] + } + + return $retCode +} + + +######################################################################## +# Procedure: vlanUtils::setUntagged +# +# Description: Sets a boolean indicated whether the ports in this list are NOT vlan-tagged ports +# +# Arguments: portList - A list of ports to tag. A port identifier is of the list form: chassis card port. +# +# Returns: TCL_OK on success and TCL_ERROR on failure +######################################################################## +proc vlanUtils::setUntagged {portList} \ +{ + set retCode $::TCL_OK + + foreach taggedPort $portList { + scan $taggedPort "%d %d %d" c l p + + if {[setPortUntagged $c $l $p]} { + set retCode $::TCL_ERROR + break + } + } + + return $retCode +} + + +######################################################################## +# Procedure: vlanUtils::isPortTagged +# +# Description: Returns a boolean indicated whether this port is a vlan-tagged port +# +# Arguments: +# chassis - The number of the chassis for the port +# card - The number of the card for the port +# port - The number of the port +# +# Returns: true if the port is tagged and false if it is not. +######################################################################## +proc vlanUtils::isPortTagged {chassis card port} \ +{ + variable untaggedPortList + + if {[catch {lsearch $untaggedPortList [list $chassis $card $port]} found]} { + set tagged $::true + } else { + + if {$found < 0} { + set tagged $::true + } else { + set tagged $::false + } + } + + return $tagged +} + + +######################################################################## +# Procedure: emptyUntaggedPortList +# +# Description: Reset the untagged port list to a empty list. +# +# Arguments: +# +# Returns: +######################################################################## +proc vlanUtils::emptyUntaggedPortList {} \ +{ + variable untaggedPortList + + set untaggedPortList "" +} + + +######################################################################## +# Procedure: setPortTagged +# +# Description: Sets a boolean indicated whether this port is a vlan-tagged port +# +# Arguments: +# chassis - The number of the chassis +# card - The number of the card +# port - The number of the port +# +# Returns: TCL_OK on completion +######################################################################## +proc setPortTagged {chassis card port} \ +{ + return [vlanUtils::setPortTagged $chassis $card $port] +} + + +######################################################################## +# Procedure: setTagged +# +# Description: Sets a boolean indicated whether the ports in this list are vlan-tagged ports +# +# Arguments: portList - A list of ports to tag. A port identifier is of the list form: chassis card port. +# +# Returns: TCL_OK on success and TCL_ERROR on failure +######################################################################## +proc setTagged {portList} \ +{ + return [vlanUtils::setTagged $portList] +} + + +######################################################################## +# Procedure: setPortUntagged +# +# Description: Sets a boolean indicated whether this port is a vlan-tagged port +# +# Arguments: +# chassis - The number of the chassis for the port +# card - The number of the card for the port +# port - The number of the port +# +# Returns: TCL_OK on success +######################################################################## +proc setPortUntagged {chassis card port} \ +{ + return [vlanUtils::setPortUntagged $chassis $card $port] +} + + +######################################################################## +# Procedure: setUntagged +# +# Description: Sets a boolean indicated whether the ports in this list are NOT vlan-tagged ports +# +# Arguments: portList - A list of ports to tag. A port identifier is of the list form: chassis card port. +# +# Returns: TCL_OK on success and TCL_ERROR on failure +######################################################################## +proc setUntagged {portList} \ +{ + return [vlanUtils::setUntagged $portList] +} + + +######################################################################## +# Procedure: isPortTagged +# +# Description: Returns a boolean indicated whether this port is a vlan-tagged port +# +# Arguments: +# chassis - The number of the chassis for the port +# card - The number of the card for the port +# port - The number of the port +# +# Returns: true if the port is tagged and false if it is not. +######################################################################## +proc isPortTagged {chassis card port} \ +{ + return [vlanUtils::isPortTagged $chassis $card $port] +} + + +######################################################################## +# Procedure: getUntaggedPortList +# +# Description: Returns the list of untagged ports +# +# Arguments: +# +# Returns: The list of untagged ports. +######################################################################## +proc getUntaggedPortList {} \ +{ + return $::vlanUtils::untaggedPortList +} + + +######################################################################## +# Procedure: emptyUntaggedPortList +# +# Description: Reset the untagged port list to a empty list. +# +# Arguments: +# +# Returns: +######################################################################## +proc emptyUntaggedPortList {} \ +{ + vlanUtils::emptyUntaggedPortList +} + diff --git a/dep/tclclient/ixTcl1.0/Generic/xmlUtils.tcl b/dep/tclclient/ixTcl1.0/Generic/xmlUtils.tcl new file mode 100644 index 00000000..3fa6463b --- /dev/null +++ b/dep/tclclient/ixTcl1.0/Generic/xmlUtils.tcl @@ -0,0 +1,241 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: xmlUtils.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 3-7-2007 DS Genesis +# +# Description: Simple XML parser. +# +# credit to Keith Vetter, March 2004 +# http://wiki.tcl.tk/11020 +# +# Modified to work with both tcl8.3 & tcl8.4. +# +################################################################################## + + namespace eval ::ixXML { variable XML "" loc 0} + + proc ::ixXML::Init {xmlData} { + variable XML + variable loc + + set XML [string trim $xmlData]; + regsub -all {} $XML {} XML ;# Remove all comments + set loc 0 + } + + # Returns {XML|TXT|EOF|PI value attributes START|END|EMPTY} + proc ::ixXML::NextToken {{peek 0}} { + variable XML + variable loc + + set n [regexp -start $loc -indices {(.*?)\s*?<(/?)(.*?)(/?)>} \ + $XML all txt stok tok etok] + if {! $n} {return [list EOF]} + foreach {all0 all1} $all {txt0 txt1} $txt \ + {stok0 stok1} $stok {tok0 tok1} $tok {etok0 etok1} $etok break + + if {$txt1 >= $txt0} { ;# Got text + set txt [string range $XML $txt0 $txt1] + if {! $peek} {set loc [expr {$txt1 + 1}]} + return [list TXT $txt] + } + + set token [string range $XML $tok0 $tok1] ;# Got something in brackets + if {! $peek} {set loc [expr {$all1 + 1}]} + if {[regexp {^!\[CDATA\[(.*)\]\]} $token => txt]} { ;# Is it CDATA stuff? + return [list TXT $txt] + } + + # Check for Processing Instruction + set type XML + if {[regexp {^\?(.*)\?$} $token => token]} { + set type PI + } + set attr "" + regexp {^(.*?)\s+(.*?)$} $token => token attr + + set etype START ;# Entity type + if {$etok0 <= $etok1} { + if {$stok0 <= $stok1} { set token "/$token"} ;# Bad XML + set etype EMPTY + } elseif {$stok0 <= $stok1} { + set etype END + } + return [list $type $token $attr $etype] + } + # ::ixXML::IsWellFormed + # checks if the XML is well-formed )http://www.w3.org/TR/1998/REC-xml-19980210) + # + # Returns "" if well-formed, error message otherwise + # missing: + # characters: doesn't check valid extended characters + # attributes: doesn't check anything: quotes, equals, unique, etc. + # text stuff: references, entities, parameters, etc. + # doctype internal stuff + # + proc ::ixXML::IsWellFormed {} { + set result [::ixXML::_IsWellFormed] + set ::ixXML::loc 0 + return $result + } + ;proc ::ixXML::_IsWellFormed {} { + array set emsg { + XMLDECLFIRST "The XML declaration must come first" + MULTIDOCTYPE "Only one DOCTYPE is allowed" + INVALID "Invalid document structure" + MISMATCH "Ending tag '$val' doesn't match starting tag" + BADELEMENT "Bad element name '$val'" + EOD "Only processing instructions allowed at end of document" + BADNAME "Bad name '$val'" + BADPI "No processing instruction starts with 'xml'" + } + + # [1] document ::= prolog element Misc* + # [22] prolog ::= XMLDecl? Misc* (doctypedecl Misc*)? + # [27] Misc ::= Comment | PI | S + # [28] doctypedecl ::= + # [16] PI ::= + set seen 0 ;# 1 xml, 2 pi, 4 doctype + while {1} { + foreach {type val attr etype} [::ixXML::NextToken] break + if {[string equal $type "PI"]} { + if {! [regexp {^[a-zA-Z_:][a-zA-Z0-9.-_:\xB7]+$} $val]} { + return [subst $emsg(BADNAME)] + } + if {[string equal $val "xml"]} { ;# XMLDecl + if {$seen != 0} { return $emsg(XMLDECLFIRST) } + # TODO: check version number exist and only encoding and + # standalone attributes are allowed + incr seen ;# Mark as seen XMLDecl + continue + } + if {[string equal -nocase "xml" $val]} {return $emsg(BADPI)} + set seen [expr {$seen | 2}] ;# Mark as seen PI + continue + } elseif {[string equal $type "XML"] && [string equal $val "!DOCTYPE"]} { ;# Doctype + if {$seen & 4} { return $emsg(MULTIDOCTYPE) } + set seen [expr {$seen | 4}] + continue + } + break + } + + # [39] element ::= EmptyElemTag | STag content ETag + # [40] STag ::= < Name (S Attribute)* S? > + # [42] ETag ::= + # [43] content ::= CharData? ((element | Reference | CDSect | PI | Comment) CharData?)* + # [44] EmptyElemTag ::= < Name (S Attribute)* S? /> + # + + set stack {} + set first 1 + while {1} { + if {! $first} { ;# Skip first time in + foreach {type val attr etype} [::ixXML::NextToken] break + } else { + if {![string equal $type "XML"] && ![string equal $type "EOF"]} { return $emsg(INVALID) } + set first 0 + } + + if {[string equal $type "EOF"]} break + ;# TODO: check attributes: quotes, equals and unique + + if {[string equal $type "TXT"]} continue + if {! [regexp {^[a-zA-Z_:][a-zA-Z0-9.-_:\xB7]+$} $val]} { + return [subst $emsg(BADNAME)] + } + + if {[string equal $type "PI"]} { + if {[string equal -nocase xml $val]} { return $emsg(BADPI) } + continue + } + if {[string equal $etype "START"]} { ;# Starting tag + lappend stack $val + } elseif {[string equal $etype "END"]} { ;# + if {![string equal $val [lindex $stack end]]} { return [subst $emsg(MISMATCH)] } + set stack [lrange $stack 0 end-1] + if {[llength $stack] == 0} break ;# Empty stack + } elseif {[string equal $etype "EMPTY"]} { ;# + } + } + + # End-of-Document can only contain processing instructions + while {1} { + foreach {type val attr etype} [::ixXML::NextToken] break + if {[string equal $type "EOF"]} break + if {[string equal $type "PI"]} { + if {[string equal -nocase xml $val]} { return $emsg(BADPI) } + continue + } + return $emsg(EOD) + } + return "" + } + + ################################################################ + # + # Demo code + # + #set xml { + # + # + # + # + # Geocache + # http://www.geocaching.com/seek/cache_details.aspx?wp=GCGPXK + # + # + # + # Geocache + # http://www.geocaching.com/seek/cache_details.aspx?wp=GC19DF + # + # + #} +# +# ::ixXML::Init $xml +# set wellFormed [::ixXML::IsWellFormed] +# if {![string equal $wellFormed ""]} { +# puts "The xml is not well-formed: $wellFormed" +# } else { +# puts "The xml is well-formed" +# while {1} { +# foreach {type val attr etype} [::ixXML::NextToken] break +# puts "looking at: $type '$val' '$attr' '$etype'" +# if {$type == "EOF"} break +# } +# } \ No newline at end of file diff --git a/dep/tclclient/ixTcl1.0/ixInit.tcl b/dep/tclclient/ixTcl1.0/ixInit.tcl new file mode 100644 index 00000000..6953aa5b --- /dev/null +++ b/dep/tclclient/ixTcl1.0/ixInit.tcl @@ -0,0 +1,65 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: ixInit.tcl +# +# NOTE: This file should be sourced from the ixTcl1.0 directory structure +# +# Copyright © IXIA. +# All Rights Reserved. +# +############################################################################################# + +# Set the initial pattern to nothing +set newPatterns {} + +# Get the current directory +set dir [pwd] + +# Get all items in the directory +foreach fileItem1 [glob -nocomplain *] { + + # We only are concerned with directories + if {[file isdirectory $fileItem1]} { + lappend newPatterns [file join $fileItem1 "*.tcl"] + + foreach fileItem2 [glob -nocomplain $fileItem1/*] { + if {[file isdirectory $fileItem2]} { + lappend newPatterns [file join $fileItem2 "*.tcl"] + } + } + } +} + +eval auto_mkindex . $newPatterns + + diff --git a/dep/tclclient/ixTcl1.0/ixTclHal.tcl b/dep/tclclient/ixTcl1.0/ixTclHal.tcl new file mode 100644 index 00000000..72ad01ca --- /dev/null +++ b/dep/tclclient/ixTcl1.0/ixTclHal.tcl @@ -0,0 +1,192 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: ixTclHal.tcl +# +# Package initialization file +# +# This file is executed when you use "package require IxTclHal" to +# load the IxTclHal library package. It sets up the IXTCLHAL_LIBRARY +# environment variable to point to the directory where the package +# resides. +# +# If the package is being loaded from a multi-user template script, then continue +# otherwise return rightaway +# +# Copyright © IXIA. +# All Rights Reserved. +# +############################################################################################# + +## basically this is a keeping room for all the method pointer stuff + ixTclHal package req procs +namespace eval ixTclHal { + variable noArgList + variable pointerList + variable protocolList + variable commandList + + variable cleanUpDone 0 + variable nonDeprecatedCommands [list] + + proc update {MyList} \ + { + upvar $MyList myList + + variable noArgList + variable pointerList + variable protocolList + variable commandList + + set myList [join [list $noArgList $pointerList $protocolList $commandList]] + } + + proc createCommand {object command args} \ + { + # make sure the command doesn't already exist so that we don't step on it!! + if {[llength [info commands ::$command]] == 0} { + validateArgs $args + eval [format "proc ::%s {args} \ + { \ + if {\[%s %s [join $args]\] == \"\"} { \ + puts \"Error instantiating %s object!\"; \ + return 1; \ + }; \ + return \[eval %s \$args]; \ + }" \ + $command $object $command $object $command] + } + } + + proc createCommandPtr {object command args} \ + { + # make sure the command doesn't already exist so that we don't step on it!! + if {[llength [info commands ::$command]] == 0} { + set ptrCmd [format "$command%s" Ptr] + validateArgs $args + eval [format "proc ::%s {args} \ + { \ + set ::%s \[%s %s [join $args]\]; \ + if {\$::%s == \"\"} { \ + puts \"Error instantiating %s object!\"; \ + return 1; \ + }; \ + return \[eval %s \$args]; \ + }" \ + $command $ptrCmd $object $command $ptrCmd $object $command $command] + } + } + + # this one is scary; it adds a line of code to the created proc right before the return + # so that we can setup pointers in an ixTclHal object + proc updateCommand {command args} \ + { + set commandProc [string trim [info body $command]] + set lastcmd [string range $commandProc [string last return $commandProc] end] + + eval [format "proc ::%s {args} \ + { \ + %s \ + }" \ + $command [concat [string range $commandProc 0 [expr [string last return $commandProc]-1]] [format "%s;" [join $args]] $lastcmd] ] + } + + proc validateArgs {args} \ + { + set args [join $args] + if {$args != ""} { + foreach cmdPtr $args { + regsub -all {[$]} $cmdPtr "" cmdPtr + if {![info exists $cmdPtr]} { + regsub -all {[;:"$']} $cmdPtr "" cmd + regsub "Ptr$" $cmd "" cmd + catch {$cmd} + } + } + } + } + + proc cleanUpDone {} {variable cleanUpDone; set cleanUpDone 1} + proc isCleanUpDone {} {variable cleanUpDone; return $cleanUpDone} +} + + +set currDir [file dirname [info script]] + +source [file join $currDir ixTclSetup.tcl] +source [file join $currDir ixTclHalSetup.tcl] + +package provide IxTclHal 6.20 + + +# +# Source the user configuration file located in the following directory. +# +# IXIA_DIR/TclScripts/lib/ixTcl1.0 +# +# IXIA_DIR is the name of the directory where IXIA software is installed. +# The directory UserFiles should be created while IXIA software is being +# installed. +# +# The name of the file has to be userProfile.tcl. +# +catch { + source [file join $currDir userProfile.tcl] +} + +if {[catch {source [file join $currDir ixScriptmatePackageControl.tcl]}] } { + catch {package require Scriptmate} +} + +# Recent IxNetwork Tcl pacakges allow the user to specify a version. Try that first: +if {[info exists ::IxTclHal_LoadIxTclProtocolVersion]} { + # If the user requests "none", simply do not load the package at all + if {$::IxTclHal_LoadIxTclProtocolVersion != "none"} { + # Attempt to require the specified version of IxTclProtocol package + if {[catch {package require IxTclProtocol $::IxTclHal_LoadIxTclProtocolVersion} IxTclProtocol_result} { + # Display a helpful message on failure + set savInfo $::errorInfo + if {[catch {package versions IxTclProtocol} versionList]} { + set versionList [list] + } + set errMsg "Couldn't load IxTclProtocol version \"$::IxTclHal_LoadIxTclProtocolVersion\" ('set ::IxTclHal_LoadIxTclProtocolVersion \"none\"', or any of: $versionList, or unset it)" + error $errMsg $savInfo + } + } +} else { + # If the user did not specify a version, try to load the first IxTclProtocol we can find: + if {[catch {package require IxTclProtocol}] } { + # Finally, try the legacy location: + catch {source [file join $currDir ixTclProtocolPackageControl.tcl] } + # If nothing is found, that's ok, IxNetwork is just not installed + } +} + diff --git a/dep/tclclient/ixTcl1.0/ixTclHalSetup.tcl b/dep/tclclient/ixTcl1.0/ixTclHalSetup.tcl new file mode 100644 index 00000000..84eaeb95 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/ixTclHalSetup.tcl @@ -0,0 +1,400 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: ixTclHalSetup.tcl +# +# Package initialization file +# +# This file is executed when you use "package require IxTclHal" to +# load the IxTclHal library package. It sets up the TclHal related +# objects and commands. This file will be called by ixtclhal.tcl file +# on the server side or whereever the TclHal.dll file is installed. +# +# +# Copyright © IXIA. +# All Rights Reserved. +# +############################################################################################# + +if [isWindows] { + load ixTclHal.dll + + # This is done for the applications that don't know the location of the mpexpr10.dll, itm + set mpexprPath "$env(IXTCLHAL_LIBRARY)/../../bin" + if { [catch {load Mpexpr10.dll}] } { + catch {load $mpexprPath/Mpexpr10.dll} + } + + + ############################ OBJECT INSTANTIATION ########################## + + foreach procName $ixTclHal::noArgList { + set tclCmd [format "TCL%s%s" [string toupper [string index $procName 0]] [string range $procName 1 end]] + ixTclHal::createCommand $tclCmd $procName + } + + ixTclHal::createCommand TCLStatistics stat + ixTclHal::createCommand TCLUtils ixUtils + ixTclHal::createCommand TCLVsrStatistics vsrStat + + foreach procName $ixTclHal::pointerList { + set tclCmd [format "TCL%s%s" [string toupper [string index $procName 0]] [string range $procName 1 end]] + ixTclHal::createCommandPtr $tclCmd $procName + } + + foreach procName $ixTclHal::protocolList { + set tclCmd [format "TCL%s%s" [string toupper [string index $procName 0]] [string range $procName 1 end]] + ixTclHal::createCommand $tclCmd $procName \$::portPtr + } + ixTclHal::createCommandPtr TCLProtocolOffset protocolOffset \$::portPtr + ixTclHal::createCommandPtr TCLStream stream \$::portPtr \$::protocolPtr \$::protocolOffsetPtr + ixTclHal::createCommandPtr TCLIgmpAddressTable igmpAddressTable \$::igmpAddressTableItemPtr + ixTclHal::createCommandPtr TCLRip rip \$::portPtr + ixTclHal::createCommandPtr TCLMpls mpls \$::portPtr + ixTclHal::createCommand TCLAtmHeader atmHeader \$::portPtr \$::atmHeaderCounterPtr + ixTclHal::createCommandPtr TCLSrpDiscovery srpDiscovery \$::portPtr \$::srpMacBindingPtr + ixTclHal::createCommandPtr TCLSrp srpHeader \$::portPtr \$::protocolPtr + ixTclHal::createCommand TCLIgmp igmp \$::portPtr \$::igmpGroupRecordPtr + + ixTclHal::createCommandPtr TCLUdf udf \$::streamPtr + ixTclHal::createCommand TCLTableUdf tableUdf \$::portPtr \$::tableUdfColumnPtr + ixTclHal::createCommand TCLSequenceNumberUdf sequenceNumberUdf \$::portPtr + ixTclHal::createCommand TCLPacketGroup packetGroup \$::udfPtr \$::packetGroupThresholdListPtr + ixTclHal::createCommand TCLPacketLengthInsertion packetLengthInsertion \$::streamPtr + + ixTclHal::createCommand TCLIpAddressTable ipAddressTable \$::ipAddressTableItemPtr + ixTclHal::createCommand TCLArpServer arpServer \$::arpAddressTableEntryPtr + ixTclHal::createCommand TCLRipRoute ripRoute \$::ripPtr + ixTclHal::createCommand TCLMplsLabel mplsLabel \$::mplsPtr + + ixTclHal::createCommandPtr TCLRprRingControl rprRingControl \$::portPtr \$::protocolPtr + ixTclHal::createCommandPtr TCLRprTlvIndividualBandwidth rprTlvIndividualBandwidth \$::rprTlvBandwidthPairPtr + ixTclHal::createCommand TCLRprTopology rprTopology \$::portPtr \$::rprTlvIndividualBandwidthPtr \ + \$::rprTlvStationNamePtr \ + \$::rprTlvNeighborAddressPtr \ + \$::rprTlvTotalBandwidthPtr \ + \$::rprTlvVendorSpecificPtr \ + \$::rprTlvWeightPtr + + + + ixTclHal::createCommandPtr TCLIpV6HopByHop ipV6HopByHop \$::ipV6OptionPAD1Ptr \ + \$::ipV6OptionPADNPtr \ + \$::ipV6OptionJumboPtr \ + \$::ipV6OptionRouterAlertPtr \ + \$::ipV6OptionBindingUpdatePtr \ + \$::ipV6OptionBindingAckPtr \ + \$::ipV6OptionBindingRequestPtr \ + \$::ipV6OptionMIpV6UniqueIdSubPtr \ + \$::ipV6OptionMIpV6AlternativeCoaSubPtr \ + \$::ipV6OptionUserDefinePtr + + ixTclHal::createCommandPtr TCLIpV6Destination ipV6Destination \$::ipV6OptionPAD1Ptr \ + \$::ipV6OptionPADNPtr \ + \$::ipV6OptionHomeAddressPtr + + + ixTclHal::createCommand TCLIpV6 ipV6 \$::portPtr \$::ipV6RoutingPtr \ + \$::ipV6FragmentPtr \ + \$::ipV6DestinationPtr \ + \$::ipV6AuthenticationPtr \ + \$::ipV6HopByHopPtr + ixTclHal::createCommandPtr TCLCustomOrderedSet customOrderedSet + ixTclHal::createCommand TCLLinkFaultSignaling linkFaultSignaling \$::customOrderedSetPtr + ixTclHal::createCommandPtr TCLPacketGroupStats packetGroupStats \$::latencyBinPtr + ixTclHal::createCommandPtr TCLGfp gfp \$::portPtr + + ixTclHal::createCommand TCLAtmOam atmOam \$::portPtr \$::atmOamAisPtr \ + \$::atmOamRdiPtr \ + \$::atmOamFaultManagementCCPtr \ + \$::atmOamFaultManagementLBPtr \ + \$::atmOamActDeactPtr + + ixTclHal::createCommandPtr TCLAtmOamTrace atmOamTrace \$::portPtr + + ixTclHal::createCommandPtr TCLVlan vlan \$::portPtr + ixTclHal::createCommand TCLStackedVlan stackedVlan \$::portPtr \$::vlanPtr + + + ixTclHal::createCommandPtr TCLMmd mmd \$::mmdRegisterPtr + ixTclHal::createCommandPtr TCLMiiae miiae \$::mmdPtr + + ixTclHal::createCommandPtr TCLDhcpV4Properties dhcpV4Properties \$::dhcpV4TlvPtr + ixTclHal::createCommandPtr TCLDhcpV4DiscoveredInfo dhcpV4DiscoveredInfo \$::dhcpV4TlvPtr + + # dhcpV6Tlv command is exactly the same as dhcpV4Tlv. We use the same underlying C++ object. + ixTclHal::createCommandPtr TCLDhcpV4Tlv dhcpV6Tlv + ixTclHal::createCommandPtr TCLDhcpV6Properties dhcpV6Properties \$::dhcpV6TlvPtr + ixTclHal::createCommandPtr TCLDhcpV6DiscoveredInfo dhcpV6DiscoveredInfo \$::dhcpV6TlvPtr + + # fipTlv command is exactly the same as dhcpV4Tlv. We use the same underlying C++ object. + ixTclHal::createCommandPtr TCLDhcpV4Tlv fipTlv + ixTclHal::createCommandPtr TCLFcoeProperties fcoeProperties \$::fipTlvPtr \ + \$::fcoePlogiPtr \ + \$::fcoeNameServerPtr \ + \$::fcNameServerQueryPtr + + ixTclHal::createCommandPtr TCLFcProperties fcProperties \$::fcPlogiPtr \ + \$::fcNameServerPtr \ + \$::fcNameServerQueryPtr + + ixTclHal::createCommandPtr TCLNpivProperties npivProperties \$::fcoePlogiPtr \ + \$::fcoeNameServerPtr \ + \$::fcNameServerPtr \ + \$::fcNameServerQueryPtr + + ixTclHal::createCommandPtr TCLDcbxProperties dcbxProperties \$::lldpPortIdPtr \ + \$::dcbxPriorityGroupFeaturePtr \ + \$::dcbxPfcFeaturePtr \ + \$::dcbxFcoeFeaturePtr \ + \$::dcbxLogicalLinkFeaturePtr \ + \$::dcbxCustomFeaturePtr \ + \$::dcbxIscsiFeaturePtr + + ixTclHal::createCommandPtr TCLInterfaceEntry interfaceEntry \$::interfaceIpV4Ptr \ + \$::interfaceIpV6Ptr \ + \$::dhcpV4PropertiesPtr \ + \$::dhcpV6PropertiesPtr \ + \$::fcoePropertiesPtr \ + \$::npivPropertiesPtr \ + \$::ptpPropertiesPtr \ + \$::dcbxPropertiesPtr \ + \$::fcPropertiesPtr + + ixTclHal::createCommandPtr TCLDiscoveredNeighbor discoveredNeighbor \$::discoveredAddressPtr + ixTclHal::createCommandPtr TCLDiscoveredList discoveredList \$::discoveredNeighborPtr \$::discoveredAddressPtr + + ixTclHal::createCommandPtr TCLDcbxDiscoveredInfo dcbxDiscoveredInfo \$::dcbxPriorityGroupFeaturePtr \ + \$::dcbxPfcFeaturePtr \ + \$::dcbxFcoeFeaturePtr \ + \$::dcbxLogicalLinkFeaturePtr \ + \$::dcbxCustomFeaturePtr \ + \$::dcbxControlTlvPtr \ + \$::dcbxIscsiFeaturePtr + + ixTclHal::createCommand TCLInterfaceTable interfaceTable \$::interfaceEntryPtr \ + \$::discoveredListPtr \ + \$::dhcpV4DiscoveredInfoPtr \ + \$::dhcpV6DiscoveredInfoPtr \ + \$::fcoeDiscoveredInfoPtr \ + \$::ptpDiscoveredInfoPtr \ + \$::dcbxDiscoveredInfoPtr + + ixTclHal::createCommand TCLSonetCircuitList sonetCircuitList \$::sonetCircuitPtr + + ixTclHal::createCommand TCLMacSecTx macSecTx \$::macSecChannelPtr + ixTclHal::createCommand TCLMacSecRx macSecRx \$::macSecChannelPtr + ixTclHal::createCommand TCLMacSecTag macSecTag \$::portPtr + + ixTclHal::createCommandPtr TCLOamInformation oamInformation \$::oamLocalInformationTlvPtr \ + \$::oamRemoteInformationTlvPtr \ + \$::oamOrganizationSpecificTlvPtr + + ixTclHal::createCommandPtr TCLOamEventNotification oamEventNotification \$::oamSymbolPeriodTlvPtr \ + \$::oamFrameTlvPtr \ + \$::oamFramePeriodTlvPtr \ + \$::oamSummaryTlvPtr \ + \$::oamEventOrgTlvPtr + + ixTclHal::createCommandPtr TCLOamVariableRequest oamVariableRequest \$::oamVariableRequestTlvPtr + ixTclHal::createCommandPtr TCLOamVariableResponse oamVariableResponse \$::oamVariableResponseTlvPtr + + + ixTclHal::createCommand TCLOamHeader oamHeader \$::portPtr \$::oamInformationPtr \ + \$::oamEventNotificationPtr \ + \$::oamVariableRequestPtr \ + \$::oamVariableResponsePtr \ + \$::oamLoopbackControlPtr \ + \$::oamOrganizationSpecificPtr + + ixTclHal::createCommand TCLCiscoMetaData ciscoMetaData \$::portPtr \$::ciscoMetaDataSourceGroupTagPtr \ + \$::ciscoMetaDataCustomOptionPtr + + ixTclHal::createCommand TCLConditionalStats conditionalStats \$::conditionalTablePtr + + ixTclHal::createCommand TCLPtp ptp \$::portPtr \$::ptpAnnouncePtr \ + \$::ptpDelayRequestPtr \ + \$::ptpSyncPtr \ + \$::ptpFollowUpPtr \ + \$::ptpDelayResponsePtr + + + ixTclHal::createCommandPtr TCLIcmpV6NeighborDiscovery icmpV6NeighborDiscovery \$::icmpV6OptionLinkLayerSourcePtr \ + \$::icmpV6OptionLinkLayerDestinationPtr \ + \$::icmpV6OptionPrefixInformationPtr \ + \$::icmpV6OptionRedirectedHeaderPtr \ + \$::icmpV6OptionMaxTransmissionUnitPtr \ + \$::icmpV6OptionUserDefinePtr + + + ixTclHal::createCommand TCLIcmpV6 icmpV6 \$::portPtr \$::icmpV6ErrorPtr \ + \$::icmpV6InformationalPtr \ + \$::icmpV6MulticastListenerPtr \ + \$::icmpV6NeighborDiscoveryPtr \ + \$::icmpV6UserDefinePtr + + ixTclHal::createCommand TCLCommonTransport commonTransport \$::portPtr \ + \$::ctPreamblePtr \ + \$::ctGetAllNextRequestPtr \ + \$::ctGetAllNextAcceptPtr \ + \$::ctGetPortNameRequestPtr \ + \$::ctGetPortNameAcceptPtr \ + \$::ctGetNodeNameRequestPtr \ + \$::ctGetNodeNameAcceptPtr \ + \$::ctGetFC4TypeRequestPtr \ + \$::ctRegisterNodeNameRequestPtr + + ixTclHal::createCommandPtr TCLFcpCommandStatus fcpCommandStatus \$::fcpSnsInfoPtr + + ixTclHal::createCommand TCLFcp fcp \$::portPtr \ + \$::fcpCommandStatusPtr \ + \$::fcpUnsolicitedCommandPtr \ + \$::fcpDataDescriptorPtr \ + \$::fcpScsiReadPtr \ + \$::fcpScsiWritePtr \ + \$::fcpScsiInquiryPtr + + ixTclHal::createCommand TCLBasicLinkServices basicLinkServices \$::portPtr + + ixTclHal::createCommand TCLExtendedLinkServices extendedLinkServices \$::portPtr \ + \$::elsFlogiPtr \ + \$::elsPlogiPtr \ + \$::elsFdiscPtr \ + \$::elsLsAccPtr \ + \$::elsLogoPtr \ + \$::elsScrPtr \ + \$::elsLsRjtPtr \ + \$::elsRscnPtr + + ixTclHal::createCommand TCLFcSOF fcSOF \$::portPtr + + ixTclHal::createCommand TCLFcEOF fcEOF \$::portPtr + + + ixTclHal::createCommandPtr TCLNetworkHeader networkHeader \$::iEEE48BitAddressSrcPtr \ + \$::iEEEExtendedSrcPtr \ + \$::locallyAssignedSrcPtr \ + \$::iEEERegisteredSrcPtr \ + \$::iEEERegisteredExtendedSrcPtr \ + \$::eUI64MappedSrcPtr \ + \$::iEEE48BitAddressDestPtr \ + \$::iEEEExtendedDestPtr \ + \$::locallyAssignedDestPtr \ + \$::iEEERegisteredDestPtr \ + \$::iEEERegisteredExtendedDestPtr \ + \$::eUI64MappedDestPtr + + ixTclHal::createCommand TCLFibreChannel fibreChannel \$::portPtr \ + \$::vftHeaderPtr \ + \$::ifrHeaderPtr \ + \$::encHeaderPtr \ + \$::espHeaderPtr \ + \$::associationHeaderPtr \ + \$::networkHeaderPtr + + ixTclHal::createCommandPtr TCLProtocolPad protocolPad \$::portPtr + + ixTclHal::createCommandPtr TCLHse40GQsfpLane hse40GQsfpLane + ixTclHal::createCommand TCLHse40GQsfp hse40GQsfp \$::hse40GQsfpLanePtr + + ixTclHal::createCommandPtr TCLHseCfpQsfpLane hseCfpQsfpLane + ixTclHal::createCommand TCLHseCfpQsfp hseCfpQsfp \$::hseCfpQsfpLanePtr + + # Enable logging of messages from SWIG + enableEvents true + + ################################################################################# + # Procedure: after + # + # NOTE: This command effective 'steps on' the original after command + # so that we have more control over what happens during the sleep + # process (ie., so that we can make some task switches) + # + # It is used *exactly* like the original after. + # + # This proc is here because we don't want to overload the 'after' command + # in unix, only windows. + # + # Argument(s): + # duration - time to sleep, in milliseconds + # + ################################################################################# + proc after {args} \ + { + set retCode "" + + set argc [llength $args] + + set duration [lindex $args 0] + if {[stringIsInteger $duration] && $argc == 1} { + ixSleep $duration + set retCode "" + } else { + catch {eval originalAfter $args} retCode + } + + set retCode [stringSubstitute $retCode originalAfter after] + + return $retCode + } + +} else { + catch {package req Mpexpr} + + proc enableEvents {flag} {} + logMsg "Tcl Client is running Ixia Software version: $env(IXIA_VERSION)" +} + + +# note that mpexpr is only req'd for 8.3 & below. +if {[info command mpexpr] == ""} { + # if we didn't get a real mpexpr AND we're not 8.4, that's ugly so throw an exception - otherwise just make it work + if {[info tclversion] <= 8.3} { + puts "Package req failed: Mpexpr package/file not found" + return -code error -errorinfo "Mpexpr package/file not found" + } else { + proc mpexpr {args} {return [eval expr $args]} + proc mpformat {args} {return [eval format $args]} + proc mpincr {args} \ + { + set Item [lindex $args 0] + upvar $Item item + set args [lrange $args 1 end] + return [eval incr item $args] + } + } +} + + +useProfile false + + diff --git a/dep/tclclient/ixTcl1.0/ixTclSetup.tcl b/dep/tclclient/ixTcl1.0/ixTclSetup.tcl new file mode 100644 index 00000000..de82f41b --- /dev/null +++ b/dep/tclclient/ixTcl1.0/ixTclSetup.tcl @@ -0,0 +1,244 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: ixTclSetup.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 10-29-2002 DHG Initial Release +# +# Description: Package initialization file. +# This file is executed when you use "package require IxTclHal" to +# load the IxTclHal library package. It sets up the Tcl-only variables +# and is called by the client and server side ixTclHal.tcl files. +# +# Copyright © IXIA. +# All Rights Reserved. +# +############################################################################################# + +set env(IXTCLHAL_LIBRARY) [file dirname [info script]] + +proc PromptIxia {} { + puts -nonewline "[pwd]> " +} + +# This flag will be set when anyone calls cleanUp proc +set cleanUpDone 0 + +# Not REQUIRED --- this is taken care of below..... source [file join $env(IXTCLHAL_LIBRARY) ixiarc.tcl] + +set genPath [file join $env(IXTCLHAL_LIBRARY) Generic] + +lappend auto_path $env(IXTCLHAL_LIBRARY) + +initializeDefineCommand +initializeDefineTest + +set interfaceFile [file join $genPath interface.tcl] +source $interfaceFile + +set constantsFile [file join $genPath constants.tcl] +source $constantsFile + + +# rename the system exit with our exit so that we can clean up before calling +# system exit for Windows only +if [isWindows] { + if {[info commands exit] != ""} { + if {[info commands exitOld] == ""} { + rename exit exitOld + } + } + + # NOTE: Need to redefine this exit here, AND NOT ANYWHERE ELSE, so that the system + # exit gets called when exitOld is called. + proc exit {{exitStat 0}} \ + { + cleanUp + exitOld $exitStat + } + # rename the system after with our after so that we can task switch for Windows only + if {[info commands after] != ""} { + if {[info commands originalAfter] == ""} { + rename after originalAfter + } + } +} + + +if {[isWindows] && [info commands tk] != ""} { + if {![regexp -nocase scriptmate [tk appname]]} { + console show + } +} + + +############################# GLOBAL VARIABLES ################################# +#### NOTE: DON'T FORGET TO ADD NEW ONES + +## these are the simple ones that don't have any ptr parameters... +set ixTclHal::noArgList { version session chassisChain chassis card qos streamRegion streamQueue streamQueueList \ + portGroup filter filterPallette statGroup statList capture captureBuffer \ + splitPacketGroup dataIntegrity tcpRoundTripFlow autoDetectInstrumentation \ + protocolServer igmpServer mii usb timeServer forcedCollisions collisionBackoff portCpu \ + ppp pppStatus sonet sonetError sonetOverhead atmPort dcc srpUsage \ + bert bertErrorGeneration bertUnframed xaui vsrError fecError opticalDigitalWrapper \ + ipV6Address logFile remoteConnection licenseManagement \ + atmStat atmFilter atmReassembly statWatch txRxPreamble streamExtractorFilter streamExtractorModifier \ + flexibleTimestamp pcpuCommandService gfpOverhead streamTransmitStats xfp lasi sfpPlus \ + poePoweredDevice poeSignalAcquisition poeAutoCalibration statAggregator \ + sonetCircuitProperties lcas prbsCapture oamPort oamStatus \ + txLane pcsLaneStatistics pcsLaneError fcPort} + +## these are the ones that we need simple pointers returned for other stuff... +set ixTclHal::pointerList { port protocol arpAddressTableEntry ipAddressTableItem igmpAddressTableItem \ + interfaceIpV4 interfaceIpV6 discoveredAddress atmHeaderCounter \ + customOrderedSet srpMacBinding mmdRegister latencyBin \ + ipV6Authentication ipV6Routing ipV6Fragment \ + rprTlvBandwidthPair rprTlvWeight rprTlvVendorSpecific \ + rprTlvTotalBandwidth rprTlvNeighborAddress rprTlvStationName \ + tableUdfColumn igmpGroupRecord dhcpV4Tlv \ + atmOamAis atmOamRdi atmOamFaultManagementCC atmOamFaultManagementLB atmOamActDeact \ + ipV6OptionPAD1 ipV6OptionPADN ipV6OptionJumbo ipV6OptionRouterAlert ipV6OptionBindingUpdate \ + ipV6OptionBindingAck ipV6OptionHomeAddress ipV6OptionBindingRequest ipV6OptionMIpV6UniqueIdSub \ + ipV6OptionMIpV6AlternativeCoaSub ipV6OptionUserDefine sonetCircuit macSecChannel \ + oamLocalInformationTlv oamRemoteInformationTlv oamOrganizationSpecificTlv oamSymbolPeriodTlv \ + oamFrameTlv oamFramePeriodTlv oamSummaryTlv oamEventOrgTlv \ + oamVariableRequestTlv oamVariableResponseTlv oamLoopbackControl oamOrganizationSpecific \ + ciscoMetaDataSourceGroupTag ciscoMetaDataCustomOption conditionalTable \ + fcoeDiscoveredInfo fcoePlogi fcoeNameServer fcNameServerQuery ptpProperties ptpDiscoveredInfo\ + ptpAnnounce ptpDelayRequest ptpSync ptpFollowUp ptpDelayResponse packetGroupThresholdList \ + icmpV6OptionLinkLayerSource icmpV6OptionLinkLayerDestination icmpV6OptionPrefixInformation \ + icmpV6OptionRedirectedHeader icmpV6OptionMaxTransmissionUnit icmpV6OptionUserDefine \ + icmpV6Error icmpV6Informational icmpV6MulticastListener icmpV6UserDefine lldpPortId \ + dcbxPriorityGroupFeature dcbxPfcFeature dcbxFcoeFeature dcbxIscsiFeature dcbxLogicalLinkFeature \ + dcbxCustomFeature dcbxControlTlv ctPreamble ctGetAllNextRequest ctGetAllNextAccept \ + ctGetPortNameRequest ctGetPortNameAccept ctGetNodeNameRequest ctGetNodeNameAccept \ + ctGetFC4TypeRequest ctRegisterNodeNameRequest elsFlogi elsPlogi elsFdisc elsLsAcc elsLogo \ + elsScr elsLsRjt elsRscn fcpUnsolicitedCommand fcpDataDescriptor \ + fcpScsiRead fcpScsiWrite fcpScsiInquiry fcpSnsInfo \ + iEEE48BitAddressSrc iEEEExtendedSrc locallyAssignedSrc iEEERegisteredSrc iEEERegisteredExtendedSrc eUI64MappedSrc \ + iEEE48BitAddressDest iEEEExtendedDest locallyAssignedDest iEEERegisteredDest iEEERegisteredExtendedDest eUI64MappedDest \ + vftHeader ifrHeader encHeader espHeader associationHeader \ + fcPlogi fcNameServer hse40GQsfpLane hseCfpQsfpLane} + +## these are basically all the protocols that require a portPtr parameter for instantiation +set ixTclHal::protocolList { ip udp tcp ipx icmp arp dhcp gre macSecTag fcoe \ + srpArp srpIps frameRelay hdlc isl pauseControl weightedRandomFramesize \ + rprArp rprProtection rprFairness rprOam cdlPreamble dataCenterEncapsulation \ + basicLinkServices fcSOF fcEOF} + +## this initial list is all the complicated ones +set ixTclHal::commandList { stat stream udf mplsLabel mpls rip ripRoute ipAddressTable igmpAddressTable arpServer\ + interfaceTable interfaceEntry discoveredList discoveredNeighbor discoveredAddress \ + packetGroupStats tableUdf sequenceNumberUdf igmp atmOam atmOamTrace vlan stackedVlan protocolOffset \ + ipV6 ipV6HopByHop ipV6Destination rprRingControl rprTopology rprTlvIndividualBandwidth \ + miiae mmd srpHeader srpDiscovery atmHeader ixUtils linkFaultSignaling gfp \ + dhcpV4Properties dhcpV4DiscoveredInfo dhcpV6Properties dhcpV6DiscoveredInfo \ + sonetCircuitList packetGroup macSecTx macSecRx oamHeader ciscoMetaData ptp \ + oamInformation oamEventNotification oamVariableRequest oamVariableResponse conditionalStats \ + fcoeProperties npivProperties icmpV6 icmpV6NeighborDiscovery dcbxProperties dcbxDiscoveredInfo \ + fibreChannel networkHeader commonTransport extendedLinkServices fcpCommandStatus fcp fcProperties protocolPad \ + hse40GQsfp packetLengthInsertion hseCfpQsfp} + +ixTclHal::update ::halCommands +## These commands needs redefinition of the body during connecting to TCL server + +set ixTclHal::redefineCommandList [list chassis card port stream captureBuffer tableUdf] + +set ::halRedefineCommands [join [list $ixTclHal::redefineCommandList]] +## We need to append dhcpV6Tlv to halCommands, since it is instantiated as an TCLDhcpV4Tlv and can't be added to ixTclHal::pointerList +lappend ::halCommands dhcpV6Tlv + +## We need to append fipTlv to halCommands, since it is instantiated as an TCLDhcpV4Tlv and can't be added to ixTclHal::pointerList +lappend ::halCommands fipTlv + +set halFuncs { enableEvents clearAllMyOwnership } + +set globalArrays { ixgTrialArray ixgTrialCongArray ixgTrialUncongArray \ + one2oneArray one2manyArray many2oneArray many2manyArray } + +set ixProtocolList {ip udp ptp tcp ipx igmp icmp arp vlan dhcp mpls mplsLabel qos rip ripRoute \ + isl frameRelay ipV6 fcoe fibreChannel commonTransport fcp basicLinkServices extendedLinkServices fcSOF fcEOF } + +set ixStopTest 0 +set ixStopAction 0 + +############################ TCL FILES/DIR INITIALIZATION ########################## + +if [info exists env(IXIA_RESULTS_DIR)] { + #set RESULTS_DIR [file join $env(IXIA_RESULTS_DIR) Results] + set RESULTS_DIR $env(IXIA_RESULTS_DIR) +} else { + set RESULTS_DIR [file join [file dirname [file dirname $env(IXTCLHAL_LIBRARY)]] Results] + set env(IXIA_RESULTS_DIR) $RESULTS_DIR +} +if {![file exists $RESULTS_DIR]} { + file mkdir $RESULTS_DIR +} + +if [info exists env(IXIA_LOGS_DIR)] { + #set LOGS_DIR [file join $env(IXIA_LOGS_DIR) Logs] + set LOGS_DIR $env(IXIA_LOGS_DIR) +} else { + set LOGS_DIR [file join [file dirname [file dirname $env(IXTCLHAL_LIBRARY)]] Logs] + set env(IXIA_LOGS_DIR) $LOGS_DIR +} +if {![file exists $LOGS_DIR]} { + file mkdir $LOGS_DIR +} + +if {![info exists env(IXIA_SAMPLES_DIR)]} { + set env(IXIA_SAMPLES_DIR) [file dirname [file dirname $env(IXTCLHAL_LIBRARY)]] +} + +foreach initProc [info commands initCommand_zz_*] { + # puts $initProc + ${initProc} +} + +# Create dummy calls to the commands that were moved to the scriptmate package. This is for +# backwards compatibility but it not guaranteed to work unless the Scriptmate package is installed. +scriptmateBackwardsCompatibility::createAllCommands + +############### sample script environment initialization ############################ +global ixgJitterIndex +set ixgJitterIndex(averageLatency) 0 +set ixgJitterIndex(standardDeviation) 1 +set ixgJitterIndex(averageDeviation) 2 +set ixgJitterIndex(minLatency) 3 +set ixgJitterIndex(maxLatency) 4 + + diff --git a/dep/tclclient/ixTcl1.0/ixiaDCB.tcl b/dep/tclclient/ixTcl1.0/ixiaDCB.tcl new file mode 100644 index 00000000..8e2da3e3 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/ixiaDCB.tcl @@ -0,0 +1,588 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# Copyright (c) 2014, Intel Corporation All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. + +#Config Ixia. +proc configIxia {chasId rxcard rxport rxda txcard txport txda} { + set ::chasId $chasId + set ::rxcard $rxcard + set ::rxport $rxport + set ::txcard $txcard + set ::txport $txport + set ::rxPortList [list [list $chasId $rxcard $rxport]] + set ::txPortList [list [list $chasId $txcard $txport]] + set ::rxsa [format "00 00 %02x 00 %02x 00" [expr $rxcard-1] [expr $rxport-1]] + set ::rxda [string trim $rxda "'"] + set ::txsa [format "00 00 %02x 00 %02x 00" [expr $txcard-1] [expr $txport-1]] + set ::txda [string trim $txda "'"] + set ::rxGroup $rxport + set ::txGroup $txport +} + +#Config Port properties for DCB & PFC. +proc configPort {direction tc} { + #direction : rx tx + #tc : 4 8 + set ::direction $direction + set ::tc $tc + set ::[set direction]StreamNum 0 + if [array exists ::[set direction]StreamUPs] { + unset ::[set direction]StreamUPs + } + set da [set ::[set direction]da] + set card [set ::[set direction]card] + set port [set ::[set direction]port] + set portList [set ::[set direction]PortList] + + port setFactoryDefaults $::chasId $card $port + port config -speed 10000 + port config -duplex full + port config -flowControl true + port config -directedAddress $da + port config -autonegotiate false + port config -transmitMode portTxModeAdvancedScheduler + port config -portMode port10GigLanMode + port config -enableDataCenterMode true + port config -flowControlType ieee8021Qbb + port config -pfcResponseDelayEnabled 0 + port config -pfcResponseDelayQuanta 255 + + if [string equal "4" $tc] { + port config -dataCenterMode fourPriorityTrafficMapping + port config -pfcEnableValueListBitMatrix "{1 1} {1 2} {1 4} {1 8}" + } elseif [string equal "8" $tc] { + port config -dataCenterMode eightPriorityTrafficMapping + port config -pfcEnableValueListBitMatrix "{1 1} {1 2} {1 4} {1 8} {1 16} {1 32} {1 64} {1 128}" + } else { + errorMsg "TCs can only be 4 or 8." + return $::TCL_ERROR + } + if [port set $::chasId $card $port] { + errorMsg "Could not call port set $::chasId $card $port" + return $::TCL_ERROR + } + #Delete all streams from a port + port reset $::chasId $card $port + + filter setDefault + if [filter set $::chasId $card $port] { + errorMsg "Error calling filter set $::chasId $card $port" + return $::TCL_ERROR + } + + filterPallette setDefault + if [filterPallette set $::chasId $card $port] { + errorMsg "Error calling filterPallette set $::chasId $card $port" + return $::TCL_ERROR + } + + capture setDefault + capture config -fullAction wrap + capture config -captureMode captureContinuousMode + if [capture set $::chasId $card $port] { + errorMsg "Error calling capture set $::chasId $card $port" + return $::TCL_ERROR + } + + streamRegion setDefault + streamRegion config -gapControlMode streamGapControlFixed + if [streamRegion set $::chasId $card $port] { + errorMsg "Error calling streamRegion set $::chasId $card $port" + return $::TCL_ERROR + } + + #Write port properties to hardware + puts "Write port $port properties to hardware." + if [ixWritePortsToHardware portList] { + errorMsg "Could not write port $port properties to hardware." + return $::TCL_ERROR + } + after 500 +} + +#Configure all streams. +proc configStream {stream rate prio type {tc $::tc}} { + #stream : UDP PFC + #rate : 1.0 100.0 + #prio : 0 1 2 3 4 5 6 7 8 + #type : on off - vt + # : XOFF XON - pfc + #tc : 8 4 + set ::stream $stream + + stream setDefault + if {$rate == "0"} { + stream config -enable false + } else { + stream config -enable true + } + stream config -percentPacketRate $rate + stream config -rateMode streamRateModePercentRate + stream config -asyncIntEnable true + stream config -enableStatistic true + stream config -enableSuspend false + stream config -dma contPacket + stream config -priorityGroup "priorityGroup$prio" + + switch -exact -- $stream { + "UDP" { + #Configure UDP + stream config -sa $::txsa + stream config -da $::txda + stream config -framesize 68 + stream config -frameSizeMIN 68 + stream config -frameSizeMAX 68 + stream config -frameType "08 00" + + protocol setDefault + protocol config -name ipV4 + protocol config -ethernetType ethernetII + protocol config -enable802dot1qTag vlanSingle + + ip setDefault + ip config -checksum "7a c0" + ip config -sourceIpAddr "0.0.0.0" + ip config -sourceIpAddrRepeatCount 10 + ip config -sourceClass classA + ip config -destIpAddr "0.0.0.0" + ip config -destIpAddrRepeatCount 10 + ip config -destClass classA + ip config -destMacAddr "00 DE BB 00 00 02" + ip config -destDutIpAddr "0.0.0.0" + if [ip set $::chasId $::txcard $::txport] { + errorMsg "Error calling ip set $::chasId $::txcard $::txport" + return $::TCL_ERROR + } + + udp setDefault + udp config -sourcePort unassignedPort + udp config -destPort unassignedPort + udp config -checksum "b6 eb " + udp config -length 26 + if [udp set $::chasId $::txcard $::txport] { + errorMsg "Error calling udp set $::chasId $::txcard $::txport" + return $::TCL_ERROR + } + + vlan setDefault + if [string equal "on" $type] { + vlan config -mode vIncrement + vlan config -repeat [expr 128/$::tc] + } elseif [string equal "off" $type] { + vlan config -vlanID [expr int([expr 32 * [expr rand()]])] + } + vlan config -userPriority $prio + if [vlan set $::chasId $::txcard $::txport] { + errorMsg "Could not call vlan set $::chasId $::txcard $::txport" + return $::TCL_ERROR + } + set bool 1 + foreach item [array names ::txStreamUPs] { + if {$prio == $::txStreamUPs($item)} { + incr bool -1 + set id $item + break + } + } + if { $bool } { + incr ::txStreamNum + set ::txStreamUPs($::txStreamNum) $prio + set id $::txStreamNum + } + if [stream set $::chasId $::txcard $::txport $id] { + errorMsg "Could not call stream set $::chasId $::txcard $::txport $id" + return $::TCL_ERROR + } + if [stream write $::chasId $::txcard $::txport $id] { + errorMsg "Could not write stream $::chasId $::txcard $::txport $id" + } + } + "PFC" { + #Configure PFC + stream config -sa $::rxsa + stream config -da $::rxda + stream config -framesize 64 + stream config -frameSizeMIN 64 + stream config -frameSizeMAX 64 + stream config -frameType "88 08" + + protocol setDefault + protocol config -name pauseControl + protocol config -ethernetType ethernetII + + pauseControl setDefault + pauseControl config -da $::rxda + pauseControl config -pauseTime 255 + pauseControl config -pauseControlType ieee8021Qbb + pauseControl config -usePfcEnableValueList 0 + + pauseControl config -priorityEnableVector [format "%#04x" [expr 0x01<<$prio]] + for {set n 0; set paFrame ""; set pfcList ""} {$n < 8 } {incr n} { + if {$type == "XOFF" && $prio == $n} { + append paFrame "FF FF " + append pfcList "{1 65535} " + continue + } + append paFrame "00 00 " + append pfcList "{0 0} " + } + pauseControl config -pauseFrame [string trimright $paFrame] + pauseControl config -pfcEnableValueList [string trimright $pfcList] + if [pauseControl set $::chasId $::rxcard $::rxport] { + errorMsg "Error calling pauseControl set $::chasId $::rxcard $::rxport" + set retCode $::TCL_ERROR + } + set bool 1 + foreach item [array names ::rxStreamUPs] { + if {$prio == $::rxStreamUPs($item)} { + incr bool -1 + set id $item + break + } + } + if { $bool } { + incr ::rxStreamNum + set ::rxStreamUPs($::rxStreamNum) $prio + set id $::rxStreamNum + } + if [stream set $::chasId $::rxcard $::rxport $id] { + errorMsg "Could not call stream set $::chasId $::rxcard $::rxport $id" + return $::TCL_ERROR + } + if [stream write $::chasId $::rxcard $::rxport $id] { + errorMsg "Could not write stream $::chasId $::rxcard $::rxport $id" + return $::TCL_ERROR + } + } + default { + errorMsg "Configure streams error." + return $::TCL_ERROR + } + } + + after 100 +} + +#Get the statistics counter for all stats +proc getAllStats {direction {type None} {prio None}} { + set card [set ::[set direction]card] + set port [set ::[set direction]port] + if [stat get statAllStats $::chasId $card $port] { + puts "Could not read stats on port $port." + return $::TCL_ERROR + } + + set framesSent [stat cget -framesSent] + set framesRecv [stat cget -framesReceived] + for {set upId 0; set recvSum 0} {$upId < $::tc} {incr upId} { + set pfcUP$upId [stat cget -rxPausePriorityGroup[set upId]Frames] + incr recvSum [set pfcUP$upId] + } + + if [stat getRate statAllStats $::chasId $card $port] { + puts "Could not read stat rate on port $port." + return $::TCL_ERROR + } + set framesSentRate [stat cget -framesSent] + set framesRecvRate [stat cget -framesReceived] + + puts "+---------------------------------------------+" + puts "[ format "| %-44s|" "Read Statistics On [string toupper $direction 0 0] Port $port Of Ixia."]" + puts "+---------------------------------------------+" + puts "[format "| %-12s| %-14s| %-14s|" "Type" "Count" "Unit"]" + puts "+-------------+---------------+---------------+" + switch -exact -- $type { + "FramesSent" { + puts "[format "| %-12s| %-14s| %-14s|" "Frames Sent" $framesSent "frames"]" + puts "+---------------------------------------------+" + after 50 + return $framesSent + } + "RateSent" { + puts "[format "| %-12s| %-14s| %-14s|" "Rate Sent" $framesSentRate "packets/sec"]" + puts "+---------------------------------------------+" + return $framesSentRate + } + "FramesRecv" { + puts "[format "| %-12s| %-14s| %-14s|" "Frames Recv" $framesRecv "frames"]" + puts "+---------------------------------------------+" + after 50 + return $framesRecv + } + "RateRecv" { + puts "[format "| %-12s| %-14s| %-14s|" "Rate Recv" $framesRecvRate "packets/sec"]" + puts "+---------------------------------------------+" + after 50 + return $framesRecvRate + } + "PfcsRecv" { + puts "[format "| %-12s| %-14s| %-14s|" "Frames Sent" $framesSent "frames"]" + puts "+-------------+---------------+---------------+" + puts "[format "| %-12s| %-14s| %-14s|" "Frames Recv" $framesRecv "frames"]" + puts "+-------------+---------------+---------------+" + for {set upId 0} {$upId < $::tc} {incr upId} { + puts "[format "| %-12s| %-14s| %-14s|" "PFCs $upId Rcvd" [set pfcUP$upId] "frames"]" + puts "+-------------+---------------+---------------+" + } + puts "[format "| %-12s| %-14s| %-14s|" "Total Recv" $recvSum "frames"]" + puts "+---------------------------------------------+" + after 50 + if {$prio == "None"} { + return $recvSum + } else { + return [set pfcUP$prio] + } + } + default { + puts "[format "| %-12s| %-14s| %-14s|" "Frames Sent" $framesSent "frames"]" + puts "+-------------+---------------+---------------+" + puts "[format "| %-12s| %-14s| %-14s|" "Actual Rate" $framesSentRate "packets/sec"]" + puts "+-------------+---------------+---------------+" + puts "[format "| %-12s| %-14s| %-14s|" "Frames Recv" $framesRecv "frames"]" + puts "+-------------+---------------+---------------+" + puts "[format "| %-12s| %-14s| %-14s|" "Actual Rate" $framesRecvRate "packets/sec"]" + puts "+-------------+---------------+---------------+" + for {set upId 0} {$upId < $::tc} {incr upId} { + puts "[format "| %-12s| %-14s| %-14s|" "PFCs $upId Rcvd" [set pfcUP$upId] "frames"]" + puts "+-------------+---------------+---------------+" + } + puts "[format "| %-12s| %-14s| %-14s|" "Total Recv" $recvSum "frames"]" + puts "+---------------------------------------------+" + after 50 + } + } +} + +#Get per-stream transmit statistics on Tx port +proc getTxPerStreamStats {} { + set nums [streamTransmitStats cget -numGroups] + puts "+-------------------------------------------------------------+" + puts "[format "| %-60s|" "Read $nums Streams On Tx Port $::txport Of Ixia."]" + puts "+-------------------------------------------------------------+" + puts "[format "| %-7s| %-3s| %-14s| %-14s| %-14s|" "Stream" "UP" "FramesSent" "Define Rate" "Actual Rate"]" + #Get the statistics for each stream + set rateSum 0 + set deRateSum 0 + set sentSum 0 + set aveRate [streamTransmitStats cget -theoreticalAverageFrameRate] + for {set streamId 1} {$streamId <= $nums} {incr streamId} { + if [streamTransmitStats getGroup $streamId] { + errorMsg "Could not get group $streamId on port $::txport." + return $::TCL_ERROR + } + set frameRate [streamTransmitStats cget -frameRate] + set framesPSent [streamTransmitStats cget -framesSent] + stream get $::chasId $::txcard $::txport $streamId + set deRate [expr int([stream cget -fpsRate])] + + puts "+--------+----+---------------+---------------+---------------+" + puts "[format "| %-7s| %-3s| %-14s| %-14s| %-14s|" $streamId $::txStreamUPs($streamId) $framesPSent $deRate $frameRate]" + incr rateSum $frameRate + incr deRateSum $deRate + incr sentSum $framesPSent + } + puts "+-------------------------------------------------------------+" + puts "[format "| %-7s| %-3s| %-14s| %-14s| %-14s|" "Total" $nums $sentSum $deRateSum $rateSum]" + puts "+-------------+---------------+---------------+---------------+" + puts "[format "| %-12s| %-14s| %-14s| %-14s|" "Unit" "frames" "packets/sec" "packets/sec"]" + puts "+-------------------------------------------------------------+" + after 50 + } + +#Get the captured frames on on Tx port +proc getTxCaptureFrames {type {prio None}} { + puts "+--------------------------------------+" + puts "[format "| %-37s|" "Analyse frames captured on port $::txport."]" + if [capture get $::chasId $::txcard $::txport] { + puts "Could not get capture frames on port $::txport" + return $::TCL_ERROR + } + set numFrames [capture cget -nPackets] + puts "+--------------------------------------+" + puts "[format "| %-16s| %-19s|" "Frames Captured" $numFrames]" + if [expr $numFrames] { + for {set frameId 1} {$frameId <= $numFrames} {incr frameId} { + captureBuffer get $::chasId $::txcard $::txport $frameId $frameId + captureBuffer getframe 1 + set capFrame [captureBuffer cget -frame] + set etherType [lrange $capFrame 12 13] + + switch -exact -- $etherType { + "08 00" { + set types "IPv4 Frame" + if {$type == "UDP"} { + return 1 + } + } + "86 DD" { + set types "IPv6 Frame" + if {$type == "UDP"} { + return 1 + } + } + "81 00" { + set types "VLAN-tagged Frame" + if {$type == "UDP"} { + return 1 + } + } + "88 08" { + set types "MAC Control Frame" + set ctrlOpcode [lrange $capFrame 14 15] + switch -exact -- $ctrlOpcode { + "00 01" { + set fc "IEEE 802.3x PAUSE Frame" + if {$type == "FC-PAUSE"} { + return 1 + } + } + "01 01" { + set prioEnVector [lrange $capFrame 16 17] + if {[lrange $capFrame 17 17] == [format "%02x" [expr 0x01<<$prio]]} { + for {set timeId 0; set start 17; set ret ""} {$timeId < $::tc} {incr timeId} { + set Time$timeId [lrange $capFrame [incr start] [incr start]] + if {[set Time$timeId] == "00 00" && $ret != "XOFF"} { + set ret "XON" + } else { + set ret "XOFF" + } + } + if {$type == $ret} { + puts "+-----------------+--------------------+" + puts "[format "| %-16s| %-19s|" "Frames Analysed" $frameId]" + puts "+-----------------+--------------------+" + puts "[format "| %-16s| %-19s|" "Frames Type" $types]" + puts "+-----------------+--------------------+" + puts "[format "| %-16s| %-19s|" " " "PFC $type Frame"]" + puts "+-----------------+--------------------+" + puts "[format "| %-16s| %-19s|" "Control Opcode" $ctrlOpcode]" + puts "+-----------------+--------------------+" + puts "[format "| %-16s| %-19s|" "Priority Vector" $prioEnVector]" + puts "+-----------------+--------------------+" + puts "[format "| %-16s| %-19s|" " " $prio]" + for {set timeId 0; set start 17; set ret ""} {$timeId < $::tc} {incr timeId} { + puts "+-----------------+--------------------+" + puts "[format "| %-16s| %-19s|" "Time$timeId" [set Time$timeId]]" + } + puts "+--------------------------------------+" + after 50 + return 1 + } + } + } + } + } + default { puts "Unknown Frame" } + } + } + } + puts "+--------------------------------------+" + return 0 +} + +#Clear all statistic counters on Tx/Rx port +proc clearStats {direction} { + set portList [set ::[set direction]PortList] + if [ixClearStats portList] { + return $::TCL_ERROR + } + after 100 +} + +#Start transmission on Tx/Rx port +proc startTransmit {direction} { + set port [set ::[set direction]port] + set portList [set ::[set direction]PortList] + puts "+------------------------------------+" + puts "[format "| %-35s|" "Start transmit on $direction port $port."]" + if [ixStartTransmit portList] { + return $::TCL_ERROR + } + puts "+------------------------------------+" + after 500 +} + +#Stop transmission on Tx/Rx port +proc stopTransmit {direction} { + set port [set ::[set direction]port] + set portList [set ::[set direction]PortList] + puts "+------------------------------------+" + puts "[format "| %-35s|" "Stop transmit on $direction port $port."]" + if [ixStopTransmit portList] { + return $::TCL_ERROR + } + puts "+------------------------------------+" + after 500 +} + +#Start capture on Tx/Rx port +proc startCapture {direction} { + set card [set ::[set direction]card] + set port [set ::[set direction]port] + puts "+------------------------------------+" + puts "[format "| %-35s|" "Start capture on $direction port $port."]" + if [ixStartPortCapture $::chasId $card $port] { + return $::TCL_ERROR + } + puts "+------------------------------------+" +} + +#Stop capture on Tx/Rx port +proc stopCapture {direction} { + set card [set ::[set direction]card] + set port [set ::[set direction]port] + puts "+------------------------------------+" + puts "[format "| %-35s|" "Stop capture on $direction port $port."]" + if [ixStopPortCapture $::chasId $card $port] { + return $::TCL_ERROR + } + puts "+------------------------------------+" +} + +#Get Per-stream transmit statistics on Tx port +proc perStreamTxStats {} { + puts "+------------------------------------+" + puts "[format "| %-35s|" "Get Per-stream stats on Tx port $::txport."]" + if [streamTransmitStats get $::chasId $::txcard $::txport 1 $::txStreamNum] { + return $::TCL_ERROR + } + puts "+------------------------------------+" + after 1000 +} + +#Clean, Logoff and Disconnect +proc clean {server portList} { + #Let go of the ports that we reserved + ixClearOwnership $portList + #Log off user + ixLogout + #End a test and cleanup all variables + cleanUp +} diff --git a/dep/tclclient/ixTcl1.0/ixiaPing6.tcl b/dep/tclclient/ixTcl1.0/ixiaPing6.tcl new file mode 100644 index 00000000..3012346d --- /dev/null +++ b/dep/tclclient/ixTcl1.0/ixiaPing6.tcl @@ -0,0 +1,140 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# Copyright (c) 2014, Intel Corporation All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. + +proc ping6 {destIP destMAC chasId card port } { + + set retCode $::TCL_OK + set streamId 1 + set portList [list [list $chasId $card $port]] + set portIP "fe80:0000:0000:0000:021e:67ff:fe0d:b60a" + + # Set ports to factory defaults. Dumps out on error. + port setFactoryDefaults $chasId $card $port + port setDefault + + # Writes port properties in hardware + if {[ixWritePortsToHardware portList]} { + return $::TCL_ERROR + } + +# Check the link state of the ports + if {[ixCheckLinkState portList]} { + return $::TCL_ERROR + } + + protocol setDefault + protocol config -ethernetType ethernetII + protocol config -name ipV6 + + icmpV6 setDefault + icmpV6 setType icmpV6EchoRequestMessage + + icmpV6Informational setDefault + icmpV6Informational config -identifier 58275 + icmpV6Informational config -sequenceNumber 1 + + if {[icmpV6 set $chasId $card $port]} { + return $::TCL_ERROR + } + + ipV6 setDefault + ipV6 config -sourceAddr $portIP + ipV6 config -sourceMask 64 + ipV6 config -destAddr $destIP + ipV6 config -destMask 64 + ipV6 config -nextHeader ipV4ProtocolIpv6Icmp + ipV6 config -trafficClass 3 + ipV6 clearAllExtensionHeaders + + if {[ipV6 addExtensionHeader ipV4ProtocolIpv6Icmp]} { + return $::TCL_ERROR + } + + if {[ipV6 set $chasId $card $port]} { + return $::TCL_ERROR + } + + stream config -numFrames 1 + stream config -dma stopStream + stream config -framesize 122 + stream config -rateMode usePercentRate + stream config -percentPacketRate 1 + stream config -sa "00 00 01 00 02 00" + stream config -da $destMAC + + if {[stream set $chasId $card $port $streamId]} { + return $::TCL_ERROR + } + + if [ixWriteConfigToHardware portList] { + return -code error + } + + if [ixClearStats portList] { + return -code error + } + + after 1000 + + ixStartCapture portList + ixStartTransmit portList + + after 1000 + + if {[ixCheckTransmitDone portList] == $::TCL_ERROR} { + return -code error + } else { + ixPuts "Transmission is complete" + } + + if [ixStopCapture portList] { + return -code error + } + + captureBuffer get $chasId $card $port + if {[captureBuffer cget -numFrames] == 0} { + ixPuts "No packets received" + } else { + captureBuffer getframe 1 + set data [captureBuffer cget -frame] + icmpV6 decode $data $chasId $card $port + ipV6 decode $data $chasId $card $port + if {[icmpV6 cget -type] == 135 && [ipV6 cget -sourceAddr] == $destIP} { + ixPuts "64 bytes from" + } + } +# ixClearOwnership $portList +# ixDisconnectFromChassis $hostname +# if [isUNIX] { +# ixDisconnectTclServer $hostname +# } +# cleanUp +} diff --git a/dep/tclclient/ixTcl1.0/ixiarc.tcl b/dep/tclclient/ixTcl1.0/ixiarc.tcl new file mode 100644 index 00000000..3c8e46d9 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/ixiarc.tcl @@ -0,0 +1,60 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: ixiarc.tcl +# +# Copyright © IXIA +# All Rights Reserved. +# +# Revision Log: +# 04-28-1998 HS Genesis +# +# Description: +# This file sets up environment variables and other resources before the package +# can be loaded. This file must be sourced before the main script is run. It can +# be sourced manually in the wish/tclsh shell on placed as the first line of the +# main script. +# +################################################################################### +global env +# Results of all Ixia tests will be placed in this directory +set env(IXIA_RESULTS_DIR) [file dirname [file dirname [info library]]] + +# Run-time Logs of all Ixia tests will be placed in this directory +set env(IXIA_LOGS_DIR) [file dirname [file dirname [info library]]] + +# The Samples files are located under this directory +set env(IXIA_SAMPLESDIR) [file dirname [file dirname [info library]]] + + + + diff --git a/dep/tclclient/ixTcl1.0/pkgIndex.tcl b/dep/tclclient/ixTcl1.0/pkgIndex.tcl new file mode 100644 index 00000000..5e61c17e --- /dev/null +++ b/dep/tclclient/ixTcl1.0/pkgIndex.tcl @@ -0,0 +1,54 @@ +# BSD LICENSE +# +# Copyright (c) 2014, Ixia All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * 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. +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 THE COPYRIGHT +# OWNER 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. +# +# Version 6.20 +# +# File: pkgIndex.tcl +# +# Copyright © IXIA. +# All Rights Reserved. +# +############################################################################################# + +if {$::tcl_platform(platform) != "unix"} { + # if this package is already loaded, then don't load it again + if {[lsearch [package names] IxTclHal] != -1} { + return + } +} else { + lappend ::auto_path $dir +} + +package ifneeded IxTclHal 6.20 [list source [file join $dir ixTclHal.tcl]] + + + + + diff --git a/dep/tclclient/ixTcl1.0/tclIndex b/dep/tclclient/ixTcl1.0/tclIndex new file mode 100644 index 00000000..8d932054 --- /dev/null +++ b/dep/tclclient/ixTcl1.0/tclIndex @@ -0,0 +1,836 @@ +# Tcl autoload index file, version 2.0 +# This file is generated by the "auto_mkindex" command +# and sourced to set up indexing information for one or +# more commands. Typically each line is a command that +# sets an element in the auto_index array, where the +# element name is the name of a command and the value is +# a script that loads the command. + +set auto_index(ixDapConfigPort) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(ixDapAddPortInterface) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(ixDapAddRouteTable) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(ixDapAddPortFilter) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(ixDapBaseIpAddresses) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(ixDapCleanUp) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(ixDapSetBaseIp) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(ixDapAddRoute) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(ixDapDelRoute) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(ixDapLogin) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::Log) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::Error) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::init) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::login) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::printVersion) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::viewRoutes) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::addRoute) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::delRoute) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::createViewFrame) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::setAppName) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::setChassisList) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::getBaseIpAddressList) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::addInterfaceToArray) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::getInterfacePortList) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::getSortListFromArray) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::removeInterfaceFromArray) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::addRouteToArray) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::addFilterToArray) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::getValueFromList) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::isSubnetOverlapped) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::setBaseIp) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::validateBaseIpAddresses) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::setupPortInterfaces) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::addPortInterface) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::setupRouteTable) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::setupPortFilters) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::manageFilter) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::managePcpuCommand) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::downloadPackage) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::deletePackage) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::managePackage) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::pingBaseIpAddress) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::cleanUp) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(::ixDapConfig::main) [list source [file join $dir Dap ixDapConfig.tcl]] +set auto_index(clearStatsAndTransmit) [list source [file join $dir Generic actions.tcl]] +set auto_index(transmitAndCollectRxRatesPerSecond) [list source [file join $dir Generic actions.tcl]] +set auto_index(collectRxRatesPerSecond) [list source [file join $dir Generic actions.tcl]] +set auto_index(prepareToTransmit) [list source [file join $dir Generic actions.tcl]] +set auto_index(writeWaitForTransmit) [list source [file join $dir Generic actions.tcl]] +set auto_index(writeWaitForPause) [list source [file join $dir Generic actions.tcl]] +set auto_index(writeWaitMessage) [list source [file join $dir Generic actions.tcl]] +set auto_index(issuePortGroupCommand) [list source [file join $dir Generic actions.tcl]] +set auto_index(getPortGroupObject) [list source [file join $dir Generic actions.tcl]] +set auto_index(issuePortGroupMethod) [list source [file join $dir Generic actions.tcl]] +set auto_index(startTx) [list source [file join $dir Generic actions.tcl]] +set auto_index(startStaggeredTx) [list source [file join $dir Generic actions.tcl]] +set auto_index(stopTx) [list source [file join $dir Generic actions.tcl]] +set auto_index(startPortTx) [list source [file join $dir Generic actions.tcl]] +set auto_index(stopPortTx) [list source [file join $dir Generic actions.tcl]] +set auto_index(startCapture) [list source [file join $dir Generic actions.tcl]] +set auto_index(startPrbsCapture) [list source [file join $dir Generic actions.tcl]] +set auto_index(stopCapture) [list source [file join $dir Generic actions.tcl]] +set auto_index(stopPrbsCapture) [list source [file join $dir Generic actions.tcl]] +set auto_index(clearPrbsCapture) [list source [file join $dir Generic actions.tcl]] +set auto_index(startPortCapture) [list source [file join $dir Generic actions.tcl]] +set auto_index(startPortPrbsCapture) [list source [file join $dir Generic actions.tcl]] +set auto_index(stopPortCapture) [list source [file join $dir Generic actions.tcl]] +set auto_index(stopPortPrbsCapture) [list source [file join $dir Generic actions.tcl]] +set auto_index(clearPortPrbsCapture) [list source [file join $dir Generic actions.tcl]] +set auto_index(startPacketGroups) [list source [file join $dir Generic actions.tcl]] +set auto_index(stopPacketGroups) [list source [file join $dir Generic actions.tcl]] +set auto_index(startPortPacketGroups) [list source [file join $dir Generic actions.tcl]] +set auto_index(stopPortPacketGroups) [list source [file join $dir Generic actions.tcl]] +set auto_index(clearPacketGroups) [list source [file join $dir Generic actions.tcl]] +set auto_index(clearPortPacketGroups) [list source [file join $dir Generic actions.tcl]] +set auto_index(startCollisions) [list source [file join $dir Generic actions.tcl]] +set auto_index(stopCollisions) [list source [file join $dir Generic actions.tcl]] +set auto_index(startPortCollisions) [list source [file join $dir Generic actions.tcl]] +set auto_index(stopPortCollisions) [list source [file join $dir Generic actions.tcl]] +set auto_index(zeroStats) [list source [file join $dir Generic actions.tcl]] +set auto_index(zeroPortStats) [list source [file join $dir Generic actions.tcl]] +set auto_index(clearPerStreamTxStats) [list source [file join $dir Generic actions.tcl]] +set auto_index(clearPerStreamTxPortStats) [list source [file join $dir Generic actions.tcl]] +set auto_index(clearPcsLaneStatistics) [list source [file join $dir Generic actions.tcl]] +set auto_index(clearPcsLanePortStatistics) [list source [file join $dir Generic actions.tcl]] +set auto_index(clearTimeStamp) [list source [file join $dir Generic actions.tcl]] +set auto_index(flushAddressTable) [list source [file join $dir Generic actions.tcl]] +set auto_index(enableArpResponse) [list source [file join $dir Generic actions.tcl]] +set auto_index(enablePortArpResponse) [list source [file join $dir Generic actions.tcl]] +set auto_index(disableArpResponse) [list source [file join $dir Generic actions.tcl]] +set auto_index(disablePortArpResponse) [list source [file join $dir Generic actions.tcl]] +set auto_index(transmitArpRequest) [list source [file join $dir Generic actions.tcl]] +set auto_index(transmitPortArpRequest) [list source [file join $dir Generic actions.tcl]] +set auto_index(clearArpTable) [list source [file join $dir Generic actions.tcl]] +set auto_index(clearPortArpTable) [list source [file join $dir Generic actions.tcl]] +set auto_index(setDataIntegrityMode) [list source [file join $dir Generic actions.tcl]] +set auto_index(setPrbsMode) [list source [file join $dir Generic actions.tcl]] +set auto_index(setPacketGroupMode) [list source [file join $dir Generic actions.tcl]] +set auto_index(setWidePacketGroupMode) [list source [file join $dir Generic actions.tcl]] +set auto_index(setCaptureMode) [list source [file join $dir Generic actions.tcl]] +set auto_index(setTcpRoundTripFlowMode) [list source [file join $dir Generic actions.tcl]] +set auto_index(setPacketStreamMode) [list source [file join $dir Generic actions.tcl]] +set auto_index(setPacketFlowMode) [list source [file join $dir Generic actions.tcl]] +set auto_index(setAdvancedStreamSchedulerMode) [list source [file join $dir Generic actions.tcl]] +set auto_index(setFirstLastTimestampMode) [list source [file join $dir Generic actions.tcl]] +set auto_index(setDataIntegrityMode) [list source [file join $dir Generic actions.tcl]] +set auto_index(setSequenceCheckingMode) [list source [file join $dir Generic actions.tcl]] +set auto_index(changePortTransmitMode) [list source [file join $dir Generic actions.tcl]] +set auto_index(changePortReceiveMode) [list source [file join $dir Generic actions.tcl]] +set auto_index(writeToHardware) [list source [file join $dir Generic actions.tcl]] +set auto_index(writeToHardwareAsChunks) [list source [file join $dir Generic actions.tcl]] +set auto_index(writePortsToHardware) [list source [file join $dir Generic actions.tcl]] +set auto_index(writeConfigToHardware) [list source [file join $dir Generic actions.tcl]] +set auto_index(resetSequenceIndex) [list source [file join $dir Generic actions.tcl]] +set auto_index(resetPortSequenceIndex) [list source [file join $dir Generic actions.tcl]] +set auto_index(loadPoePulse) [list source [file join $dir Generic actions.tcl]] +set auto_index(loadPortPoePulse) [list source [file join $dir Generic actions.tcl]] +set auto_index(armPoeTrigger) [list source [file join $dir Generic actions.tcl]] +set auto_index(armPortPoeTrigger) [list source [file join $dir Generic actions.tcl]] +set auto_index(abortPoeArm) [list source [file join $dir Generic actions.tcl]] +set auto_index(abortPortPoeArm) [list source [file join $dir Generic actions.tcl]] +set auto_index(resetSequenceIndex) [list source [file join $dir Generic actions.tcl]] +set auto_index(resetPortSequenceIndex) [list source [file join $dir Generic actions.tcl]] +set auto_index(loadPoEPulse) [list source [file join $dir Generic actions.tcl]] +set auto_index(loadPortPoEPulse) [list source [file join $dir Generic actions.tcl]] +set auto_index(restartAutoNegotiation) [list source [file join $dir Generic actions.tcl]] +set auto_index(rebootLocalCpu) [list source [file join $dir Generic actions.tcl]] +set auto_index(rebootPortLocalCpu) [list source [file join $dir Generic actions.tcl]] +set auto_index(simulatePhysicalInterfaceDown) [list source [file join $dir Generic actions.tcl]] +set auto_index(simulatePortPhysicalInterfaceDown) [list source [file join $dir Generic actions.tcl]] +set auto_index(simulatePhysicalInterfaceUp) [list source [file join $dir Generic actions.tcl]] +set auto_index(simulatePortPhysicalInterfaceUp) [list source [file join $dir Generic actions.tcl]] +set auto_index(startAtmOamTransmit) [list source [file join $dir Generic actions.tcl]] +set auto_index(startPortAtmOamTransmit) [list source [file join $dir Generic actions.tcl]] +set auto_index(stopAtmOamTransmit) [list source [file join $dir Generic actions.tcl]] +set auto_index(stopPortAtmOamTransmit) [list source [file join $dir Generic actions.tcl]] +set auto_index(setScheduledTransmitTime) [list source [file join $dir Generic actions.tcl]] +set auto_index(setAutoDetectInstrumentationMode) [list source [file join $dir Generic actions.tcl]] +set auto_index(enablePortIntrinsicLatencyAdjustment) [list source [file join $dir Generic actions.tcl]] +set auto_index(enableIntrinsicLatencyAdjustment) [list source [file join $dir Generic actions.tcl]] +set auto_index(isIntrinsicLatencyAdjustmentEnabled) [list source [file join $dir Generic actions.tcl]] +set auto_index(ipAddressSetDefault) [list source [file join $dir Generic addressTableUtils.tcl]] +set auto_index(updateIpAddressTable) [list source [file join $dir Generic addressTableUtils.tcl]] +set auto_index(mpincr) [list source [file join $dir Generic calculate.tcl]] +set auto_index(calculatePercentLossExact) [list source [file join $dir Generic calculate.tcl]] +set auto_index(calculatePercentLoss) [list source [file join $dir Generic calculate.tcl]] +set auto_index(calculatePercentThroughput) [list source [file join $dir Generic calculate.tcl]] +set auto_index(calculateDuration) [list source [file join $dir Generic calculate.tcl]] +set auto_index(calculateTotalBursts) [list source [file join $dir Generic calculate.tcl]] +set auto_index(calculateAvgLatency) [list source [file join $dir Generic calculate.tcl]] +set auto_index(calculateLoopCounterFromTxFrames) [list source [file join $dir Generic calculate.tcl]] +set auto_index(calculateStreamNumFrames) [list source [file join $dir Generic calculate.tcl]] +set auto_index(getTransmitTime) [list source [file join $dir Generic calculate.tcl]] +set auto_index(getDurationFromCapture) [list source [file join $dir Generic calculate.tcl]] +set auto_index(::ixMath::min) [list source [file join $dir Generic calculate.tcl]] +set auto_index(::ixMath::max) [list source [file join $dir Generic calculate.tcl]] +set auto_index(maxArray) [list source [file join $dir Generic calculate.tcl]] +set auto_index(connectToChassis) [list source [file join $dir Generic chassisUtils.tcl]] +set auto_index(setConnectChassisFlag) [list source [file join $dir Generic chassisUtils.tcl]] +set auto_index(getConnectChassisFlag) [list source [file join $dir Generic chassisUtils.tcl]] +set auto_index(clientOpen) [list source [file join $dir Generic clientUtils.tcl]] +set auto_index(clientClose) [list source [file join $dir Generic clientUtils.tcl]] +set auto_index(clientSend) [list source [file join $dir Generic clientUtils.tcl]] +set auto_index(remoteDefine) [list source [file join $dir Generic clientUtils.tcl]] +set auto_index(getConstantsValue) [list source [file join $dir Generic clientUtils.tcl]] +set auto_index(ixMasterSet) [list source [file join $dir Generic clientUtils.tcl]] +set auto_index(redefineCommand) [list source [file join $dir Generic clientUtils.tcl]] +set auto_index(doFileTransfer) [list source [file join $dir Generic clientUtils.tcl]] +set auto_index(getPercentMaxRate) [list source [file join $dir Generic conversions.tcl]] +set auto_index(getMaxFPS) [list source [file join $dir Generic conversions.tcl]] +set auto_index(convertPercentMaxRate) [list source [file join $dir Generic conversions.tcl]] +set auto_index(convertKbpsRate) [list source [file join $dir Generic conversions.tcl]] +set auto_index(convertFpsRate) [list source [file join $dir Generic conversions.tcl]] +set auto_index(generateFullList) [list source [file join $dir Generic conversions.tcl]] +set auto_index(::dataValidation::initialize) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::setParameter) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::getParameter) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::showRules) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::parseValues) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::isPortList) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::isString) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::isInteger) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::isDouble) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::areValuesOfTheType) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::validateTest) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::getValidValueString) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::validateTestConfParameter) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::doValidateTestConfParameter) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::validateCommandParameter) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::setErrorString) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::getErrorString) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::isDataInRange) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::validatePortSpeed) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::getProtocol) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::checkIdentical) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::checkMatched) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::frameSizeProtocolSame) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::validateImixList) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::validateProtocolTable) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::validateCmFlowMix) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::validateRunType) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::validateNumber) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::checkErrFrameFrameErrorList) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::checkCmatsIpAndMacAddress) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::isValidMulticastIp) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::isValidUnicastIp) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::isOverlappingIpAddress) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::isSameSubnet) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::isValidNetMask) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(::dataValidation::isValidHostPart) [list source [file join $dir Generic dataValidation.tcl]] +set auto_index(initializeDefineCommand) [list source [file join $dir Generic defineCommand.tcl]] +set auto_index(::defineCommand::initialize) [list source [file join $dir Generic defineCommand.tcl]] +set auto_index(::defineCommand::isRegistered) [list source [file join $dir Generic defineCommand.tcl]] +set auto_index(::defineCommand::getAllRegisteredCommands) [list source [file join $dir Generic defineCommand.tcl]] +set auto_index(::defineCommand::registerCommand) [list source [file join $dir Generic defineCommand.tcl]] +set auto_index(::defineCommand::registerMethod) [list source [file join $dir Generic defineCommand.tcl]] +set auto_index(::defineCommand::registerParameter) [list source [file join $dir Generic defineCommand.tcl]] +set auto_index(initializeDefineTest) [list source [file join $dir Generic defineTest.tcl]] +set auto_index(::defineTest::initialize) [list source [file join $dir Generic defineTest.tcl]] +set auto_index(::defineTest::registerCommand) [list source [file join $dir Generic defineTest.tcl]] +set auto_index(::defineTest::registerParameter) [list source [file join $dir Generic defineTest.tcl]] +set auto_index(::defineTest::registerTest) [list source [file join $dir Generic defineTest.tcl]] +set auto_index(dhcpSetState) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpGetState) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpSetLease) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpGetLease) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpSetIP) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpGetIP) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpSetServer) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpGetServer) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpGetTimer) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpStartTimers) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpStartTimer) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpStopTimers) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpStopTimer) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpSetStreamRegion) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpGetStreamRegion) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpSetPortList) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpGetPortList) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpStop) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpStopPort) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpEnableStateMachine) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(dhcpDisableStateMachine) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(DHCPdiscoverIP) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(send_DHCP_discover) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(get_DHCP_offer) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(send_DHCP_request) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(get_DHCP_ack) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(send_DHCP_release) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(setupUDPbootp) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(setupDhcpBroadcastIP) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(setupDhcpUnicastIP) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(setupDefaultDhcpParameters) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(setDhcpOptions) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(sendDhcpPacket) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(buildDhcpPacket) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(get_DHCP_packet) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::Initialize) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::Stop) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::StopPort) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::SetState) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::GetState) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::SetLease) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::GetLease) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::StartTimers) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::StopTimers) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::StartTimer) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::StopTimer) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::GetTimer) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::SetIP) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::GetIP) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::SetServer) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::GetServer) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::SetStreamRegion) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::GetStreamRegion) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::SetPortList) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::GetPortList) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::InitStateTable) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::StateLookup) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::ActionNull) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::ActionRenew) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::ActionRebind) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::ActionInit) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::GetStateNames) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::GetStateName) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::GetStateCodes) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::ValidState) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::GetEventName) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::GetEventCodes) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::ValidEvent) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::getTransactionID) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::clearParameterRequestList) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::getParameterRequestList) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::setParameterRequestList) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::getMagicCookie) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::setStartTime) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::getStartTime) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(::dhcpClient::debug) [list source [file join $dir Generic dhcpExchange.tcl]] +set auto_index(createDialog) [list source [file join $dir Generic dialogUtils.tcl]] +set auto_index(writeDialog) [list source [file join $dir Generic dialogUtils.tcl]] +set auto_index(destroyDialog) [list source [file join $dir Generic dialogUtils.tcl]] +set auto_index(::dialogUtils::init) [list source [file join $dir Generic dialogUtils.tcl]] +set auto_index(::dialogUtils::create) [list source [file join $dir Generic dialogUtils.tcl]] +set auto_index(::dialogUtils::writeText) [list source [file join $dir Generic dialogUtils.tcl]] +set auto_index(::dialogUtils::getWindowState) [list source [file join $dir Generic dialogUtils.tcl]] +set auto_index(::dialogUtils::setWindowState) [list source [file join $dir Generic dialogUtils.tcl]] +set auto_index(::dialogUtils::saveWindowState) [list source [file join $dir Generic dialogUtils.tcl]] +set auto_index(::dialogUtils::destroyedByUser) [list source [file join $dir Generic dialogUtils.tcl]] +set auto_index(::dialogUtils::destroy) [list source [file join $dir Generic dialogUtils.tcl]] +set auto_index(setStopTestFlag) [list source [file join $dir Generic eventHandler.tcl]] +set auto_index(stopTest) [list source [file join $dir Generic eventHandler.tcl]] +set auto_index(isTestStopped) [list source [file join $dir Generic eventHandler.tcl]] +set auto_index(informServerCurrentTestStopped) [list source [file join $dir Generic eventHandler.tcl]] +set auto_index(IsPOSPort) [list source [file join $dir Generic featureUtils.tcl]] +set auto_index(Is10GigEPort) [list source [file join $dir Generic featureUtils.tcl]] +set auto_index(IsGigabitPort) [list source [file join $dir Generic featureUtils.tcl]] +set auto_index(any10100Ports) [list source [file join $dir Generic featureUtils.tcl]] +set auto_index(anyGigPorts) [list source [file join $dir Generic featureUtils.tcl]] +set auto_index(anyOc48Ports) [list source [file join $dir Generic featureUtils.tcl]] +set auto_index(anyOc192Ports) [list source [file join $dir Generic featureUtils.tcl]] +set auto_index(anyPortsByInterface) [list source [file join $dir Generic featureUtils.tcl]] +set auto_index(anyPortsBySpeed) [list source [file join $dir Generic featureUtils.tcl]] +set auto_index(supportsProtocolServer) [list source [file join $dir Generic featureUtils.tcl]] +set auto_index(supportsPortCPU) [list source [file join $dir Generic featureUtils.tcl]] +set auto_index(isValidFeature) [list source [file join $dir Generic featureUtils.tcl]] +set auto_index(isPacketFlowMode) [list source [file join $dir Generic featureUtils.tcl]] +set auto_index(isAdvancedStreamSchedulerMode) [list source [file join $dir Generic featureUtils.tcl]] +set auto_index(::fileTransferClient::openConnectionToTclServer) [list source [file join $dir Generic fileTransferClient.tcl]] +set auto_index(::fileTransferClient::closeTclServerConnection) [list source [file join $dir Generic fileTransferClient.tcl]] +set auto_index(::fileTransferClient::sendTransactionToTclServer) [list source [file join $dir Generic fileTransferClient.tcl]] +set auto_index(::fileTransferClient::getFile) [list source [file join $dir Generic fileTransferClient.tcl]] +set auto_index(::fileTransferClient::putFile) [list source [file join $dir Generic fileTransferClient.tcl]] +set auto_index(::fileTransferClient::getTransferOutcome) [list source [file join $dir Generic fileTransferClient.tcl]] +set auto_index(putFtpLogWindow) [list source [file join $dir Generic ftp.tcl]] +set auto_index(ixFileSend) [list source [file join $dir Generic ftp.tcl]] +set auto_index(ixFileTransferStart) [list source [file join $dir Generic ftp.tcl]] +set auto_index(ixInitialize) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixConnectToChassis) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixConnectToTclServer) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixDisconnectTclServer) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixGetChassisID) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixDisconnectFromChassis) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixGlobalSetDefault) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStartTransmit) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStartPortTransmit) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStartStaggeredTransmit) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStopTransmit) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStopPortTransmit) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStartCapture) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStartPrbsCapture) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStopCapture) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStopPrbsCapture) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixClearPrbsCapture) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStartPortCapture) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStartPortPrbsCapture) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStopPortCapture) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStopPortPrbsCapture) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixClearPortPrbsCapture) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixClearStats) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixClearPortStats) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixClearPerStreamTxStats) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixClearPerStreamTxPortStats) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixRequestStats) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixClearTimeStamp) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStartPacketGroups) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStartPortPacketGroups) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStopPacketGroups) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixClearPacketGroups) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixClearPortPacketGroups) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetScheduledTransmitTime) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixClearScheduledTransmitTime) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStopPortPacketGroups) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStartCollisions) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStartPortCollisions) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStopCollisions) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStopPortCollisions) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixLoadPoePulse) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixLoadPortPoePulse) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixArmPoeTrigger) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixArmPortPoeTrigger) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixAbortPoeArm) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixAbortPortPoeArm) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStartAtmOamTransmit) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStartPortAtmOamTransmit) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStopAtmOamTransmit) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixStopPortAtmOamTransmit) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixCreatePortListWildCard) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixCreateSortedPortList) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixPuts) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixiaPortSetParms) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixiaReadWriteMII) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixTclSvrConnect) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixTclSvrDisconnect) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixEnableArpResponse) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixEnablePortArpResponse) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixDisableArpResponse) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixTransmitArpRequest) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixClearArpTable) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixDisablePortArpResponse) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixTransmitPortArpRequest) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixClearPortArpTable) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetPacketGroupMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetPortPacketGroupMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetAutoDetectInstrumentationMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetPortAutoDetectInstrumentationMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetWidePacketGroupMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetPortWidePacketGroupMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetCaptureMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetPortCaptureMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetTcpRoundTripFlowMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetPortTcpRoundTripFlowMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetDataIntegrityMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetPortDataIntegrityMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetPrbsMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetPortPrbsMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetSequenceCheckingMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetPortSequenceCheckingMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetPacketFlowMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetPortPacketFlowMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetPacketStreamMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetPortPacketStreamMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetAdvancedStreamSchedulerMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSetPortAdvancedStreamSchedulerMode) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixWritePortsToHardware) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixWriteConfigToHardware) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixCheckTransmitDone) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixCheckPortTransmitDone) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixCheckLinkState) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixCheckPPPState) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixCollectStats) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixProxyConnect) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixResetSequenceIndex) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixResetPortSequenceIndex) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixRestartAutoNegotiation) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixRestartPortAutoNegotiation) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixRestartPPPNegotiation) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixRestartPortPPPNegotiation) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSimulatePhysicalInterfaceUp) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSimulatePortPhysicalInterfaceUp) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSimulatePhysicalInterfaceDown) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixSimulatePortPhysicalInterfaceDown) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixIsOverlappingIpAddress) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixIsSameSubnet) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixIsValidHost) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixIsValidNetMask) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixIsValidUnicastIp) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixConvertFromSeconds) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixConvertToSeconds) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixEnablePortIntrinsicLatencyAdjustment) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixEnableIntrinsicLatencyAdjustment) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(ixIsIntrinsicLatencyAdjustmentEnabled) [list source [file join $dir Generic highLevelAPI.tcl]] +set auto_index(initCommand_zz_advancedTestParameter) [list source [file join $dir Generic interface.tcl]] +set auto_index(initCommand_zz_fastpath) [list source [file join $dir Generic interface.tcl]] +set auto_index(initCommand_zz_ipfastpath) [list source [file join $dir Generic interface.tcl]] +set auto_index(initCommand_zz_learn) [list source [file join $dir Generic interface.tcl]] +set auto_index(initCommand_zz_logger) [list source [file join $dir Generic interface.tcl]] +set auto_index(initCommand_zz_map) [list source [file join $dir Generic interface.tcl]] +set auto_index(initCommand_zz_tclClient) [list source [file join $dir Generic interface.tcl]] +set auto_index(initCommand_zz_testProfile) [list source [file join $dir Generic interface.tcl]] +set auto_index(::interfaceTable::setDefault) [list source [file join $dir Generic interfaceTableUtils.tcl]] +set auto_index(::interfaceTable::configurePort) [list source [file join $dir Generic interfaceTableUtils.tcl]] +set auto_index(::interfaceTable::configure) [list source [file join $dir Generic interfaceTableUtils.tcl]] +set auto_index(::interfaceTable::addEntry) [list source [file join $dir Generic interfaceTableUtils.tcl]] +set auto_index(::interfaceTable::updateEntry) [list source [file join $dir Generic interfaceTableUtils.tcl]] +set auto_index(::interfaceTable::updateItemIpV4) [list source [file join $dir Generic interfaceTableUtils.tcl]] +set auto_index(::interfaceTable::updateItemIpV6) [list source [file join $dir Generic interfaceTableUtils.tcl]] +set auto_index(::interfaceTable::addMultipleEntry) [list source [file join $dir Generic interfaceTableUtils.tcl]] +set auto_index(::interfaceTable::addItemIpV4) [list source [file join $dir Generic interfaceTableUtils.tcl]] +set auto_index(::interfaceTable::formatEntryDescription) [list source [file join $dir Generic interfaceTableUtils.tcl]] +set auto_index(::interfaceTable::getInterfaceId) [list source [file join $dir Generic interfaceTableUtils.tcl]] +set auto_index(::interfaceTable::getInterfaceCount) [list source [file join $dir Generic interfaceTableUtils.tcl]] +set auto_index(::interfaceTable::enableInterface) [list source [file join $dir Generic interfaceTableUtils.tcl]] +set auto_index(::interfaceTable::disableInterface) [list source [file join $dir Generic interfaceTableUtils.tcl]] +set auto_index(::interfaceTable::disableAllInterfaces) [list source [file join $dir Generic interfaceTableUtils.tcl]] +set auto_index(::interfaceTable::deleteInterface) [list source [file join $dir Generic interfaceTableUtils.tcl]] +set auto_index(::interfaceTable::getEntryList) [list source [file join $dir Generic interfaceTableUtils.tcl]] +set auto_index(::interfaceTable::getGatewayArray) [list source [file join $dir Generic interfaceTableUtils.tcl]] +set auto_index(::ipv6::host2addr) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::expandAddress) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::compressAddress) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::convertAddress) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::convertMacToIpV6) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::convertIpToIpV6) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::convertIpToIsatap) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::convertIptoIpV4Compatible) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::convertIpTo6to4) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::convertIpV6ToMac) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::convertIpV6ToIp) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::convertNoop) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::getAddressFields) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::getFieldListByPrefix) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::getFieldNamesByPrefix) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::getFormatPrefix) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::getTopLevelAggregateId) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::getNextLevelAggregateId) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::getSiteLevelAggregateId) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::getSubnetId) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::getInterfaceId) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::getLoopbackAddress) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::isValidAddress) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::validateAddress) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::isReservedMCAddress) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::isValidMCAddress) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::isMixedVersionAddress) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::incrIpField) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::convertIpv6AddrToBytes) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::convertBytesToIpv6Address) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::incIpv6AddressByPrefix) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::getFieldMask) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::getMinimumValidFramesize) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::getHeaderLength) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ipv6::getAddressFieldOffset) [list source [file join $dir Generic ipV6Utils.tcl]] +set auto_index(::ixFileUtils::closeAll) [list source [file join $dir Generic ixFileUtils.tcl]] +set auto_index(::ixFileUtils::addFileToList) [list source [file join $dir Generic ixFileUtils.tcl]] +set auto_index(::ixFileUtils::removeFileFromList) [list source [file join $dir Generic ixFileUtils.tcl]] +set auto_index(::ixFileUtils::open) [list source [file join $dir Generic ixFileUtils.tcl]] +set auto_index(::ixFileUtils::close) [list source [file join $dir Generic ixFileUtils.tcl]] +set auto_index(::ixGraph::create) [list source [file join $dir Generic ixGraph.tcl]] +set auto_index(::ixGraph::reset) [list source [file join $dir Generic ixGraph.tcl]] +set auto_index(::ixGraph::destroy) [list source [file join $dir Generic ixGraph.tcl]] +set auto_index(::ixGraph::addLine) [list source [file join $dir Generic ixGraph.tcl]] +set auto_index(::ixGraph::getLines) [list source [file join $dir Generic ixGraph.tcl]] +set auto_index(::ixGraph::deleteLine) [list source [file join $dir Generic ixGraph.tcl]] +set auto_index(::ixGraph::resetLine) [list source [file join $dir Generic ixGraph.tcl]] +set auto_index(::ixGraph::updateLine) [list source [file join $dir Generic ixGraph.tcl]] +set auto_index(::ixGraph::getDataVector) [list source [file join $dir Generic ixGraph.tcl]] +set auto_index(::ixGraph::setDataVector) [list source [file join $dir Generic ixGraph.tcl]] +set auto_index(::ixGraph::resetDataVector) [list source [file join $dir Generic ixGraph.tcl]] +set auto_index(::ixGraph::updateDataVectorPair) [list source [file join $dir Generic ixGraph.tcl]] +set auto_index(::ixGraph::updateDataVector) [list source [file join $dir Generic ixGraph.tcl]] +set auto_index(::ixGraph::updateCoordinates) [list source [file join $dir Generic ixGraph.tcl]] +set auto_index(::ixGraph::advanceBuffer) [list source [file join $dir Generic ixGraph.tcl]] +set auto_index(::logger::on) [list source [file join $dir Generic log.tcl]] +set auto_index(::logger::off) [list source [file join $dir Generic log.tcl]] +set auto_index(::logger::message) [list source [file join $dir Generic log.tcl]] +set auto_index(::logger::show) [list source [file join $dir Generic log.tcl]] +set auto_index(ixCollectAndSendLogs) [list source [file join $dir Generic logCollector.tcl]] +set auto_index(::map::new) [list source [file join $dir Generic map.tcl]] +set auto_index(::map::add) [list source [file join $dir Generic map.tcl]] +set auto_index(::map::del) [list source [file join $dir Generic map.tcl]] +set auto_index(::map::show) [list source [file join $dir Generic map.tcl]] +set auto_index(::map::validateMapType) [list source [file join $dir Generic map.tcl]] +set auto_index(showmaps) [list source [file join $dir Generic map.tcl]] +set auto_index(ixMiiConfigPreEmphasis) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(ixMiiConfigLossOfSignalThreshold) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(ixMiiConfigXgxsLinkMonitoring) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(ixMiiConfigAlignRxDataClock) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(ixMiiConfigReceiveEqualization) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(ixMiiConfigXauiOutput) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(ixMiiConfigXauiSerialLoopback) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(ixMiiConfigXgmiiParallelLoopback) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(::miiConfig::preEmphasis) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(::miiConfig::lossOfSignalThreshold) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(::miiConfig::xgxsLinkMonitoring) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(::miiConfig::alignRxDataClock) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(::miiConfig::receiveEqualization) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(::miiConfig::xauiOutput) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(::miiConfig::xauiSerialLoopback) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(::miiConfig::xgmiiParallelLoopback) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(::miiConfig::isXauiSerialLoopback) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(::miiConfig::isXgmiiParallelLoopback) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(::miiConfig::setRegister) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(::miiConfig::getRegister) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(::miiaeConfig::setRegister) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(::miiaeConfig::getRegister) [list source [file join $dir Generic miiUtils.tcl]] +set auto_index(logOn) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(logOff) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(logMsg) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(errorMsg) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(debugOn) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(debugOff) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(debugMsg) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(showCmd) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(openMyFile) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(closeMyFile) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(callTraceMsg) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(traceVariableMsg) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(isDigit) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(isNegative) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(isValidExponentialFloat) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(isValidPositiveExponentialFloat) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(isValidPartialFloat) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(isValidPositivePartialFloat) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(isValidPositiveFloat) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(isValidInteger) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(isValidPositiveInteger) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(getProcList) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(getTxBasedOnRx) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(convertFromSeconds) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(convertToSeconds) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(formatDurationTime) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(formatNumber) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(unixCludgeGetExpr) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(useProfile) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(CountGlobalMemory) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(createNamedFont) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(buildFileName) [list source [file join $dir Generic miscCmds.tcl]] +set auto_index(ixSource) [list source [file join $dir Generic multiUser.tcl]] +set auto_index(sourceRecursively) [list source [file join $dir Generic multiUser.tcl]] +set auto_index(ixLogin) [list source [file join $dir Generic multiUser.tcl]] +set auto_index(ixLogout) [list source [file join $dir Generic multiUser.tcl]] +set auto_index(ixTakeOwnership) [list source [file join $dir Generic multiUser.tcl]] +set auto_index(ixPortTakeOwnership) [list source [file join $dir Generic multiUser.tcl]] +set auto_index(ixClearOwnership) [list source [file join $dir Generic multiUser.tcl]] +set auto_index(ixPortClearOwnership) [list source [file join $dir Generic multiUser.tcl]] +set auto_index(ixCheckOwnership) [list source [file join $dir Generic multiUser.tcl]] +set auto_index(canUse) [list source [file join $dir Generic multiUser.tcl]] +set auto_index(isMine) [list source [file join $dir Generic multiUser.tcl]] +set auto_index(calculateChecksum) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(oid2octet) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildLLCHeader) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildIpHeader) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildRipBlock) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildVidHeader) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildIPXPacket) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildIPXData) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildServerEntry) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildSapPacket) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildNetworkEntry) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildRipxPacket) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildArpPacket) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildRipPacket) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildUdpEchoPacket) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildIgmpPacket) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildIpPriorityPacket) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildVlanTagPacket) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildSnmpPacket) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildIcmpPacket) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(buildBpduPacket) [list source [file join $dir Generic packetBuilder.tcl]] +set auto_index(ixGetArgument) [list source [file join $dir Generic parseArgs.tcl]] +set auto_index(isUNIX) [list source [file join $dir Generic platform.tcl]] +set auto_index(isWindows) [list source [file join $dir Generic platform.tcl]] +set auto_index(RandomRange) [list source [file join $dir Generic random.tcl]] +set auto_index(Random) [list source [file join $dir Generic random.tcl]] +set auto_index(RandomInit) [list source [file join $dir Generic random.tcl]] +set auto_index(RandomFromTo) [list source [file join $dir Generic random.tcl]] +set auto_index(::scriptmateBackwardsCompatibility::createAllCommands) [list source [file join $dir Generic scriptmateBackCompat.tcl]] +set auto_index(serverSocketAccept) [list source [file join $dir Generic socket.tcl]] +set auto_index(readsocket) [list source [file join $dir Generic socket.tcl]] +set auto_index(handleEvent) [list source [file join $dir Generic socket.tcl]] +set auto_index(generatePort) [list source [file join $dir Generic socket.tcl]] +set auto_index(putsClient) [list source [file join $dir Generic socket.tcl]] +set auto_index(createServerSocket) [list source [file join $dir Generic socket.tcl]] +set auto_index(closeSocket) [list source [file join $dir Generic socket.tcl]] +set auto_index(closeServerSocket) [list source [file join $dir Generic socket.tcl]] +set auto_index(isTestCommandSocket) [list source [file join $dir Generic socket.tcl]] +set auto_index(isLogSocket) [list source [file join $dir Generic socket.tcl]] +set auto_index(isCommandSocket) [list source [file join $dir Generic socket.tcl]] +set auto_index(isDataSocket) [list source [file join $dir Generic socket.tcl]] +set auto_index(createClientSocket) [list source [file join $dir Generic socket.tcl]] +set auto_index(closeClientSocket) [list source [file join $dir Generic socket.tcl]] +set auto_index(closeAllSockets) [list source [file join $dir Generic socket.tcl]] +set auto_index(readClientSocket) [list source [file join $dir Generic socket.tcl]] +set auto_index(createClientSocketCreate) [list source [file join $dir Generic socket.tcl]] +set auto_index(putsServer) [list source [file join $dir Generic socket.tcl]] +set auto_index(handleCommand) [list source [file join $dir Generic socket.tcl]] +set auto_index(checkTransmitDone) [list source [file join $dir Generic statistics.tcl]] +set auto_index(checkAllTransmitDone) [list source [file join $dir Generic statistics.tcl]] +set auto_index(requestStats) [list source [file join $dir Generic statistics.tcl]] +set auto_index(collectTxStats) [list source [file join $dir Generic statistics.tcl]] +set auto_index(collectRxStats) [list source [file join $dir Generic statistics.tcl]] +set auto_index(collectVlanStats) [list source [file join $dir Generic statistics.tcl]] +set auto_index(collectDataIntegrityStats) [list source [file join $dir Generic statistics.tcl]] +set auto_index(collectSequenceStats) [list source [file join $dir Generic statistics.tcl]] +set auto_index(collectErroredFramesStats) [list source [file join $dir Generic statistics.tcl]] +set auto_index(collectQosStats) [list source [file join $dir Generic statistics.tcl]] +set auto_index(collectStats) [list source [file join $dir Generic statistics.tcl]] +set auto_index(getNumErroredFrames) [list source [file join $dir Generic statistics.tcl]] +set auto_index(checkLinkState) [list source [file join $dir Generic statistics.tcl]] +set auto_index(checkPPPState) [list source [file join $dir Generic statistics.tcl]] +set auto_index(getRunningRate) [list source [file join $dir Generic statistics.tcl]] +set auto_index(getRunRatePerSecond) [list source [file join $dir Generic statistics.tcl]] +set auto_index(collectRates) [list source [file join $dir Generic statistics.tcl]] +set auto_index(capitalizeString) [list source [file join $dir Generic stringUtils.tcl]] +set auto_index(stringRepeat) [list source [file join $dir Generic stringUtils.tcl]] +set auto_index(stringIsInteger) [list source [file join $dir Generic stringUtils.tcl]] +set auto_index(stringIsDouble) [list source [file join $dir Generic stringUtils.tcl]] +set auto_index(stringSubstitute) [list source [file join $dir Generic stringUtils.tcl]] +set auto_index(stringUnderscore) [list source [file join $dir Generic stringUtils.tcl]] +set auto_index(stringTitle) [list source [file join $dir Generic stringUtils.tcl]] +set auto_index(stringSplitToTitle) [list source [file join $dir Generic stringUtils.tcl]] +set auto_index(stringJoinFromTitle) [list source [file join $dir Generic stringUtils.tcl]] +set auto_index(stringToUpper) [list source [file join $dir Generic stringUtils.tcl]] +set auto_index(stringMap) [list source [file join $dir Generic stringUtils.tcl]] +set auto_index(stringCompare) [list source [file join $dir Generic stringUtils.tcl]] +set auto_index(stringFormatNumber) [list source [file join $dir Generic stringUtils.tcl]] +set auto_index(stringReplace) [list source [file join $dir Generic stringUtils.tcl]] +set auto_index(send_learn_frames) [list source [file join $dir Generic switchLearn.tcl]] +set auto_index(OLDsend_arp_frames) [list source [file join $dir Generic switchLearn.tcl]] +set auto_index(send_arp_frames) [list source [file join $dir Generic switchLearn.tcl]] +set auto_index(configureArp) [list source [file join $dir Generic switchLearn.tcl]] +set auto_index(sendArp) [list source [file join $dir Generic switchLearn.tcl]] +set auto_index(verifyAllArpFramesSent) [list source [file join $dir Generic switchLearn.tcl]] +set auto_index(verifyArpReply) [list source [file join $dir Generic switchLearn.tcl]] +set auto_index(send_neighborDiscovery_frames) [list source [file join $dir Generic switchLearn.tcl]] +set auto_index(performNeighborDiscovery) [list source [file join $dir Generic switchLearn.tcl]] +set auto_index(sendRouterSolicitation) [list source [file join $dir Generic switchLearn.tcl]] +set auto_index(getNeighborDiscovery) [list source [file join $dir Generic switchLearn.tcl]] +set auto_index(getNeighborDiscoveryPort) [list source [file join $dir Generic switchLearn.tcl]] +set auto_index(sapStr2Asc) [list source [file join $dir Generic switchLearn.tcl]] +set auto_index(send_ripx_frames) [list source [file join $dir Generic switchLearn.tcl]] +set auto_index(send_sap_server_frames) [list source [file join $dir Generic switchLearn.tcl]] +set auto_index(send_sapgns_frames) [list source [file join $dir Generic switchLearn.tcl]] +set auto_index(::tclServer::getTclServerName) [list source [file join $dir Generic tclServer.tcl]] +set auto_index(::tclServer::setTclServerName) [list source [file join $dir Generic tclServer.tcl]] +set auto_index(::tclServer::disconnectTclServer) [list source [file join $dir Generic tclServer.tcl]] +set auto_index(::tclServer::read) [list source [file join $dir Generic tclServer.tcl]] +set auto_index(::tclServer::connectToTclServer) [list source [file join $dir Generic tclServer.tcl]] +set auto_index(::tclServer::isTclServerConnected) [list source [file join $dir Generic tclServer.tcl]] +set auto_index(::tclServer::setTclServer) [list source [file join $dir Generic tclServer.tcl]] +set auto_index(::tclServer::connectButton) [list source [file join $dir Generic tclServer.tcl]] +set auto_index(::tclServer::cancelButton) [list source [file join $dir Generic tclServer.tcl]] +set auto_index(parseCmd) [list source [file join $dir Generic testCmdControl.tcl]] +set auto_index(configureOptions) [list source [file join $dir Generic testCmdControl.tcl]] +set auto_index(cgetOptions) [list source [file join $dir Generic testCmdControl.tcl]] +set auto_index(getParmProperty) [list source [file join $dir Generic testCmdControl.tcl]] +set auto_index(startOptions) [list source [file join $dir Generic testCmdControl.tcl]] +set auto_index(registerResultVarsOptions) [list source [file join $dir Generic testCmdControl.tcl]] +set auto_index(existsOptions) [list source [file join $dir Generic testCmdControl.tcl]] +set auto_index(calcTrafficMix) [list source [file join $dir Generic trafficMix.tcl]] +set auto_index(calcAggregateDataRate) [list source [file join $dir Generic trafficMix.tcl]] +set auto_index(calcAggregateFrameRate) [list source [file join $dir Generic trafficMix.tcl]] +set auto_index(calcAggregateTotalRate) [list source [file join $dir Generic trafficMix.tcl]] +set auto_index(calcAggregateBitRate) [list source [file join $dir Generic trafficMix.tcl]] +set auto_index(calcAggregatePPS) [list source [file join $dir Generic trafficMix.tcl]] +set auto_index(calcTotalBits) [list source [file join $dir Generic trafficMix.tcl]] +set auto_index(calcTotalStreamTime) [list source [file join $dir Generic trafficMix.tcl]] +set auto_index(globalSetDefault) [list source [file join $dir Generic utils.tcl]] +set auto_index(protocolStackSetDefault) [list source [file join $dir Generic utils.tcl]] +set auto_index(streamSet) [list source [file join $dir Generic utils.tcl]] +set auto_index(validateFramesize) [list source [file join $dir Generic utils.tcl]] +set auto_index(validateFramesizeList) [list source [file join $dir Generic utils.tcl]] +set auto_index(validatePreamblesize) [list source [file join $dir Generic utils.tcl]] +set auto_index(getLearnProc) [list source [file join $dir Generic utils.tcl]] +set auto_index(validateProtocol) [list source [file join $dir Generic utils.tcl]] +set auto_index(initMaxRate) [list source [file join $dir Generic utils.tcl]] +set auto_index(buildIpMcastMacAddress) [list source [file join $dir Generic utils.tcl]] +set auto_index(setPortName) [list source [file join $dir Generic utils.tcl]] +set auto_index(getPortString) [list source [file join $dir Generic utils.tcl]] +set auto_index(getPortId) [list source [file join $dir Generic utils.tcl]] +set auto_index(getPortName) [list source [file join $dir Generic utils.tcl]] +set auto_index(setPortFactoryDefaults) [list source [file join $dir Generic utils.tcl]] +set auto_index(setFactoryDefaults) [list source [file join $dir Generic utils.tcl]] +set auto_index(getProtocolName) [list source [file join $dir Generic utils.tcl]] +set auto_index(getDuplexModeString) [list source [file join $dir Generic utils.tcl]] +set auto_index(disableUdfs) [list source [file join $dir Generic utils.tcl]] +set auto_index(getIpClassName) [list source [file join $dir Generic utils.tcl]] +set auto_index(getMinimum) [list source [file join $dir Generic utils.tcl]] +set auto_index(swapPortList) [list source [file join $dir Generic utils.tcl]] +set auto_index(copyPortList) [list source [file join $dir Generic utils.tcl]] +set auto_index(removePorts) [list source [file join $dir Generic utils.tcl]] +set auto_index(lnumsort) [list source [file join $dir Generic utils.tcl]] +set auto_index(mergeLists) [list source [file join $dir Generic utils.tcl]] +set auto_index(host2addr) [list source [file join $dir Generic utils.tcl]] +set auto_index(long2IpAddr) [list source [file join $dir Generic utils.tcl]] +set auto_index(byte2IpAddr) [list source [file join $dir Generic utils.tcl]] +set auto_index(num2ip) [list source [file join $dir Generic utils.tcl]] +set auto_index(ip2num) [list source [file join $dir Generic utils.tcl]] +set auto_index(long2octet) [list source [file join $dir Generic utils.tcl]] +set auto_index(list2word) [list source [file join $dir Generic utils.tcl]] +set auto_index(value2Hexlist) [list source [file join $dir Generic utils.tcl]] +set auto_index(hexlist2Value) [list source [file join $dir Generic utils.tcl]] +set auto_index(expandHexString) [list source [file join $dir Generic utils.tcl]] +set auto_index(getMultipleNumbers) [list source [file join $dir Generic utils.tcl]] +set auto_index(hextodec) [list source [file join $dir Generic utils.tcl]] +set auto_index(dectohex) [list source [file join $dir Generic utils.tcl]] +set auto_index(incrMacAddress) [list source [file join $dir Generic utils.tcl]] +set auto_index(incrIpField) [list source [file join $dir Generic utils.tcl]] +set auto_index(incrIpFieldHexFormat) [list source [file join $dir Generic utils.tcl]] +set auto_index(assignIncrMacAddresses) [list source [file join $dir Generic utils.tcl]] +set auto_index(incrHostIpAddr) [list source [file join $dir Generic utils.tcl]] +set auto_index(waitForResidualFrames) [list source [file join $dir Generic utils.tcl]] +set auto_index(getPerTxArray) [list source [file join $dir Generic utils.tcl]] +set auto_index(getTxPorts) [list source [file join $dir Generic utils.tcl]] +set auto_index(getRxPorts) [list source [file join $dir Generic utils.tcl]] +set auto_index(getAllPorts) [list source [file join $dir Generic utils.tcl]] +set auto_index(comparePortArray) [list source [file join $dir Generic utils.tcl]] +set auto_index(mergePortArray) [list source [file join $dir Generic utils.tcl]] +set auto_index(getAdvancedSchedulerArray) [list source [file join $dir Generic utils.tcl]] +set auto_index(cleanUpMultiuser) [list source [file join $dir Generic utils.tcl]] +set auto_index(cleanUp) [list source [file join $dir Generic utils.tcl]] +set auto_index(isIpAddressValid) [list source [file join $dir Generic utils.tcl]] +set auto_index(isMacAddressValid) [list source [file join $dir Generic utils.tcl]] +set auto_index(isPartialMacAddressValid) [list source [file join $dir Generic utils.tcl]] +set auto_index(getCommandParameters) [list source [file join $dir Generic utils.tcl]] +set auto_index(changePortLoopback) [list source [file join $dir Generic utils.tcl]] +set auto_index(validateUnidirectionalMap) [list source [file join $dir Generic utils.tcl]] +set auto_index(getTxRxModeString) [list source [file join $dir Generic utils.tcl]] +set auto_index(removeStreams) [list source [file join $dir Generic utils.tcl]] +set auto_index(getIpV4MaskWidth) [list source [file join $dir Generic utils.tcl]] +set auto_index(getIpV4MaskFromWidth) [list source [file join $dir Generic utils.tcl]] +set auto_index(ixIsBgpInstalled) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(ixIsIsisInstalled) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(ixIsRsvpInstalled) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(ixIsOspfInstalled) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(ixIsRipInstalled) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(ixIsArpInstalled) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(ixIsIgmpInstalled) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(ixIsVpnL2Installed) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(ixIsVpnL3Installed) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(ixIsMldInstalled) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(ixIsOspfV3Installed) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(ixIsPimsmInstalled) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(ixGetLineUtilization) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(ixIsLdpInstalled) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(ixIsRipngInstalled) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(calculateMaxRate) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(calculateGapBytes) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(calculateFPS) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(calculatePercentMaxRate) [list source [file join $dir Generic utilWrappers.tcl]] +set auto_index(vlanUtilsSetDefault) [list source [file join $dir Generic vlanUtils.tcl]] +set auto_index(::vlanUtils::setPortTagged) [list source [file join $dir Generic vlanUtils.tcl]] +set auto_index(::vlanUtils::setTagged) [list source [file join $dir Generic vlanUtils.tcl]] +set auto_index(::vlanUtils::setPortUntagged) [list source [file join $dir Generic vlanUtils.tcl]] +set auto_index(::vlanUtils::setUntagged) [list source [file join $dir Generic vlanUtils.tcl]] +set auto_index(::vlanUtils::isPortTagged) [list source [file join $dir Generic vlanUtils.tcl]] +set auto_index(::vlanUtils::emptyUntaggedPortList) [list source [file join $dir Generic vlanUtils.tcl]] +set auto_index(setPortTagged) [list source [file join $dir Generic vlanUtils.tcl]] +set auto_index(setTagged) [list source [file join $dir Generic vlanUtils.tcl]] +set auto_index(setPortUntagged) [list source [file join $dir Generic vlanUtils.tcl]] +set auto_index(setUntagged) [list source [file join $dir Generic vlanUtils.tcl]] +set auto_index(isPortTagged) [list source [file join $dir Generic vlanUtils.tcl]] +set auto_index(getUntaggedPortList) [list source [file join $dir Generic vlanUtils.tcl]] +set auto_index(emptyUntaggedPortList) [list source [file join $dir Generic vlanUtils.tcl]] +set auto_index(::ixXML::Init) [list source [file join $dir Generic xmlUtils.tcl]] +set auto_index(::ixXML::NextToken) [list source [file join $dir Generic xmlUtils.tcl]] +set auto_index(::ixXML::IsWellFormed) [list source [file join $dir Generic xmlUtils.tcl]] +set auto_index(::ixXML::_IsWellFormed) [list source [file join $dir Generic xmlUtils.tcl]] diff --git a/dep/tgen.tgz b/dep/tgen.tgz deleted file mode 100644 index 5b242a1ec15db3948b09456e9a8736c93ac337f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 134392 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[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

3jcAv)Mf-e0 zw9h9*bCOf32D!x1kJWixTfW{CZWrp>{@az|p6xy6hO%h_=s7KH_|u+BF%&hYu$7se zN6emZml%|kg>MxjzsmeD?p$Py&)!F-u#SS+qjqC_Mwgkt@BN+`p9#|TcC)j-a)x

mrpS;kmhw$5VgnQ2%4 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 0 ) then + return i; + end + for k in pairs(t) do + i = i + 1; + end + end + return i; +end + +-- =========================================================================== +-- returns the 'basename' and the 'basedir' +-- +function basename(filename) + local fn, dn; + + -- Convert the '\' to '/' in the path name. + filename = d2u(filename); + + fn = gsub(filename, "(.*/)(.*)", "%2") or filename; + dn = gsub(filename, "(.*/)(.*)", "%1") + + dn = strsub(dn, 1, -2); + + return fn, dn; +end + +-- =========================================================================== +-- Default routine to read data from syscmd function. +-- +local function __doRead(fn) + local data, f; + + -- Open and read all of the data. + f = assert(io.open(fn)); + data = f:read("*all"); + f:close(); + + unlink(fn); + + return data; +end + +-- =========================================================================== +-- Execute the system command return the command output if needed. +-- +function syscmd( cmd, funcPtr ) + local tmpFile = "syscmd_tmp"; + + system( cmd .. " > " .. tmpFile ); + + funcPtr = funcPtr or __doRead; + + return funcPtr(tmpFile); -- tmpFile is removed by the function. +end + +-- =========================================================================== +-- Execute the string and return true/false. +-- +function isTrue(f) + local s; + + if ( f == nil ) then + return 0; + end + + s = "if ( "..f.." ) then return 1 else return 0 end"; + return assert(loadstring(s))(); +end + +-- =========================================================================== +-- Output a message and return. +-- +function msg(m, ...) + + if ( m ~= nil ) then io.write("++ "..strfmt(m, ...)); io.flush(); end +end +-- =========================================================================== +-- Display an error message and exit. +-- +function errmsg(m, ...) + + if ( m ~= nil ) then printf("** %s", strfmt(m, ...)); end + + os.exit(1); +end + +-- =========================================================================== +-- Output a 'C' like block comment. +-- +function cPrintf(m, ...) + + printf("/* "); + io.write(strfmt(m, ...)); + printf(" */\n"); +end +-- =========================================================================== +-- Output a 'C' like comment. +-- +function comment(msg) + + printf("/* %s */\n", msg or "ooops"); +end + +-- Standard set of functions for normal operation. +-- + +----------------------------------------------------------------------------- +-- serializeIt - Convert a variable to text or its type of variable. +-- +local function serializeIt(v) + local s; + local t = type(v); + + if (t == "number") then + s = tostring(v); + elseif (t == "table") then + s = tostring(v); + elseif (t == "string") then + s = strfmt("%q", v); + elseif (t == "boolean") then + s = tostring(v); + elseif (t == "function") then + s = strfmt("()"); + elseif (t == "userdata") then + s = tostring(v); + elseif (t == "nil") then + s = "nil"; + else + s = strfmt("<%s>", tostring(v)); + end + + return s; +end + +----------------------------------------------------------------------------- +-- Serialize a value +-- k - is the variable name string. +-- o - is the orignal variable name for tables. +-- v - the value of the variable. +-- saved - is the saved table to detect loops. +-- tab - is the current tab depth. +-- +local function doSerialize(k, o, v, saved, tab) + local s, t; + local space = function(t) return strrep(" ", t); end; + + tab = tab or 0; + t = type(v); + saved = saved or {}; + + if (t == "table") then + if ( saved[v] ~= nil ) then + return strfmt("%s[%s] = %s,\n", space(tab), serializeIt(o), saved[v]); + else + local kk, vv, mt; + + saved[v] = k; + + if ( tab == 0 ) then + s = strfmt("%s%s = {\n", space(tab), tostring(k)); + else + s = strfmt("%s[%s] = {\n", space(tab), serializeIt(o)); + end + for kk,vv in pairs(v) do + local fn = strfmt("%s[%s]", tostring(k), serializeIt(kk)); + + s = s .. doSerialize(fn, kk, vv, saved, tab+2); + end + + if ( tab == 0 ) then + return s .. strfmt("%s}\n", space(tab)); + else + return s .. strfmt("%s},\n", space(tab)); + end + end + else + return strfmt("%s[%s] = %s,\n", space(tab), serializeIt(o), serializeIt(v)); + end +end + +----------------------------------------------------------------------------- +-- serialize - For a given key serialize the global variable. +-- k is a string for display and t is the table to display. +-- e.g. printf(serialize("foo", { ["bar"] = "foobar" } )); +-- +function serialize(k, t) + + if ( k == nil ) then + k = "Globals"; + t = _G; -- Dump out globals + end + if ( t == nil ) then + t = _G; + end + + return doSerialize(k, k, t, {}, 0); +end + +function prints(k, t) io.write(serialize(k, t)); io.flush(); end +function sleep(t) pktgen.delay(t * 1000); end diff --git a/dep/tgen/PktgenGUI.lua b/dep/tgen/PktgenGUI.lua new file mode 100644 index 00000000..1ce542ab --- /dev/null +++ b/dep/tgen/PktgenGUI.lua @@ -0,0 +1,113 @@ +-- +-- PktgenGUI.lua +-- + +gui = {} + +function gui.msg(...) + str = strfmt("msg=%s\n",strfmt(...)); + io.write(str); + io.flush(); +end + +function gui.dumpStats(func, name, portlist) + stats = func(portlist); + printf("%s={ ", name); + st = stats[0]; + for k,v in pairs(st) do + printf("%s ", k); + end + printf("},"); + for i = 0, (stats.n - 1) do + st = stats[i]; + printf("%d={ ", i); + for k,v in pairs(st) do + printf("%d ", v); + end + if ( i == (stats.n - 1) ) then + printf("}\n"); + else + printf("},"); + end + end +end + +function gui.dumpInfo(func, name, portlist) + stats = func(portlist); + printf("%s={ ", name); + st = stats[0]; + for k,v in pairs(st) do + printf("%s ", k); + end + printf("},"); + for i = 0, (stats.n - 1) do + st = stats[i]; + printf("%d={ ", i); + for k,v in pairs(st) do + printf("%s ", v); + end + if ( i == (stats.n - 1) ) then + printf("}\n"); + else + printf("},"); + end + end +end + +function gui.getPktStats(portlist) + gui.dumpStats(pktgen.pktStats, "pktStats", portlist) +end + +function gui.getPortStats(portlist) + gui.dumpStats(pktgen.portStats, "portStats", portlist) +end + +function gui.getRateStats(portlist) + gui.dumpStats(pktgen.rateStats, "rateStats", portlist) +end + +function gui.getPortSizes(portlist) + gui.dumpStats(pktgen.portSizes, "portSizes", portlist) +end + +function gui.getPortInfo(portlist) + gui.dumpInfo(pktgen.portInfo, "portInfo", portlist) +end + +function gui.getLinkState(portlist) + links = pktgen.linkState(portlist); + printf("linkState={ "); + for k,v in pairs(links) do + if ( k == "n" ) then break; end + printf("%d=%s ", k, v); + end + printf("}\n"); +end + +function gui.getPortFlags(portlist) + links = pktgen.portFlags(portlist); + printf("portFlags={ "); + for k,v in pairs(links) do + if ( k == "n" ) then break; end + printf("%d=%s ", k, v); + end + printf("}\n"); +end + +function gui.startTransmitting(portlist) + -- gui.msg("=== Start Transmit %s", portlist); + pktgen.start(portlist); +end + +function gui.stopTransmitting(portlist) + -- gui.msg("=== Stop Transmit %s", portlist); + pktgen.stop(portlist); +end + +function gui.clearAllStats() + pktgen.clear("all"); +end + +function gui.noop() + -- Do nothing +end diff --git a/dep/tgen/tgen b/dep/tgen/tgen new file mode 100755 index 0000000000000000000000000000000000000000..63fe0f631ef4602e7a210377c0dfa121cb478887 GIT binary patch literal 300314 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 RD7idIs}7jZ