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
27 from tools.pkt_gen.trafficgen.trafficgenhelper import TRAFFIC_DEFAULTS
29 class TestCase(object):
30 """TestCase base class
32 In this basic form runs RFC2544 throughput test
34 def __init__(self, cfg, results_dir):
35 """Pull out fields from test config
37 :param cfg: A dictionary of string-value pairs describing the test
38 configuration. Both the key and values strings use well-known
40 :param results_dir: Where the csv formatted results are written.
42 self._logger = logging.getLogger(__name__)
43 self.name = cfg['Name']
44 self.desc = cfg.get('Description', 'No description given.')
45 self.deployment = cfg['Deployment']
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
60 # set traffic details, so they can be passed to vswitch and traffic ctls
61 self._traffic = TRAFFIC_DEFAULTS.copy()
62 self._traffic.update({'traffic_type': cfg['Traffic Type'],
63 'flow_type': cfg.get('Flow Type', 'port'),
64 'bidir': cfg['biDirectional'],
65 'multistream': cfg.get('MultiStream', 0)})
70 All setup and teardown through controllers is included.
72 self._logger.debug(self.name)
74 # OVS Vanilla requires guest VM MAC address and IPs
76 if (self.deployment in ["pvp", "pvvp"] and S.getValue('VSWITCH') == "OvsVanilla"):
77 self._traffic['l2'] = {'srcmac': S.getValue('GUEST_NET2_MAC')[0],
78 'dstmac': S.getValue('GUEST_NET1_MAC')[0]}
79 self._traffic['l3'] = {'srcip': S.getValue('VANILLA_TGEN_PORT1_IP'),
80 'dstip': S.getValue('VANILLA_TGEN_PORT2_IP')}
82 self._logger.debug("Controllers:")
84 traffic_ctl = component_factory.create_traffic(
85 self._traffic['traffic_type'],
86 loader.get_trafficgen_class())
87 vnf_ctl = component_factory.create_vnf(
89 loader.get_vnf_class())
90 vswitch_ctl = component_factory.create_vswitch(
92 loader.get_vswitch_class(),
94 collector = component_factory.create_collector(
95 loader.get_collector_class(),
96 self._results_dir, self.name)
97 loadgen = component_factory.create_loadgen(
101 self._logger.debug("Setup:")
102 with vswitch_ctl, loadgen:
103 with vnf_ctl, collector:
104 vswitch = vswitch_ctl.get_vswitch()
105 # TODO BOM 15-08-07 the frame mod code assumes that the
106 # physical ports are ports 1 & 2. The actual numbers
107 # need to be retrived from the vSwitch and the metadata value
108 # updated accordingly.
109 bridge = S.getValue('VSWITCH_BRIDGE_NAME')
110 if self._frame_mod == "vlan":
111 # 0x8100 => VLAN ethertype
112 self._logger.debug(" **** VLAN ***** ")
113 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
114 'actions': ['push_vlan:0x8100', 'goto_table:3']}
115 vswitch.add_flow(bridge, flow)
116 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
117 'actions': ['push_vlan:0x8100', 'goto_table:3']}
118 vswitch.add_flow(bridge, flow)
119 elif self._frame_mod == "mpls":
120 # 0x8847 => MPLS unicast ethertype
121 self._logger.debug(" **** MPLS ***** ")
122 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
123 'actions': ['push_mpls:0x8847', 'goto_table:3']}
124 vswitch.add_flow(bridge, flow)
125 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
126 'actions': ['push_mpls:0x8847', 'goto_table:3']}
127 vswitch.add_flow(bridge, flow)
128 elif self._frame_mod == "mac":
129 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
130 'actions': ['mod_dl_src:22:22:22:22:22:22',
132 vswitch.add_flow(bridge, flow)
133 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
134 'actions': ['mod_dl_src:11:11:11:11:11:11',
136 vswitch.add_flow(bridge, flow)
137 elif self._frame_mod == "dscp":
138 # DSCP 184d == 0x4E<<2 => 'Expedited Forwarding'
139 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
141 'actions': ['mod_nw_tos:184', 'goto_table:3']}
142 vswitch.add_flow(bridge, flow)
143 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
145 'actions': ['mod_nw_tos:184', 'goto_table:3']}
146 vswitch.add_flow(bridge, flow)
147 elif self._frame_mod == "ttl":
148 # 251 and 241 are the highest prime numbers < 255
149 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
151 'actions': ['mod_nw_ttl:251', 'goto_table:3']}
152 vswitch.add_flow(bridge, flow)
153 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
155 'actions': ['mod_nw_ttl:241', 'goto_table:3']}
156 vswitch.add_flow(bridge, flow)
157 elif self._frame_mod == "ip_addr":
158 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
160 'actions': ['mod_nw_src:10.10.10.10',
161 'mod_nw_dst:20.20.20.20',
163 vswitch.add_flow(bridge, flow)
164 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
166 'actions': ['mod_nw_src:20.20.20.20',
167 'mod_nw_dst:10.10.10.10',
169 vswitch.add_flow(bridge, flow)
170 elif self._frame_mod == "ip_port":
171 # TODO BOM 15-08-27 The traffic generated is assumed
172 # to be UDP (nw_proto 17d) which is the default case but
173 # we will need to pick up the actual traffic params in use.
174 flow = {'table':'2', 'priority':'1000', 'metadata':'2',
175 'dl_type':'0x0800', 'nw_proto':'17',
176 'actions': ['mod_tp_src:44444',
177 'mod_tp_dst:44444', 'goto_table:3']}
178 vswitch.add_flow(bridge, flow)
179 flow = {'table':'2', 'priority':'1000', 'metadata':'1',
180 'dl_type':'0x0800', 'nw_proto':'17',
181 'actions': ['mod_tp_src:44444',
182 'mod_tp_dst:44444', 'goto_table:3']}
183 vswitch.add_flow(bridge, flow)
188 traffic_ctl.send_traffic(self._traffic)
190 # dump vswitch flows before they are affected by VNF termination
191 vswitch_ctl.dump_vswitch_flows()
193 self._logger.debug("Traffic Results:")
194 traffic_ctl.print_results()
196 self._logger.debug("Collector Results:")
197 collector.print_results()
199 output_file = os.path.join(self._results_dir, "result_" + self.name +
200 "_" + self.deployment + ".csv")
202 tc_results = self._append_results(traffic_ctl.get_results())
203 TestCase._write_result_to_file(tc_results, output_file)
205 report.generate(output_file, tc_results, collector.get_results())
207 def _append_results(self, results):
209 Method appends mandatory Test Case results to list of dictionaries.
211 :param results: list of dictionaries which contains results from
214 :returns: modified list of dictionaries.
217 item[ResultsConstants.ID] = self.name
218 item[ResultsConstants.DEPLOYMENT] = self.deployment
224 def _write_result_to_file(results, output):
225 """Write list of dictionaries to a CSV file.
227 Each element on list will create separate row in output file.
228 If output file already exists, data will be appended at the end,
229 otherwise it will be created.
231 :param results: list of dictionaries.
232 :param output: path to output file.
234 with open(output, 'a') as csvfile:
236 logging.info("Write results to file: " + output)
237 fieldnames = TestCase._get_unique_keys(results)
239 writer = csv.DictWriter(csvfile, fieldnames)
241 if not csvfile.tell(): # file is now empty
244 for result in results:
245 writer.writerow(result)
249 def _get_unique_keys(list_of_dicts):
250 """Gets unique key values as ordered list of strings in given dicts
252 :param list_of_dicts: list of dictionaries.
254 :returns: list of unique keys(strings).
256 result = OrderedDict()
257 for item in list_of_dicts:
258 for key in item.keys():
261 return list(result.keys())