aboutsummaryrefslogtreecommitdiffstats
path: root/tools/pkt_gen/ixia/ixia.py
blob: ae5da6d24e9a0eb3e4f2dc9998737a6919907987 (plain) @media only all and (prefers-color-scheme: dark) { .highlight .hll { background-color: #49483e } .highlight .c { color: #75715e } /* Comment */ .highlight .err { color: #960050; background-color: #1e0010 } /* Error */ .highlight .k { color: #66d9ef } /* Keyword */ .highlight .l { color: #ae81ff } /* Literal */ .highlight .n { color: #f8f8f2 } /* Name */ .highlight .o { color: #f92672 } /* Operator */ .highlight .p { color: #f8f8f2 } /* Punctuation */ .highlight .ch { color: #75715e } /* Comment.Hashbang */ .highlight .cm { color: #75715e } /* Comment.Multiline */ .highlight .cp { color: #75715e } /* Comment.Preproc */ .highlight .cpf { color: #75715e } /* Comment.PreprocFile */ .highlight .c1 { color: #75715e } /* Comment.Single */ .highlight .cs { color: #75715e } /* Comment.Special */ .highlight .gd { color: #f92672 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gi { color: #a6e22e } /* Generic.Inserted */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #75715e } /* Generic.Subheading */ .highlight .kc { color: #66d9ef } /* Keyword.Constant */ .highlight .kd { color: #66d9ef } /* Keyword.Declaration */ .highlight .kn { color: #f92672 } /* Keyword.Namespace */ .highlight .kp { color: #66d9ef } /* Keyword.Pseudo */ .highlight .kr { color: #66d9ef } /* Keyword.Reserved */ .highlight .kt { color: #66d9ef } /* Keyword.Type */ .highlight .ld { color: #e6db74 } /* Literal.Date */ .highlight .m { color: #ae81ff } /* Literal.Number */ .highlight .s { color: #e6db74 } /* Literal.String */ .highlight .na { color: #a6e22e } /* Name.Attribute */ .highlight .nb { color: #f8f8f2 } /* Name.Builtin */ .highlight .nc { color: #a6e22e } /* Name.Class */ .highlight .no { color: #66d9ef } /* Name.Constant */ .highlight .nd { color: #a6e22e } /* Name.Decorator */ .highlight .ni { color: #f8f8f2 } /* Name.Entity */ .highlight .ne { color: #a6e22e } /* Name.Exception */ .highlight .nf { color: #a6e22e } /* Name.Function */ .highlight .nl { color: #f8f8f2 } /* Name.Label */ .highlight .nn { color: #f8f8f2 } /* Name.Namespace */ .highlight .nx { color: #a6e22e } /* Name.Other */ .highlight .py { color: #f8f8f2 } /* Name.Property */ .highlight .nt { color: #f92672 } /* Name.Tag */ .highlight .nv { color: #f8f8f2 } /* Name.Variable */ .highlight .ow { color: #f92672 } /* Operator.Word */ .highlight .w { color: #f8f8f2 } /* Text.Whitespace */ .highlight .mb { color: #ae81ff } /* Literal.Number.Bin */ .highlight .mf { color: #ae81ff } /* Literal.Number.Float */ .highlight .mh { color: #ae81ff } /* Literal.Number.Hex */ .highlight .mi { color: #ae81ff } /* Literal.Number.Integer */ .highlight .mo { color: #ae81ff } /* Literal.Number.Oct */ .highlight .sa { color: #e6db74 } /* Literal.String.Affix */ .highlight .sb { color: #e6db74 } /* Literal.String.Backtick */ .highlight .sc { color: #e6db74 } /* Literal.String.Char */ .highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */ .highlight .sd { color: #e6db74 } /* Literal.String.Doc */ .highlight .s2 { color: #e6db74 } /* Literal.String.Double */ .highlight .se { color: #ae81ff } /* Literal.String.Escape */ .highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */ .highlight .si { color: #e6db74 } /* Literal.String.Interpol */ .highlight .sx { color: #e6db74 } /* Literal.String.Other */ .highlight .sr { color: #e6db74 } /* Literal.String.Regex */ .highlight .s1 { color: #e6db74 } /* Literal.String.Single */ .highlight .ss { color: #e6db74 } /* Literal.String.Symbol */ .highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #a6e22e } /* Name.Function.Magic */ .highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */ .highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */ .highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */ .highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */ .highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */ } @media (prefers-color-scheme: light) { .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ }

a id='n307' href='#n307'>307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
# Copyright 2015-2016 Intel Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""IXIA traffic generator model.

Provides a model for the IXIA traffic generator. In addition, provides
a number of generic "helper" functions that are used to do the "heavy
lifting".

This requires the following settings in your config file:

* TRAFFICGEN_IXIA_LIB_PATH
    IXIA libraries path
* TRAFFICGEN_IXIA_HOST
    IXIA chassis IP address
* TRAFFICGEN_IXIA_CARD
    IXIA card
* TRAFFICGEN_IXIA_PORT1
    IXIA Tx port
* TRAFFICGEN_IXIA_PORT2
    IXIA Rx port

If any of these don't exist, the application will raise an exception
(EAFP).
"""

import tkinter
import logging
import os

from tools.pkt_gen import trafficgen
from conf import settings
from collections import OrderedDict
from core.results.results_constants import ResultsConstants

_ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
_IXIA_ROOT_DIR = settings.getValue('TRAFFICGEN_IXIA_ROOT_DIR')


def configure_env():
    """Configure envionment for TCL.

    """
    os.environ['IXIA_HOME'] = _IXIA_ROOT_DIR

    # USER MAY NEED TO CHANGE THESE IF USING OWN TCL LIBRARY
    os.environ['TCL_HOME'] = _IXIA_ROOT_DIR
    os.environ['TCLver'] = '8.5'

    # USER NORMALLY DOES NOT CHANGE ANY LINES BELOW
    os.environ['IxiaLibPath'] = os.path.expandvars('$IXIA_HOME/lib')
    os.environ['IxiaBinPath'] = os.path.expandvars('$IXIA_HOME/bin')

    os.environ['TCLLibPath'] = os.path.expandvars('$TCL_HOME/lib')
    os.environ['TCLBinPath'] = os.path.expandvars('$TCL_HOME/bin')

    os.environ['TCL_LIBRARY'] = os.path.expandvars('$TCLLibPath/tcl$TCLver')
    os.environ['TK_LIBRARY'] = os.path.expandvars('$TCLLibPath/tk$TCLver')

    os.environ['PATH'] = os.path.expandvars('$IxiaBinPath:.:$TCLBinPath:$PATH')
    os.environ['TCLLIBPATH'] = os.path.expandvars('$IxiaLibPath')
    os.environ['LD_LIBRARY_PATH'] = os.path.expandvars(
        '$IxiaLibPath:$TCLLibPath:$LD_LIBRARY_PATH')

    os.environ['IXIA_RESULTS_DIR'] = '/tmp/Ixia/Results'
    os.environ['IXIA_LOGS_DIR'] = '/tmp/Ixia/Logs'
    os.environ['IXIA_TCL_DIR'] = os.path.expandvars('$IxiaLibPath')
    os.environ['IXIA_SAMPLES'] = os.path.expandvars('$IxiaLibPath/ixTcl1.0')
    os.environ['IXIA_VERSION'] = '6.60.1000.11'


def _build_set_cmds(values, prefix='dict set'):
    """Generate a list of 'dict set' args for Tcl.

    Parse a dictionary and recursively build the arguments for the
    'dict set' Tcl command, given that this is of the format:

        dict set [name...] [key] [value]

    For example, for a non-nested dict (i.e. a non-dict element):

        dict set mydict mykey myvalue

    For a nested dict (i.e. a dict element):

        dict set mydict mysubdict mykey myvalue

    :param values: Dictionary to yield values for
    :param prefix: Prefix to append to output string. Generally the
        already generated part of the command.

    :yields: Output strings to be passed to a `Tcl` instance.
    """
    for key in values:
        value = values[key]

        # Not allowing derived dictionary types for now
        # pylint: disable=unidiomatic-typecheck
        if type(value) == dict:
            _prefix = ' '.join([prefix, key]).strip()
            for subkey in _build_set_cmds(value, _prefix):
                yield subkey
            continue

        # tcl doesn't recognise the strings "True" or "False", only "1"
        # or "0". Special case to convert them
        if type(value) == bool:
            value = str(int(value))
        else:
            value = str(value)

        if prefix:
            yield ' '.join([prefix, key, value]).strip()
        else:
            yield ' '.join([key, value]).strip()


class Ixia(trafficgen.ITrafficGenerator):
    """A wrapper around the IXIA traffic generator.

    Runs different traffic generator tests through an Ixia traffic
    generator chassis by generating TCL scripts from templates.
    """
    _script = os.path.join(os.path.dirname(__file__), 'pass_fail.tcl')
    _tclsh = tkinter.Tcl()
    _logger = logging.getLogger(__name__)

    def run_tcl(self, cmd):
        """Run a TCL script using the TCL interpreter found in ``tkinter``.

        :param cmd: Command to execute

        :returns: Output of command, where applicable.
        """
        self._logger.debug('%s%s', trafficgen.CMD_PREFIX, cmd)

        output = self._tclsh.eval(cmd)

        return output.split()

    def connect(self):
        """Connect to Ixia chassis.
        """
        ixia_cfg = {
            'lib_path': os.path.join(_IXIA_ROOT_DIR, 'lib', 'ixTcl1.0'),
            'host': settings.getValue('TRAFFICGEN_IXIA_HOST'),
            'card': settings.getValue('TRAFFICGEN_IXIA_CARD'),
            'port1': settings.getValue('TRAFFICGEN_IXIA_PORT1'),
            'port2': settings.getValue('TRAFFICGEN_IXIA_PORT2'),
        }

        self._logger.info('Connecting to IXIA...')

        self._logger.debug('IXIA configuration configuration : %s', ixia_cfg)

        configure_env()

        for cmd in _build_set_cmds(ixia_cfg, prefix='set'):
            self.run_tcl(cmd)

        output = self.run_tcl('source {%s}' % self._script)
        if output:
            self._logger.critical(
                'An error occured when connecting to IXIA...')
            raise RuntimeError('Ixia failed to initialise.')

        self._logger.info('Connected to IXIA...')

        return self

    def disconnect(self):
        """Disconnect from Ixia chassis.
        """
        self._logger.info('Disconnecting from IXIA...')

        self.run_tcl('cleanUp')

        self._logger.info('Disconnected from IXIA...')

    def _send_traffic(self, flow, traffic):
        """Send regular traffic.

        :param flow: Flow specification
        :param traffic: Traffic specification

        :returns: Results from IXIA
        """
        params = {}

        params['flow'] = flow
        params['traffic'] = self.traffic_defaults.copy()

        if traffic:
            params['traffic'] = trafficgen.merge_spec(
                params['traffic'], traffic)

        for cmd in _build_set_cmds(params):
            self.run_tcl(cmd)

        result = self.run_tcl('sendTraffic $flow $traffic')

        return result

    def send_burst_traffic(self, traffic=None, numpkts=100, duration=20):
        """See ITrafficGenerator for description
        """
        flow = {
            'numpkts': numpkts,
            'duration': duration,
            'type': 'stopStream',
            'framerate': traffic['frame_rate'],
        }

        result = self._send_traffic(flow, traffic)

        assert len(result) == 6  # fail-fast if underlying Tcl code changes

        #TODO - implement Burst results setting via TrafficgenResults.

    def send_cont_traffic(self, traffic=None, duration=30):
        """See ITrafficGenerator for description
        """
        flow = {
            'numpkts': 100,
            'duration': duration,
            'type': 'contPacket',
            'framerate': traffic['frame_rate'],
            'multipleStreams': traffic['multistream'],
        }

        result = self._send_traffic(flow, traffic)

        return Ixia._create_result(result)

    def start_cont_traffic(self, traffic=None, duration=30):
        """See ITrafficGenerator for description
        """
        return self.send_cont_traffic(traffic, 0)

    def stop_cont_traffic(self):
        """See ITrafficGenerator for description
        """
        return self.run_tcl('stopTraffic')

    def send_rfc2544_throughput(self, traffic=None, trials=3, duration=20, lossrate=0.0):
        """See ITrafficGenerator for description
        """
        params = {}

        params['config'] = {
            'trials': trials,
            'duration': duration,
            'lossrate': lossrate,
            'multipleStreams': traffic['multistream'],
        }
        params['traffic'] = self.traffic_defaults.copy()

        if traffic:
            params['traffic'] = trafficgen.merge_spec(
                params['traffic'], traffic)

        for cmd in _build_set_cmds(params):
            self.run_tcl(cmd)

        # this will return a list with one result
        result = self.run_tcl('rfcThroughputTest $config $traffic')

        return Ixia._create_result(result)

    @staticmethod
    def _create_result(result):
        """Create result based on list returned from tcl script.

        :param result: list representing output from tcl script.

        :returns: dictionary strings representing results from
                    traffic generator.
        """
        assert len(result) == 8  # fail-fast if underlying Tcl code changes

        if float(result[0]) == 0:
            loss_rate = 100
        else:
            loss_rate = (float(result[0]) - float(result[1])) / float(result[0]) * 100
        result_dict = OrderedDict()
        # drop the first 4 elements as we don't use/need them. In
        # addition, IxExplorer does not support latency or % line rate
        # metrics so we have to return dummy values for these metrics
        result_dict[ResultsConstants.THROUGHPUT_RX_FPS] = result[4]
        result_dict[ResultsConstants.TX_RATE_FPS] = result[5]
        result_dict[ResultsConstants.THROUGHPUT_RX_MBPS] = result[6]
        result_dict[ResultsConstants.TX_RATE_MBPS] = result[7]
        result_dict[ResultsConstants.FRAME_LOSS_PERCENT] = loss_rate
        result_dict[ResultsConstants.TX_RATE_PERCENT] = \
                                            ResultsConstants.UNKNOWN_VALUE
        result_dict[ResultsConstants.THROUGHPUT_RX_PERCENT] = \
                                            ResultsConstants.UNKNOWN_VALUE
        result_dict[ResultsConstants.MIN_LATENCY_NS] = \
                                            ResultsConstants.UNKNOWN_VALUE
        result_dict[ResultsConstants.MAX_LATENCY_NS] = \
                                            ResultsConstants.UNKNOWN_VALUE
        result_dict[ResultsConstants.AVG_LATENCY_NS] = \
                                            ResultsConstants.UNKNOWN_VALUE

        return result_dict

if __name__ == '__main__':
    TRAFFIC = {
        'l3': {
            'proto': 'udp',
            'srcip': '10.1.1.1',
            'dstip': '10.1.1.254',
        },
    }

    with Ixia() as dev:
        print(dev.send_burst_traffic(traffic=TRAFFIC))
        print(dev.send_cont_traffic(traffic=TRAFFIC))
        print(dev.send_rfc2544_throughput(traffic=TRAFFIC))