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
26 from conf import settings as S
28 class TestCase(object):
29 """TestCase base class
31 In this basic form runs RFC2544 throughput test
33 def __init__(self, cfg, results_dir):
34 """Pull out fields from test config
36 :param cfg: A dictionary of string-value pairs describing the test
37 configuration. Both the key and values strings use well-known
39 :param results_dir: Where the csv formatted results are written.
41 self._logger = logging.getLogger(__name__)
42 self.name = cfg['Name']
43 self.desc = cfg.get('Description', 'No description given.')
44 self._traffic_type = cfg['Traffic Type']
45 self.deployment = cfg['Deployment']
46 self._bidir = cfg['biDirectional']
47 self._frame_mod = cfg.get('Frame Modification', None)
49 # check if test requires background load and which generator it uses
50 self._load_cfg = cfg.get('Load', None)
51 if self._load_cfg and 'tool' in self._load_cfg:
52 self._loadgen = self._load_cfg['tool']
54 # background load is not requested, so use dummy implementation
55 self._loadgen = "Dummy"
58 self._frame_mod = self._frame_mod.lower()
59 self._results_dir = results_dir
60 self._multistream = cfg.get('MultiStream', 0)
65 All setup and teardown through controllers is included.
67 self._logger.debug(self.name)
69 self._logger.debug("Controllers:")
71 traffic_ctl = component_factory.create_traffic(
73 loader.get_trafficgen_class())
74 vnf_ctl = component_factory.create_vnf(
76 loader.get_vnf_class())
77 vswitch_ctl = component_factory.create_vswitch(
79 loader.get_vswitch_class(),
81 collector = component_factory.create_collector(
82 loader.get_collector_class(),
83 self._results_dir, self.name)
84 loadgen = component_factory.create_loadgen(
88 self._logger.debug("Setup:")
89 with vswitch_ctl, loadgen:
90 with vnf_ctl, collector:
91 traffic = {'traffic_type': self._traffic_type,
93 'multistream': self._multistream}
95 # OVS Vanilla requires guest VM MAC address and IPs
97 if (self.deployment in ["pvp", "pvvp"] and
98 S.getValue('VSWITCH') == "OvsVanilla"):
100 traffic['l2'] = {'srcmac': S.getValue('GUEST_NET2_MAC')[0],
101 'dstmac': S.getValue('GUEST_NET1_MAC')[0]}
103 traffic['l3'] = {'srcip':
104 S.getValue('VANILLA_TGEN_PORT1_IP'),
106 S.getValue('VANILLA_TGEN_PORT2_IP')}
108 vswitch = vswitch_ctl.get_vswitch()
109 # TODO BOM 15-08-07 the frame mod code assumes that the
110 # physical ports are ports 1 & 2. The actual numbers
111 # need to be retrived from the vSwitch and the metadata value
112 # updated accordingly.
113 bridge = S.getValue('VSWITCH_BRIDGE_NAME')
114 if self._frame_mod == "vlan":
115 # 0x8100 => VLAN ethertype
116 self._logger.debug(" **** VLAN ***** ")
117 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
118 'actions': ['push_vlan:0x8100', 'goto_table:3']}
119 vswitch.add_flow(bridge, flow)
120 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
121 'actions': ['push_vlan:0x8100', 'goto_table:3']}
122 vswitch.add_flow(bridge, flow)
123 elif self._frame_mod == "mpls":
124 # 0x8847 => MPLS unicast ethertype
125 self._logger.debug(" **** MPLS ***** ")
126 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
127 'actions': ['push_mpls:0x8847', 'goto_table:3']}
128 vswitch.add_flow(bridge, flow)
129 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
130 'actions': ['push_mpls:0x8847', 'goto_table:3']}
131 vswitch.add_flow(bridge, flow)
132 elif self._frame_mod == "mac":
133 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
134 'actions': ['mod_dl_src:22:22:22:22:22:22',
136 vswitch.add_flow(bridge, flow)
137 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
138 'actions': ['mod_dl_src:11:11:11:11:11:11',
140 vswitch.add_flow(bridge, flow)
141 elif self._frame_mod == "dscp":
142 # DSCP 184d == 0x4E<<2 => 'Expedited Forwarding'
143 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
145 'actions': ['mod_nw_tos:184', 'goto_table:3']}
146 vswitch.add_flow(bridge, flow)
147 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
149 'actions': ['mod_nw_tos:184', 'goto_table:3']}
150 vswitch.add_flow(bridge, flow)
151 elif self._frame_mod == "ttl":
152 # 251 and 241 are the highest prime numbers < 255
153 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
155 'actions': ['mod_nw_ttl:251', 'goto_table:3']}
156 vswitch.add_flow(bridge, flow)
157 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
159 'actions': ['mod_nw_ttl:241', 'goto_table:3']}
160 vswitch.add_flow(bridge, flow)
161 elif self._frame_mod == "ip_addr":
162 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
164 'actions': ['mod_nw_src:10.10.10.10',
165 'mod_nw_dst:20.20.20.20',
167 vswitch.add_flow(bridge, flow)
168 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
170 'actions': ['mod_nw_src:20.20.20.20',
171 'mod_nw_dst:10.10.10.10',
173 vswitch.add_flow(bridge, flow)
174 elif self._frame_mod == "ip_port":
175 # TODO BOM 15-08-27 The traffic generated is assumed
176 # to be UDP (nw_proto 17d) which is the default case but
177 # we will need to pick up the actual traffic params in use.
178 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
179 'dl_type':'0x0800', 'nw_proto':'17',
180 'actions': ['mod_tp_src:44444',
181 'mod_tp_dst:44444', 'goto_table:3']}
182 vswitch.add_flow(bridge, flow)
183 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
184 'dl_type':'0x0800', 'nw_proto':'17',
185 'actions': ['mod_tp_src:44444',
186 'mod_tp_dst:44444', 'goto_table:3']}
187 vswitch.add_flow(bridge, flow)
192 traffic_ctl.send_traffic(traffic)
194 # dump vswitch flows before they are affected by VNF termination
195 vswitch_ctl.dump_vswitch_flows()
197 self._logger.debug("Traffic Results:")
198 traffic_ctl.print_results()
200 self._logger.debug("Collector Results:")
201 collector.print_results()
203 output_file = os.path.join(self._results_dir, "result_" + self.name +
204 "_" + self.deployment + ".csv")
206 tc_results = self._append_results(traffic_ctl.get_results())
207 TestCase._write_result_to_file(tc_results, output_file)
209 report.generate(output_file, tc_results, collector.get_results())
211 def _append_results(self, results):
213 Method appends mandatory Test Case results to list of dictionaries.
215 :param results: list of dictionaries which contains results from
218 :returns: modified list of dictionaries.
221 item[ResultsConstants.ID] = self.name
222 item[ResultsConstants.DEPLOYMENT] = self.deployment
228 def _write_result_to_file(results, output):
229 """Write list of dictionaries to a CSV file.
231 Each element on list will create separate row in output file.
232 If output file already exists, data will be appended at the end,
233 otherwise it will be created.
235 :param results: list of dictionaries.
236 :param output: path to output file.
238 with open(output, 'a') as csvfile:
240 logging.info("Write results to file: " + output)
241 fieldnames = TestCase._get_unique_keys(results)
243 writer = csv.DictWriter(csvfile, fieldnames)
245 if not csvfile.tell(): # file is now empty
248 for result in results:
249 writer.writerow(result)
253 def _get_unique_keys(list_of_dicts):
254 """Gets unique key values as ordered list of strings in given dicts
256 :param list_of_dicts: list of dictionaries.
258 :returns: list of unique keys(strings).
260 result = OrderedDict()
261 for item in list_of_dicts:
262 for key in item.keys():
265 return list(result.keys())