test_spec: LTD: Add Caution for Scaleability Address Time-out.
[vswitchperf.git] / testcases / testcase.py
1 # Copyright 2015 Intel Corporation.
2 #
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
6 #
7 #   http://www.apache.org/licenses/LICENSE-2.0
8 #
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
15 """
16
17 import csv
18 import os
19 import logging
20 from collections import OrderedDict
21
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
28 class TestCase(object):
29     """TestCase base class
30
31     In this basic form runs RFC2544 throughput test
32     """
33     def __init__(self, cfg, results_dir):
34         """Pull out fields from test config
35
36         :param cfg: A dictionary of string-value pairs describing the test
37             configuration. Both the key and values strings use well-known
38             values.
39         :param results_dir: Where the csv formatted results are written.
40         """
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)
48
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']
53         else:
54             # background load is not requested, so use dummy implementation
55             self._loadgen = "Dummy"
56
57         if self._frame_mod:
58             self._frame_mod = self._frame_mod.lower()
59         self._results_dir = results_dir
60         self._multistream = cfg.get('MultiStream', 0)
61
62     def run(self):
63         """Run the test
64
65         All setup and teardown through controllers is included.
66         """
67         self._logger.debug(self.name)
68
69         self._logger.debug("Controllers:")
70         loader = Loader()
71         traffic_ctl = component_factory.create_traffic(
72             self._traffic_type,
73             loader.get_trafficgen_class())
74         vnf_ctl = component_factory.create_vnf(
75             self.deployment,
76             loader.get_vnf_class())
77         vswitch_ctl = component_factory.create_vswitch(
78             self.deployment,
79             loader.get_vswitch_class(),
80             self._bidir)
81         collector = component_factory.create_collector(
82             loader.get_collector_class(),
83             self._results_dir, self.name)
84         loadgen = component_factory.create_loadgen(
85             self._loadgen,
86             self._load_cfg)
87
88         self._logger.debug("Setup:")
89         with vswitch_ctl, loadgen:
90             with vnf_ctl, collector:
91                 traffic = {'traffic_type': self._traffic_type,
92                            'bidir': self._bidir,
93                            'multistream': self._multistream}
94
95                 # OVS Vanilla requires guest VM MAC address and IPs
96                 # to work
97                 if (self.deployment in ["pvp", "pvvp"] and
98                         S.getValue('VSWITCH') == "OvsVanilla"):
99
100                     traffic['l2'] = {'srcmac': S.getValue('GUEST_NET2_MAC')[0],
101                                      'dstmac': S.getValue('GUEST_NET1_MAC')[0]}
102
103                     traffic['l3'] = {'srcip':
104                                      S.getValue('VANILLA_TGEN_PORT1_IP'),
105                                      'dstip':
106                                      S.getValue('VANILLA_TGEN_PORT2_IP')}
107
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',
135                                         'goto_table:3']}
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',
139                                         'goto_table:3']}
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',
144                             'dl_type':'0x0800',
145                             'actions': ['mod_nw_tos:184', 'goto_table:3']}
146                     vswitch.add_flow(bridge, flow)
147                     flow = {'table':'2', 'priority':'1000', 'metadata':'1',
148                             'dl_type':'0x0800',
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',
154                             'dl_type':'0x0800',
155                             'actions': ['mod_nw_ttl:251', 'goto_table:3']}
156                     vswitch.add_flow(bridge, flow)
157                     flow = {'table':'2', 'priority':'1000', 'metadata':'1',
158                             'dl_type':'0x0800',
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',
163                             'dl_type':'0x0800',
164                             'actions': ['mod_nw_src:10.10.10.10',
165                                         'mod_nw_dst:20.20.20.20',
166                                         'goto_table:3']}
167                     vswitch.add_flow(bridge, flow)
168                     flow = {'table':'2', 'priority':'1000', 'metadata':'1',
169                             'dl_type':'0x0800',
170                             'actions': ['mod_nw_src:20.20.20.20',
171                                         'mod_nw_dst:10.10.10.10',
172                                         'goto_table:3']}
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)
188                 else:
189                     pass
190
191                 with traffic_ctl:
192                     traffic_ctl.send_traffic(traffic)
193
194                 # dump vswitch flows before they are affected by VNF termination
195                 vswitch_ctl.dump_vswitch_flows()
196
197         self._logger.debug("Traffic Results:")
198         traffic_ctl.print_results()
199
200         self._logger.debug("Collector Results:")
201         collector.print_results()
202
203         output_file = os.path.join(self._results_dir, "result_" + self.name +
204                                    "_" + self.deployment + ".csv")
205
206         tc_results = self._append_results(traffic_ctl.get_results())
207         TestCase._write_result_to_file(tc_results, output_file)
208
209         report.generate(output_file, tc_results, collector.get_results())
210
211     def _append_results(self, results):
212         """
213         Method appends mandatory Test Case results to list of dictionaries.
214
215         :param results: list of dictionaries which contains results from
216                 traffic generator.
217
218         :returns: modified list of dictionaries.
219         """
220         for item in results:
221             item[ResultsConstants.ID] = self.name
222             item[ResultsConstants.DEPLOYMENT] = self.deployment
223
224         return results
225
226
227     @staticmethod
228     def _write_result_to_file(results, output):
229         """Write list of dictionaries to a CSV file.
230
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.
234
235         :param results: list of dictionaries.
236         :param output: path to output file.
237         """
238         with open(output, 'a') as csvfile:
239
240             logging.info("Write results to file: " + output)
241             fieldnames = TestCase._get_unique_keys(results)
242
243             writer = csv.DictWriter(csvfile, fieldnames)
244
245             if not csvfile.tell():  # file is now empty
246                 writer.writeheader()
247
248             for result in results:
249                 writer.writerow(result)
250
251
252     @staticmethod
253     def _get_unique_keys(list_of_dicts):
254         """Gets unique key values as ordered list of strings in given dicts
255
256         :param list_of_dicts: list of dictionaries.
257
258         :returns: list of unique keys(strings).
259         """
260         result = OrderedDict()
261         for item in list_of_dicts:
262             for key in item.keys():
263                 result[key] = ''
264
265         return list(result.keys())