StatsManager must clean up self when there is an exception in __init__
[nfvbench.git] / nfvbench / chain_managers.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 log import LOG
18 from network import Network
19 from packet_analyzer import PacketAnalyzer
20 from specs import ChainType
21 from stats_collector import IntervalCollector
22 import time
23
24
25 class StageManager(object):
26
27     def __init__(self, config, cred, factory):
28         self.config = config
29         self.client = None
30         # conditions due to EXT chain special cases
31         if (config.vlan_tagging and not config.vlans) or not config.no_int_config:
32             VM_CLASS = factory.get_stage_class(config.service_chain)
33             self.client = VM_CLASS(config, cred)
34             self.client.setup()
35
36     def get_vlans(self):
37         return self.client.get_vlans() if self.client else []
38
39     def get_host_ips(self):
40         return self.client.get_host_ips()
41
42     def get_networks_uuids(self):
43         return self.client.get_networks_uuids()
44
45     def disable_port_security(self):
46         self.client.disable_port_security()
47
48     def get_vms(self):
49         return self.client.vms
50
51     def get_nets(self):
52         return self.client.nets
53
54     def get_ports(self):
55         return self.client.ports
56
57     def get_compute_nodes(self):
58         return self.client.compute_nodes
59
60     def set_vm_macs(self):
61         if self.client and self.config.service_chain != ChainType.EXT:
62             self.config.generator_config.set_vm_mac_list(self.client.get_end_port_macs())
63
64     def close(self):
65         if not self.config.no_cleanup and self.client:
66             self.client.dispose()
67
68
69 class StatsManager(object):
70
71     def __init__(self, config, clients, specs, factory, vlans, notifier=None):
72         self.config = config
73         self.clients = clients
74         self.specs = specs
75         self.notifier = notifier
76         self.interval_collector = None
77         self.vlans = vlans
78         self.factory = factory
79         self._setup()
80
81     def set_vlan_tag(self, device, vlan):
82         self.worker.set_vlan_tag(device, vlan)
83
84     def _setup(self):
85         WORKER_CLASS = self.factory.get_chain_worker(self.specs.openstack.encaps,
86                                                      self.config.service_chain)
87         self.worker = WORKER_CLASS(self.config, self.clients, self.specs)
88         try:
89             self.worker.set_vlans(self.vlans)
90             self._config_interfaces()
91         except Exception as exc:
92             # since the wrorker is up and running, we need to close it
93             # in case of exception
94             self.close()
95             raise exc
96
97     def _get_data(self):
98         return self.worker.get_data()
99
100     def _get_network(self, traffic_port, index=None, reverse=False):
101         interfaces = [self.clients['traffic'].get_interface(traffic_port)]
102         interfaces.extend(self.worker.get_network_interfaces(index))
103         return Network(interfaces, reverse)
104
105     def _config_interfaces(self):
106         if self.config.service_chain != ChainType.EXT:
107             self.clients['vm'].disable_port_security()
108
109         self.worker.config_interfaces()
110
111     def _generate_traffic(self):
112         if self.config.no_traffic:
113             return
114
115         self.interval_collector = IntervalCollector(time.time())
116         self.interval_collector.attach_notifier(self.notifier)
117         LOG.info('Starting to generate traffic...')
118         stats = {}
119         for stats in self.clients['traffic'].run_traffic():
120             self.interval_collector.add(stats)
121
122         LOG.info('...traffic generating ended.')
123         return stats
124
125     def get_stats(self):
126         return self.interval_collector.get() if self.interval_collector else []
127
128     def get_version(self):
129         return self.worker.get_version()
130
131     def run(self):
132         """
133         Run analysis in both direction and return the analysis
134         """
135         self.worker.run()
136
137         stats = self._generate_traffic()
138         result = {
139             'raw_data': self._get_data(),
140             'packet_analysis': {},
141             'stats': stats
142         }
143
144         LOG.info('Requesting packet analysis on the forward direction...')
145         result['packet_analysis']['direction-forward'] = \
146             self.get_analysis([self._get_network(0, 0),
147                                self._get_network(0, 1, reverse=True)])
148         LOG.info('Packet analysis on the forward direction completed')
149
150         LOG.info('Requesting packet analysis on the reverse direction...')
151         result['packet_analysis']['direction-reverse'] = \
152             self.get_analysis([self._get_network(1, 1),
153                                self._get_network(1, 0, reverse=True)])
154
155         LOG.info('Packet analysis on the reverse direction completed')
156         return result
157
158     def get_compute_nodes_bios(self):
159         return self.worker.get_compute_nodes_bios()
160
161     @staticmethod
162     def get_analysis(nets):
163         LOG.info('Starting traffic analysis...')
164
165         packet_analyzer = PacketAnalyzer()
166         # Traffic types are assumed to always alternate in every chain. Add a no stats interface in
167         # between if that is not the case.
168         tx = True
169         for network in nets:
170             for interface in network.get_interfaces():
171                 packet_analyzer.record(interface, 'tx' if tx else 'rx')
172                 tx = not tx
173
174         LOG.info('...traffic analysis completed')
175         return packet_analyzer.get_analysis()
176
177     def close(self):
178         self.worker.close()
179
180
181 class PVPStatsManager(StatsManager):
182
183     def __init__(self, config, clients, specs, factory, vlans, notifier=None):
184         StatsManager.__init__(self, config, clients, specs, factory, vlans, notifier)
185
186
187 class PVVPStatsManager(StatsManager):
188
189     def __init__(self, config, clients, specs, factory, vlans, notifier=None):
190         StatsManager.__init__(self, config, clients, specs, factory, vlans, notifier)
191
192     def run(self):
193         """
194         Run analysis in both direction and return the analysis
195         """
196         fwd_v2v_net, rev_v2v_net = self.worker.run()
197
198         stats = self._generate_traffic()
199         result = {
200             'raw_data': self._get_data(),
201             'packet_analysis': {},
202             'stats': stats
203         }
204
205         fwd_nets = [self._get_network(0, 0)]
206         if fwd_v2v_net:
207             fwd_nets.append(fwd_v2v_net)
208         fwd_nets.append(self._get_network(0, 1, reverse=True))
209
210         rev_nets = [self._get_network(1, 1)]
211         if rev_v2v_net:
212             rev_nets.append(rev_v2v_net)
213         rev_nets.append(self._get_network(1, 0, reverse=True))
214
215         LOG.info('Requesting packet analysis on the forward direction...')
216         result['packet_analysis']['direction-forward'] = self.get_analysis(fwd_nets)
217         LOG.info('Packet analysis on the forward direction completed')
218
219         LOG.info('Requesting packet analysis on the reverse direction...')
220         result['packet_analysis']['direction-reverse'] = self.get_analysis(rev_nets)
221
222         LOG.info('Packet analysis on the reverse direction completed')
223         return result
224
225
226 class EXTStatsManager(StatsManager):
227     def __init__(self, config, clients, specs, factory, vlans, notifier=None):
228         StatsManager.__init__(self, config, clients, specs, factory, vlans, notifier)
229
230     def _setup(self):
231         WORKER_CLASS = self.factory.get_chain_worker(self.specs.openstack.encaps,
232                                                      self.config.service_chain)
233         self.worker = WORKER_CLASS(self.config, self.clients, self.specs)
234         self.worker.set_vlans(self.vlans)
235
236         if not self.config.no_int_config:
237             self._config_interfaces()