aboutsummaryrefslogtreecommitdiffstats
path: root/samples/netperf.yaml
blob: a3df3f4d33f2897d0e89c5a44867371d647c4d5e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
##############################################################################
# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
---
# Sample benchmark task config file
# measure network latency and throughput using netperf
# There are two sample scenarios: bulk test and request/response test
# In bulk test, UDP_STREAM and TCP_STREAM can be used
# send_msg_size and recv_msg_size are options of bulk test
# In req/rsp test, TCP_RR TCP_CRR UDP_RR can be used
# req_rsp_size is option of req/rsp test

schema: "yardstick:task:0.1"

scenarios:
-
  type: Netperf
  options:
    testname: 'UDP_STREAM'
    send_msg_size: 1024
    duration: 20

  host: Chang'e.demo
  target: Houyi.demo

  runner:
    type: Iteration
    iterations: 1
    interval: 1
  sla:
    mean_latency: 100
    action: monitor
-
  type: Netperf
  options:
    testname: 'TCP_RR'
    req_rsp_size: '32,1024'

  host: Chang'e.demo
  target: Houyi.demo

  runner:
    type: Duration
    duration: 20
    interval: 1
  sla:
    mean_latency: 300
    action: monitor

context:
  name: demo
  image: yardstick-image
  flavor: yardstick-flavor
  user: ubuntu

  placement_groups:
    pgrp1:
      policy: "availability"

  servers:
    Chang'e:
      floating_ip: true
      placement: "pgrp1"
    Houyi:
      floating_ip: true
      placement: "pgrp1"

  networks:
    test:
      cidr: '10.0.1.0/24'
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 */ }
# 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.
"""TestCase base class
"""

import csv
import os
import time
import logging
import subprocess
import copy
from collections import OrderedDict

import core.component_factory as component_factory
from core.loader import Loader
from core.results.results_constants import ResultsConstants
from tools import tasks
from tools import hugepages
from tools.pkt_gen.trafficgen.trafficgenhelper import TRAFFIC_DEFAULTS
from conf import settings as S
from conf import get_test_param

