NFVBENCH-102 NFVBench won't work with external chain
[nfvbench.git] / nfvbench / service_chain.py
1 #!/usr/bin/env python
2 # Copyright 2016 Cisco Systems, Inc.  All rights reserved.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15 #
16
17 from collections import OrderedDict
18 import time
19
20 from chain_managers import StageManager
21 from log import LOG
22 from specs import ChainType
23
24
25 class ServiceChain(object):
26
27     def __init__(self, config, clients, cred, specs, factory, notifier=None):
28         self.config = config
29         self.clients = clients
30         self.cred = cred
31         self.specs = specs
32         self.factory = factory
33         self.notifier = notifier
34         self.chain_name = self.config.service_chain
35         self.vlans = None
36         self.stage_manager = None
37         self.stats_manager = None
38         LOG.info('ServiceChain initialized.')
39
40     def __set_helpers(self):
41         self.stage_manager = StageManager(self.config, self.cred, self.factory)
42         self.clients['vm'] = self.stage_manager
43         self.vlans = self.stage_manager.get_vlans()
44
45         STATS_CLASS = self.factory.get_stats_class(self.config.service_chain)
46         self.stats_manager = STATS_CLASS(self.config,
47                                          self.clients,
48                                          self.specs,
49                                          self.factory,
50                                          self.vlans,
51                                          self.notifier)
52
53     def __set_vlan_tags(self):
54         if self.config.vlan_tagging:
55             # override with user-specified vlans if configured
56             vlans = self.config.vlans if self.config.vlans else self.vlans[:2]
57             for vlan, device in zip(vlans, self.config.generator_config.devices):
58                 self.stats_manager.set_vlan_tag(device, vlan)
59
60     def __get_result_per_frame_size(self, frame_size, actual_frame_size, bidirectional):
61         start_time = time.time()
62         traffic_result = {
63             frame_size: {}
64         }
65         result = {}
66         if not self.config.no_traffic:
67             self.clients['traffic'].set_traffic(actual_frame_size, bidirectional)
68
69             if self.config.single_run:
70                 result = self.stats_manager.run()
71             else:
72                 results = self.clients['traffic'].get_ndr_and_pdr()
73
74                 for dr in ['pdr', 'ndr']:
75                     if dr in results:
76                         if frame_size != actual_frame_size:
77                             results[dr]['l2frame_size'] = frame_size
78                             results[dr]['actual_l2frame_size'] = actual_frame_size
79                         traffic_result[frame_size][dr] = results[dr]
80                         if 'warning' in results[dr]['stats'] and results[dr]['stats']['warning']:
81                             traffic_result['warning'] = results[dr]['stats']['warning']
82                 traffic_result[frame_size]['iteration_stats'] = results['iteration_stats']
83
84             result['analysis_duration_sec'] = time.time() - start_time
85             if self.config.single_run:
86                 result['run_config'] = self.clients['traffic'].get_run_config(result)
87                 required = result['run_config']['direction-total']['orig']['rate_pps']
88                 actual = result['stats']['total_tx_rate']
89                 if frame_size != actual_frame_size:
90                     result['actual_l2frame_size'] = actual_frame_size
91                 warning = self.clients['traffic'].compare_tx_rates(required, actual)
92                 if warning is not None:
93                     result['run_config']['warning'] = warning
94
95         traffic_result[frame_size].update(result)
96         return traffic_result
97
98     def __get_chain_result(self):
99         result = OrderedDict()
100         for fs, actual_fs in zip(self.config.frame_sizes, self.config.actual_frame_sizes):
101             result.update(self.__get_result_per_frame_size(fs,
102                                                            actual_fs,
103                                                            self.config.traffic.bidirectional))
104
105         chain_result = {
106             'flow_count': self.config.flow_count,
107             'service_chain_count': self.config.service_chain_count,
108             'bidirectional': self.config.traffic.bidirectional,
109             'profile': self.config.traffic.profile,
110             'compute_nodes': self.stats_manager.get_compute_nodes_bios(),
111             'result': result
112         }
113
114         return chain_result
115
116     def __setup_traffic(self):
117         self.clients['traffic'].setup()
118         if not self.config.no_traffic:
119             if self.config.service_chain == ChainType.EXT and not self.config.no_arp:
120                 self.clients['traffic'].ensure_arp_successful()
121             self.clients['traffic'].ensure_end_to_end()
122
123     def run(self):
124         LOG.info('Starting %s chain...', self.chain_name)
125         LOG.info('Dry run: %s', self.config.no_traffic)
126         results = {}
127
128         self.__set_helpers()
129         self.__set_vlan_tags()
130         self.stage_manager.set_vm_macs()
131         self.__setup_traffic()
132         results[self.chain_name] = {'result': self.__get_chain_result()}
133
134         if self.config.service_chain == ChainType.PVVP:
135             results[self.chain_name]['mode'] = 'inter-node' \
136                 if self.config.inter_node else 'intra-node'
137
138         LOG.info("Service chain '%s' run completed.", self.chain_name)
139         return results
140
141     def get_version(self):
142         return self.stats_manager.get_version()
143
144     def close(self):
145         if self.stage_manager:
146             self.stage_manager.close()
147         if self.stats_manager:
148             self.stats_manager.close()