104cfb454bf1f78cb094160afc08ced41dbef68c
[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 chain_managers import StageManager
18 from collections import OrderedDict
19 from log import LOG
20 from specs import ChainType
21 import time
22
23
24 class ServiceChain(object):
25
26     def __init__(self, config, clients, cred, specs, factory, notifier=None):
27         self.config = config
28         self.clients = clients
29         self.cred = cred
30         self.specs = specs
31         self.factory = factory
32         self.notifier = notifier
33         self.chain_name = self.config.service_chain
34         self.vlans = None
35         self.stage_manager = None
36         self.stats_manager = None
37         LOG.info('ServiceChain initialized.')
38
39     def __set_helpers(self):
40         self.stage_manager = StageManager(self.config, self.cred, self.factory)
41         self.clients['vm'] = self.stage_manager
42         self.vlans = self.stage_manager.get_vlans()
43
44         STATS_CLASS = self.factory.get_stats_class(self.config.service_chain)
45         self.stats_manager = STATS_CLASS(self.config,
46                                          self.clients,
47                                          self.specs,
48                                          self.factory,
49                                          self.vlans,
50                                          self.notifier)
51
52     def __set_vlan_tags(self):
53         if self.config.vlan_tagging:
54             # override with user-specified vlans if configured
55             vlans = self.config.vlans if self.config.vlans else self.vlans[:2]
56             for vlan, device in zip(vlans, self.config.generator_config.devices):
57                 self.stats_manager.set_vlan_tag(device, vlan)
58
59     def __get_result_per_frame_size(self, frame_size, bidirectional):
60         start_time = time.time()
61         traffic_result = {
62             frame_size: {}
63         }
64         result = {}
65         if not self.config.no_traffic:
66             self.clients['traffic'].set_traffic(frame_size, bidirectional)
67
68             if self.config.single_run:
69                 result = self.stats_manager.run()
70             else:
71                 results = self.clients['traffic'].get_ndr_and_pdr()
72
73                 for dr in ['pdr', 'ndr']:
74                     if dr in results:
75                         traffic_result[frame_size][dr] = results[dr]
76                         if 'warning' in results[dr]['stats'] and results[dr]['stats']['warning']:
77                             traffic_result['warning'] = results[dr]['stats']['warning']
78                 traffic_result[frame_size]['iteration_stats'] = results['iteration_stats']
79
80             result['analysis_duration_sec'] = time.time() - start_time
81             if self.config.single_run:
82                 result['run_config'] = self.clients['traffic'].get_run_config(result)
83                 required = result['run_config']['direction-total']['orig']['rate_pps']
84                 actual = result['stats']['total_tx_rate']
85                 warning = self.clients['traffic'].compare_tx_rates(required, actual)
86                 if warning is not None:
87                     result['run_config']['warning'] = warning
88
89         traffic_result[frame_size].update(result)
90         return traffic_result
91
92     def __get_chain_result(self):
93         result = OrderedDict()
94         for fs in self.config.frame_sizes:
95             result.update(self.__get_result_per_frame_size(fs, self.config.traffic.bidirectional))
96
97         chain_result = {
98             'flow_count': self.config.flow_count,
99             'service_chain_count': self.config.service_chain_count,
100             'bidirectional': self.config.traffic.bidirectional,
101             'profile': self.config.traffic.profile,
102             'compute_nodes': self.stats_manager.get_compute_nodes_bios(),
103             'result': result
104         }
105
106         return chain_result
107
108     def __setup_traffic(self):
109         self.clients['traffic'].setup()
110         if not self.config.no_traffic:
111             if self.config.service_chain == ChainType.EXT and not self.config.no_arp:
112                 self.clients['traffic'].ensure_arp_successful()
113             self.clients['traffic'].ensure_end_to_end()
114
115     def run(self):
116         LOG.info('Starting {} chain...'.format(self.chain_name))
117         LOG.info('Dry run: {}'.format(self.config.no_traffic))
118         results = {}
119
120         self.__set_helpers()
121         self.__set_vlan_tags()
122         self.stage_manager.set_vm_macs()
123         self.__setup_traffic()
124         results[self.chain_name] = {'result': self.__get_chain_result()}
125
126         if self.config.service_chain == ChainType.PVVP:
127             results[self.chain_name]['mode'] = 'inter-node' \
128                 if self.config.inter_node else 'intra-node'
129
130         LOG.info("Service chain '{}' run completed.".format(self.chain_name))
131         return results
132
133     def get_version(self):
134         return self.stats_manager.get_version()
135
136     def close(self):
137         self.stage_manager.close()
138         self.stats_manager.close()