class TestCase(object):
    """TestCase base class

    In this basic form runs RFC2544 throughput test
    """
    def __init__(self, cfg, results_dir):
        """Pull out fields from test config

        :param cfg: A dictionary of string-value pairs describing the test
            configuration. Both the key and values strings use well-known
            values.
        :param results_dir: Where the csv formatted results are written.
        """
        self._hugepages_mounted = False
        self._traffic_ctl = None
        self._vnf_ctl = None
        self._vswitch_ctl = None
        self._collector = None
        self._loadgen = None
        self._output_file = None
        self._tc_results = None

        # set test parameters; CLI options take precedence to testcase settings
        self._logger = logging.getLogger(__name__)
        self.name = cfg['Name']
        self.desc = cfg.get('Description', 'No description given.')
        self.test = cfg.get('TestSteps', None)

        bidirectional = cfg.get('biDirectional', TRAFFIC_DEFAULTS['bidir'])
        bidirectional = get_test_param('bidirectional', bidirectional)

        traffic_type = cfg.get('Traffic Type', TRAFFIC_DEFAULTS['traffic_type'])
        traffic_type = get_test_param('traffic_type', traffic_type)

        framerate = cfg.get('iLoad', TRAFFIC_DEFAULTS['frame_rate'])
        framerate = get_test_param('iload', framerate)

        self.deployment = cfg['Deployment']
        self._frame_mod = cfg.get('Frame Modification', None)

        self._tunnel_type = None
        self._tunnel_operation = None

        if self.deployment == 'op2p':
            self._tunnel_operation = cfg['Tunnel Operation']

            if 'Tunnel Type' in cfg:
                self._tunnel_type = cfg['Tunnel Type']
                self._tunnel_type = get_test_param('tunnel_type',
                                                   self._tunnel_type)


        # identify guest loopback method, so it can be added into reports
        self.guest_loopback = []
        if self.deployment in ['pvp', 'pvvp']:
            guest_loopback = get_test_param('guest_loopback', None)
            if guest_loopback:
                self.guest_loopback.append(guest_loopback)
            else:
                if self.deployment == 'pvp':
                    self.guest_loopback.append(S.getValue('GUEST_LOOPBACK')[0])
                else:
                    self.guest_loopback = S.getValue('GUEST_LOOPBACK').copy()

        # read configuration of streams; CLI parameter takes precedence to
        # testcase definition
        multistream = cfg.get('MultiStream', TRAFFIC_DEFAULTS['multistream'])
        multistream = get_test_param('multistream', multistream)
        stream_type = cfg.get('Stream Type', TRAFFIC_DEFAULTS['stream_type'])
        stream_type = get_test_param('stream_type', stream_type)
        pre_installed_flows = cfg.get('Pre-installed Flows', TRAFFIC_DEFAULTS['pre_installed_flows'])
        pre_installed_flows = get_test_param('pre-installed_flows', pre_installed_flows)

        # check if test requires background load and which generator it uses
        self._load_cfg = cfg.get('Load', None)
        if self._load_cfg and 'tool' in self._load_cfg:
            self._loadgen = self._load_cfg['tool']
        else:
            # background load is not requested, so use dummy implementation
            self._loadgen = "Dummy"

        if self._frame_mod:
            self._frame_mod = self._frame_mod.lower()
        self._results_dir = results_dir

        # set traffic details, so they can be passed to vswitch and traffic ctls
        self._traffic = copy.deepcopy(TRAFFIC_DEFAULTS)
        self._traffic.update({'traffic_type': traffic_type,
                              'flow_type': cfg.get('Flow Type', TRAFFIC_DEFAULTS['flow_type']),
                              'bidir': bidirectional,
                              'tunnel_type': self._tunnel_type,
                              'multistream': int(multistream),
                              'stream_type': stream_type,
                              'pre_installed_flows' : pre_installed_flows,
                              'frame_rate': int(framerate)})

        # OVS Vanilla requires guest VM MAC address and IPs to work
        if 'linux_bridge' in self.guest_loopback:
            self._traffic['l2'].update({'srcmac': S.getValue('GUEST_NET2_MAC')[0],
                                        'dstmac': S.getValue('GUEST_NET1_MAC')[0]})
            self._traffic['l3'].update({'srcip': S.getValue('VANILLA_TGEN_PORT1_IP'),
                                        'dstip': S.getValue('VANILLA_TGEN_PORT2_IP')})

        # Packet Forwarding mode
        self._vswitch_none = 'none' == S.getValue('VSWITCH').strip().lower()

    def run_initialize(self):
        """ Prepare test execution environment
        """
        self._logger.debug(self.name)

        # mount hugepages if needed
        self._mount_hugepages()

        # copy sources of l2 forwarding tools into VM shared dir if needed
        self._copy_fwd_tools_for_guest()

        if self.deployment == "op2p":
            self._traffic['l2'].update({'srcmac':
                                        S.getValue('TRAFFICGEN_PORT1_MAC'),
                                        'dstmac':
                                        S.getValue('TRAFFICGEN_PORT2_MAC')})

            self._traffic['l3'].update({'srcip':
                                        S.getValue('TRAFFICGEN_PORT1_IP'),
                                        'dstip':
                                        S.getValue('TRAFFICGEN_PORT2_IP')})

            if self._tunnel_operation == "decapsulation":
                self._traffic['l2'] = S.getValue(self._tunnel_type.upper() + '_FRAME_L2')
                self._traffic['l3'] = S.getValue(self._tunnel_type.upper() + '_FRAME_L3')
                self._traffic['l4'] = S.getValue(self._tunnel_type.upper() + '_FRAME_L4')


        self._logger.debug("Controllers:")
        loader = Loader()
        self._traffic_ctl = component_factory.create_traffic(
            self._traffic['traffic_type'],
            loader.get_trafficgen_class())

        self._vnf_ctl = component_factory.create_vnf(
            self.deployment,
            loader.get_vnf_class())

        if self._vswitch_none:
            self._vswitch_ctl = component_factory.create_pktfwd(
                loader.get_pktfwd_class())
        else:
            self._vswitch_ctl = component_factory.create_vswitch(
                self.deployment,
                loader.get_vswitch_class(),
                self._traffic,
                self._tunnel_operation)

        self._collector = component_factory.create_collector(
            loader.get_collector_class(),
            self._results_dir, self.name)
        self._loadgen = component_factory.create_loadgen(
            self._loadgen,
            self._load_cfg)

        self._output_file = os.path.join(self._results_dir, "result_" + self.name +
                                         "_" + self.deployment + ".csv")

        self._logger.debug("Setup:")

    def run_finalize(self):
        """ Tear down test execution environment and record test results
        """
        # umount hugepages if mounted
        self._umount_hugepages()

    def run_report(self):
        """ Report test results
        """
        self._logger.debug("self._collector Results:")
        self._collector.print_results()

        if S.getValue('mode') != 'trafficgen-off':
            self._logger.debug("Traffic Results:")
            self._traffic_ctl.print_results()

            self._tc_results = self._append_results(self._traffic_ctl.get_results())
            TestCase._write_result_to_file(self._tc_results, self._output_file)

    def run(self):
        """Run the test

        All setup and teardown through controllers is included.
        """
        # prepare test execution environment
        self.run_initialize()

        with self._vswitch_ctl, self._loadgen:
            with self._vnf_ctl, self._collector:
                if not self._vswitch_none:
                    self._add_flows()

                # run traffic generator if requested, otherwise wait for manual termination
                if S.getValue('mode') == 'trafficgen-off':
                    time.sleep(2)
                    self._logger.debug("All is set. Please run traffic generator manually.")
                    input(os.linesep + "Press Enter to terminate vswitchperf..." + os.linesep + os.linesep)
                else:
                    if S.getValue('mode') == 'trafficgen-pause':
                        time.sleep(2)
                        true_vals = ('yes', 'y', 'ye', None)
                        while True:
                            choice = input(os.linesep + 'Transmission paused, should'
                                           ' transmission be resumed? ' + os.linesep).lower()
                            if not choice or choice not in true_vals:
                                print('Please respond with \'yes\' or \'y\' ', end='')
                            else:
                                break
                    with self._traffic_ctl:
                        self._traffic_ctl.send_traffic(self._traffic)

                    # dump vswitch flows before they are affected by VNF termination
                    if not self._vswitch_none:
                        self._vswitch_ctl.dump_vswitch_flows()

        # tear down test execution environment and log results
        self.run_finalize()

        # report test results
        self.run_report()

    def _append_results(self, results):
        """
        Method appends mandatory Test Case results to list of dictionaries.

        :param results: list of dictionaries which contains results from
                traffic generator.

        :returns: modified list of dictionaries.
        """
        for item in results:
            item[ResultsConstants.ID] = self.name
            item[ResultsConstants.DEPLOYMENT] = self.deployment
            item[ResultsConstants.TRAFFIC_TYPE] = self._traffic['l3']['proto']
            if self._traffic['multistream']:
                item[ResultsConstants.SCAL_STREAM_COUNT] = self._traffic['multistream']
                item[ResultsConstants.SCAL_STREAM_TYPE] = self._traffic['stream_type']
                item[ResultsConstants.SCAL_PRE_INSTALLED_FLOWS] = self._traffic['pre_installed_flows']
            if len(self.guest_loopback):
                item[ResultsConstants.GUEST_LOOPBACK] = ' '.join(self.guest_loopback)
            if self._tunnel_type:
                item[ResultsConstants.TUNNEL_TYPE] = self._tunnel_type
        return results

    def _copy_fwd_tools_for_guest(self):
        """Copy dpdk and l2fwd code to GUEST_SHARE_DIR[s] for use by guests.
        """
        counter = 0
        # method is executed only for pvp and pvvp, so let's count number of 'v'
        while counter < self.deployment.count('v'):
            guest_dir = S.getValue('GUEST_SHARE_DIR')[counter]

            # remove shared dir if it exists to avoid issues with file consistency
            if os.path.exists(guest_dir):
                tasks.run_task(['rm', '-f', '-r', guest_dir], self._logger,
                               'Removing content of shared directory...', True)

            # directory to share files between host and guest
            os.makedirs(guest_dir)

            # copy sources into shared dir only if neccessary
            if 'testpmd' in self.guest_loopback or 'l2fwd' in self.guest_loopback:
                try:
                    tasks.run_task(['rsync', '-a', '-r', '-l', r'--exclude="\.git"',
                                    os.path.join(S.getValue('RTE_SDK'), ''),
                                    os.path.join(guest_dir, 'DPDK')],
                                   self._logger,
                                   'Copying DPDK to shared directory...',
                                   True)
                    tasks.run_task(['rsync', '-a', '-r', '-l',
                                    os.path.join(S.getValue('ROOT_DIR'), 'src/l2fwd/'),
                                    os.path.join(guest_dir, 'l2fwd')],
                                   self._logger,
                                   'Copying l2fwd to shared directory...',
                                   True)
                except subprocess.CalledProcessError:
                    self._logger.error('Unable to copy DPDK and l2fwd to shared directory')

            counter += 1

    def _mount_hugepages(self):
        """Mount hugepages if usage of DPDK or Qemu is detected
        """
        # hugepages are needed by DPDK and Qemu
        if not self._hugepages_mounted and \
            (self.deployment.count('v') or \
             S.getValue('VSWITCH').lower().count('dpdk') or \
             self._vswitch_none):
            hugepages.mount_hugepages()
            self._hugepages_mounted = True

    def _umount_hugepages(self):
        """Umount hugepages if they were mounted before
        """
        if self._hugepages_mounted:
            hugepages.umount_hugepages()
            self._hugepages_mounted = False

    @staticmethod
    def _write_result_to_file(results, output):
        """Write list of dictionaries to a CSV file.

        Each element on list will create separate row in output file.
        If output file already exists, data will be appended at the end,
        otherwise it will be created.

        :param results: list of dictionaries.
        :param output: path to output file.
        """
        with open(output, 'a') as csvfile:

            logging.info("Write results to file: " + output)
            fieldnames = TestCase._get_unique_keys(results)

            writer = csv.DictWriter(csvfile, fieldnames)

            if not csvfile.tell():  # file is now empty
                writer.writeheader()

            for result in results:
                writer.writerow(result)

    @staticmethod
    def _get_unique_keys(list_of_dicts):
        """Gets unique key values as ordered list of strings in given dicts

        :param list_of_dicts: list of dictionaries.

        :returns: list of unique keys(strings).
        """
        result = OrderedDict()
        for item in list_of_dicts:
            for key in item.keys():
                result[key] = ''

        return list(result.keys())

    def _add_flows(self):
        """Add flows to the vswitch
        """
        vswitch = self._vswitch_ctl.get_vswitch()
        # TODO BOM 15-08-07 the frame mod code assumes that the
        # physical ports are ports 1 & 2. The actual numbers
        # need to be retrived from the vSwitch and the metadata value
        # updated accordingly.
        bridge = S.getValue('VSWITCH_BRIDGE_NAME')
        if self._frame_mod == "vlan":
            # 0x8100 => VLAN ethertype
            self._logger.debug(" ****   VLAN   ***** ")
            flow = {'table':'2', 'priority':'1000', 'metadata':'2',
                    'actions': ['push_vlan:0x8100', 'goto_table:3']}
            vswitch.add_flow(bridge, flow)
            flow = {'table':'2', 'priority':'1000', 'metadata':'1',
                    'actions': ['push_vlan:0x8100', 'goto_table:3']}
            vswitch.add_flow(bridge, flow)
        elif self._frame_mod == "mpls":
            # 0x8847 => MPLS unicast ethertype
            self._logger.debug(" ****   MPLS  ***** ")
            flow = {'table':'2', 'priority':'1000', 'metadata':'2',
                    'actions': ['push_mpls:0x8847', 'goto_table:3']}
            vswitch.add_flow(bridge, flow)
            flow = {'table':'2', 'priority':'1000', 'metadata':'1',
                    'actions': ['push_mpls:0x8847', 'goto_table:3']}
            vswitch.add_flow(bridge, flow)
        elif self._frame_mod == "mac":
            flow = {'table':'2', 'priority':'1000', 'metadata':'2',
                    'actions': ['mod_dl_src:22:22:22:22:22:22',
                                'goto_table:3']}
            vswitch.add_flow(bridge, flow)
            flow = {'table':'2', 'priority':'1000', 'metadata':'1',
                    'actions': ['mod_dl_src:11:11:11:11:11:11',
                                'goto_table:3']}
            vswitch.add_flow(bridge, flow)
        elif self._frame_mod == "dscp":
            # DSCP 184d == 0x4E<<2 => 'Expedited Forwarding'
            flow = {'table':'2', 'priority':'1000', 'metadata':'2',
                    'dl_type':'0x0800',
                    'actions': ['mod_nw_tos:184', 'goto_table:3']}
            vswitch.add_flow(bridge, flow)
            flow = {'table':'2', 'priority':'1000', 'metadata':'1',
                    'dl_type':'0x0800',
                    'actions': ['mod_nw_tos:184', 'goto_table:3']}
            vswitch.add_flow(bridge, flow)
        elif self._frame_mod == "ttl":
            # 251 and 241 are the highest prime numbers < 255
            flow = {'table':'2', 'priority':'1000', 'metadata':'2',
                    'dl_type':'0x0800',
                    'actions': ['mod_nw_ttl:251', 'goto_table:3']}
            vswitch.add_flow(bridge, flow)
            flow = {'table':'2', 'priority':'1000', 'metadata':'1',
                    'dl_type':'0x0800',
                    'actions': ['mod_nw_ttl:241', 'goto_table:3']}
            vswitch.add_flow(bridge, flow)
        elif self._frame_mod == "ip_addr":
            flow = {'table':'2', 'priority':'1000', 'metadata':'2',
                    'dl_type':'0x0800',
                    'actions': ['mod_nw_src:10.10.10.10',
                                'mod_nw_dst:20.20.20.20',
                                'goto_table:3']}
            vswitch.add_flow(bridge, flow)
            flow = {'table':'2', 'priority':'1000', 'metadata':'1',
                    'dl_type':'0x0800',
                    'actions': ['mod_nw_src:20.20.20.20',
                                'mod_nw_dst:10.10.10.10',
                                'goto_table:3']}
            vswitch.add_flow(bridge, flow)
        elif self._frame_mod == "ip_port":
            # TODO BOM 15-08-27 The traffic generated is assumed
            # to be UDP (nw_proto 17d) which is the default case but
            # we will need to pick up the actual traffic params in use.
            flow = {'table':'2', 'priority':'1000', 'metadata':'2',
                    'dl_type':'0x0800', 'nw_proto':'17',
                    'actions': ['mod_tp_src:44444',
                                'mod_tp_dst:44444', 'goto_table:3']}
            vswitch.add_flow(bridge, flow)
            flow = {'table':'2', 'priority':'1000', 'metadata':'1',
                    'dl_type':'0x0800', 'nw_proto':'17',
                    'actions': ['mod_tp_src:44444',
                                'mod_tp_dst:44444', 'goto_table:3']}
            vswitch.add_flow(bridge, flow)
        else:
            pass