1 # Copyright 2015 Intel Corporation.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 """TestCase base class
20 from collections import OrderedDict
22 from core.results.results_constants import ResultsConstants
23 import core.component_factory as component_factory
24 from core.loader import Loader
25 from tools.report import report
27 class TestCase(object):
28 """TestCase base class
30 In this basic form runs RFC2544 throughput test
32 def __init__(self, cfg, results_dir):
33 """Pull out fields from test config
35 :param cfg: A dictionary of string-value pairs describing the test
36 configuration. Both the key and values strings use well-known
38 :param results_dir: Where the csv formatted results are written.
40 self._logger = logging.getLogger(__name__)
41 self.name = cfg['Name']
42 self.desc = cfg.get('Description', 'No description given.')
43 self._traffic_type = cfg['Traffic Type']
44 self.deployment = cfg['Deployment']
45 self._bidir = cfg['biDirectional']
46 self._frame_mod = cfg.get('Frame Modification', None)
48 # check if test requires background load and which generator it uses
49 self._load_cfg = cfg.get('Load', None)
50 if self._load_cfg and 'tool' in self._load_cfg:
51 self._loadgen = self._load_cfg['tool']
53 # background load is not requested, so use dummy implementation
54 self._loadgen = "Dummy"
57 self._frame_mod = self._frame_mod.lower()
58 self._results_dir = results_dir
59 self._multistream = cfg.get('MultiStream', 0)
64 All setup and teardown through controllers is included.
66 self._logger.debug(self.name)
68 self._logger.debug("Controllers:")
70 traffic_ctl = component_factory.create_traffic(
72 loader.get_trafficgen_class())
73 vnf_ctl = component_factory.create_vnf(
75 loader.get_vnf_class())
76 vswitch_ctl = component_factory.create_vswitch(
78 loader.get_vswitch_class(),
80 collector = component_factory.create_collector(
81 loader.get_collector_class(),
82 self._results_dir, self.name)
83 loadgen = component_factory.create_loadgen(
87 self._logger.debug("Setup:")
88 with vswitch_ctl, loadgen:
89 with vnf_ctl, collector:
90 traffic = {'traffic_type': self._traffic_type,
92 'multistream': self._multistream}
94 vswitch = vswitch_ctl.get_vswitch()
95 # TODO BOM 15-08-07 the frame mod code assumes that the
96 # physical ports are ports 1 & 2. The actual numbers
97 # need to be retrived from the vSwitch and the metadata value
98 # updated accordingly.
99 if self._frame_mod == "vlan":
100 # 0x8100 => VLAN ethertype
101 self._logger.debug(" **** VLAN ***** ")
102 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
103 'actions': ['push_vlan:0x8100', 'goto_table:3']}
104 vswitch.add_flow('br0', flow)
105 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
106 'actions': ['push_vlan:0x8100', 'goto_table:3']}
107 vswitch.add_flow('br0', flow)
108 elif self._frame_mod == "mpls":
109 # 0x8847 => MPLS unicast ethertype
110 self._logger.debug(" **** MPLS ***** ")
111 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
112 'actions': ['push_mpls:0x8847', 'goto_table:3']}
113 vswitch.add_flow('br0', flow)
114 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
115 'actions': ['push_mpls:0x8847', 'goto_table:3']}
116 vswitch.add_flow('br0', flow)
117 elif self._frame_mod == "mac":
118 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
119 'actions': ['mod_dl_src:22:22:22:22:22:22', 'goto_table:3']}
120 vswitch.add_flow('br0', flow)
121 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
122 'actions': ['mod_dl_src:11:11:11:11:11:11', 'goto_table:3']}
123 vswitch.add_flow('br0', flow)
124 elif self._frame_mod == "dscp":
125 # DSCP 184d == 0x4E<<2 => 'Expedited Forwarding'
126 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
128 'actions': ['mod_nw_tos:184', 'goto_table:3']}
129 vswitch.add_flow('br0', flow)
130 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
132 'actions': ['mod_nw_tos:184', 'goto_table:3']}
133 vswitch.add_flow('br0', flow)
134 elif self._frame_mod == "ttl":
135 # 251 and 241 are the highest prime numbers < 255
136 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
138 'actions': ['mod_nw_ttl:251', 'goto_table:3']}
139 vswitch.add_flow('br0', flow)
140 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
142 'actions': ['mod_nw_ttl:241', 'goto_table:3']}
143 vswitch.add_flow('br0', flow)
144 elif self._frame_mod == "ip_addr":
145 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
147 'actions': ['mod_nw_src:10.10.10.10',
148 'mod_nw_dst:20.20.20.20', 'goto_table:3']}
149 vswitch.add_flow('br0', flow)
150 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
152 'actions': ['mod_nw_src:20.20.20.20',
153 'mod_nw_dst:10.10.10.10', 'goto_table:3']}
154 vswitch.add_flow('br0', flow)
155 elif self._frame_mod == "ip_port":
156 # TODO BOM 15-08-27 The traffic generated is assumed
157 # to be UDP (nw_proto 17d) which is the default case but
158 # we will need to pick up the actual traffic params in use.
159 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
160 'dl_type':'0x0800', 'nw_proto':'17',
161 'actions': ['mod_tp_src:44444',
162 'mod_tp_dst:44444', 'goto_table:3']}
163 vswitch.add_flow('br0', flow)
164 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
165 'dl_type':'0x0800', 'nw_proto':'17',
166 'actions': ['mod_tp_src:44444',
167 'mod_tp_dst:44444', 'goto_table:3']}
168 vswitch.add_flow('br0', flow)
173 traffic_ctl.send_traffic(traffic)
175 self._logger.debug("Traffic Results:")
176 traffic_ctl.print_results()
178 self._logger.debug("Collector Results:")
179 collector.print_results()
181 output_file = os.path.join(self._results_dir, "result_" + self.name +
182 "_" + self.deployment + ".csv")
184 tc_results = self._append_results(traffic_ctl.get_results())
185 TestCase._write_result_to_file(tc_results, output_file)
187 report.generate(output_file, tc_results, collector.get_results())
189 def _append_results(self, results):
191 Method appends mandatory Test Case results to list of dictionaries.
193 :param results: list of dictionaries which contains results from
196 :returns: modified list of dictionaries.
199 item[ResultsConstants.ID] = self.name
200 item[ResultsConstants.DEPLOYMENT] = self.deployment
206 def _write_result_to_file(results, output):
207 """Write list of dictionaries to a CSV file.
209 Each element on list will create separate row in output file.
210 If output file already exists, data will be appended at the end,
211 otherwise it will be created.
213 :param results: list of dictionaries.
214 :param output: path to output file.
216 with open(output, 'a') as csvfile:
218 logging.info("Write results to file: " + output)
219 fieldnames = TestCase._get_unique_keys(results)
221 writer = csv.DictWriter(csvfile, fieldnames)
223 if not csvfile.tell(): # file is now empty
226 for result in results:
227 writer.writerow(result)
231 def _get_unique_keys(list_of_dicts):
232 """Gets unique key values as ordered list of strings in given dicts
234 :param list_of_dicts: list of dictionaries.
236 :returns: list of unique keys(strings).
238 result = OrderedDict()
239 for item in list_of_dicts:
240 for key in item.keys():
243 return list(result.keys())