blob: 074506b476bf5a65243c39cc80a8b7112e6c434f [file] [log] [blame]
peusterm79ef6ae2016-07-08 13:53:57 +02001"""
2Copyright (c) 2015 SONATA-NFV
3ALL RIGHTS RESERVED.
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16
17Neither the name of the SONATA-NFV [, ANY ADDITIONAL AFFILIATION]
18nor the names of its contributors may be used to endorse or promote
19products derived from this software without specific prior written
20permission.
21
22This work has been performed in the framework of the SONATA project,
23funded by the European Commission under Grant number 671517 through
24the Horizon 2020 and 5G-PPP programmes. The authors would like to
25acknowledge the contributions of their colleagues of the SONATA
26partner consortium (www.sonata-nfv.eu).
27"""
stevenvanrossemc5a536a2016-02-16 14:52:39 +010028
stevenvanrossemc5a536a2016-02-16 14:52:39 +010029import logging
30from mininet.node import OVSSwitch
31import ast
stevenvanrossem9315da42016-04-11 12:10:06 +020032import time
stevenvanrossem300e1e52016-04-22 22:17:51 +020033from prometheus_client import start_http_server, Summary, Histogram, Gauge, Counter, REGISTRY, CollectorRegistry, \
34 pushadd_to_gateway, push_to_gateway, delete_from_gateway
stevenvanrossema24b4372016-04-14 09:55:20 +020035import threading
stevenvanrosseme131bf52016-07-14 11:42:09 +020036from subprocess import Popen
stevenvanrossemb098cb52016-04-15 13:28:23 +020037import os
stevenvanrossema24b4372016-04-14 09:55:20 +020038
stevenvanrossem461941c2016-05-10 11:41:29 +020039
stevenvanrossemc5a536a2016-02-16 14:52:39 +010040logging.basicConfig(level=logging.INFO)
41
42"""
stevenvanrossema24b4372016-04-14 09:55:20 +020043class to read openflow stats from the Ryu controller of the DCNetwork
stevenvanrossemc5a536a2016-02-16 14:52:39 +010044"""
45
46class DCNetworkMonitor():
47 def __init__(self, net):
48 self.net = net
stevenvanrossem27b6d952016-05-10 16:37:57 +020049
stevenvanrosseme131bf52016-07-14 11:42:09 +020050 # TODO: these global variables should be part of a config file?
51 '''
52 # prometheus is started outside of son-emu
stevenvanrossem48db0512016-05-18 15:43:24 +020053 prometheus_ip = '127.0.0.1'
stevenvanrossem461941c2016-05-10 11:41:29 +020054 prometheus_port = '9090'
55 self.prometheus_REST_api = 'http://{0}:{1}'.format(prometheus_ip, prometheus_port)
stevenvanrosseme131bf52016-07-14 11:42:09 +020056 '''
stevenvanrossema24b4372016-04-14 09:55:20 +020057 # helper variables to calculate the metrics
stevenvanrosseme131bf52016-07-14 11:42:09 +020058 # pushgateway is started outside of son-emu and son-emu is started with net=host
59 # so localhost:9091 works
stevenvanrossem300e1e52016-04-22 22:17:51 +020060 self.pushgateway = 'localhost:9091'
stevenvanrosseme131bf52016-07-14 11:42:09 +020061 # when sdk is started with docker-compose, we could use
62 # self.pushgateway = 'pushgateway:9091'
stevenvanrosseme131bf52016-07-14 11:42:09 +020063
stevenvanrossema24b4372016-04-14 09:55:20 +020064 # supported Prometheus metrics
stevenvanrossem300e1e52016-04-22 22:17:51 +020065 self.registry = CollectorRegistry()
stevenvanrossema24b4372016-04-14 09:55:20 +020066 self.prom_tx_packet_count = Gauge('sonemu_tx_count_packets', 'Total number of packets sent',
stevenvanrossem461941c2016-05-10 11:41:29 +020067 ['vnf_name', 'vnf_interface', 'flow_id'], registry=self.registry)
stevenvanrossema24b4372016-04-14 09:55:20 +020068 self.prom_rx_packet_count = Gauge('sonemu_rx_count_packets', 'Total number of packets received',
stevenvanrossem461941c2016-05-10 11:41:29 +020069 ['vnf_name', 'vnf_interface', 'flow_id'], registry=self.registry)
stevenvanrossema24b4372016-04-14 09:55:20 +020070 self.prom_tx_byte_count = Gauge('sonemu_tx_count_bytes', 'Total number of bytes sent',
stevenvanrossem461941c2016-05-10 11:41:29 +020071 ['vnf_name', 'vnf_interface', 'flow_id'], registry=self.registry)
stevenvanrossema24b4372016-04-14 09:55:20 +020072 self.prom_rx_byte_count = Gauge('sonemu_rx_count_bytes', 'Total number of bytes received',
stevenvanrossem461941c2016-05-10 11:41:29 +020073 ['vnf_name', 'vnf_interface', 'flow_id'], registry=self.registry)
stevenvanrossema24b4372016-04-14 09:55:20 +020074
75 self.prom_metrics={'tx_packets':self.prom_tx_packet_count, 'rx_packets':self.prom_rx_packet_count,
76 'tx_bytes':self.prom_tx_byte_count,'rx_bytes':self.prom_rx_byte_count}
77
78 # list of installed metrics to monitor
79 # each entry can contain this data
80 '''
81 {
82 switch_dpid = 0
83 vnf_name = None
84 vnf_interface = None
85 previous_measurement = 0
86 previous_monitor_time = 0
87 metric_key = None
88 mon_port = None
89 }
90 '''
stevenvanrossem300e1e52016-04-22 22:17:51 +020091 self.monitor_lock = threading.Lock()
stevenvanrossem461941c2016-05-10 11:41:29 +020092 self.monitor_flow_lock = threading.Lock()
stevenvanrossemc6abf132016-04-14 11:15:58 +020093 self.network_metrics = []
stevenvanrossem461941c2016-05-10 11:41:29 +020094 self.flow_metrics = []
stevenvanrossema24b4372016-04-14 09:55:20 +020095
96 # start monitoring thread
stevenvanrossemb098cb52016-04-15 13:28:23 +020097 self.start_monitoring = True
stevenvanrossema24b4372016-04-14 09:55:20 +020098 self.monitor_thread = threading.Thread(target=self.get_network_metrics)
99 self.monitor_thread.start()
100
stevenvanrossem461941c2016-05-10 11:41:29 +0200101 self.monitor_flow_thread = threading.Thread(target=self.get_flow_metrics)
102 self.monitor_flow_thread.start()
103
stevenvanrossemc6abf132016-04-14 11:15:58 +0200104 # helper tools
stevenvanrossem89706802016-07-19 02:54:45 +0200105 # cAdvisor, Prometheus pushgateway and DB are started as external container, outside of son-emu
106
stevenvanrossema24b4372016-04-14 09:55:20 +0200107
stevenvanrossemed711fd2016-04-11 16:59:29 +0200108 # first set some parameters, before measurement can start
stevenvanrossem461941c2016-05-10 11:41:29 +0200109 def setup_flow(self, vnf_name, vnf_interface=None, metric='tx_packets', cookie=0):
110
111 flow_metric = {}
112
113 # check if port is specified (vnf:port)
114 if vnf_interface is None:
115 # take first interface by default
116 connected_sw = self.net.DCNetwork_graph.neighbors(vnf_name)[0]
117 link_dict = self.net.DCNetwork_graph[vnf_name][connected_sw]
118 vnf_interface = link_dict[0]['src_port_id']
119
120 flow_metric['vnf_name'] = vnf_name
121 flow_metric['vnf_interface'] = vnf_interface
122
123 vnf_switch = None
124 for connected_sw in self.net.DCNetwork_graph.neighbors(vnf_name):
125 link_dict = self.net.DCNetwork_graph[vnf_name][connected_sw]
126 for link in link_dict:
stevenvanrossem461941c2016-05-10 11:41:29 +0200127 if link_dict[link]['src_port_id'] == vnf_interface:
128 # found the right link and connected switch
stevenvanrossem461941c2016-05-10 11:41:29 +0200129 vnf_switch = connected_sw
130 flow_metric['mon_port'] = link_dict[link]['dst_port_nr']
131 break
132
133 if not vnf_switch:
134 logging.exception("vnf switch of {0}:{1} not found!".format(vnf_name, vnf_interface))
135 return "vnf switch of {0}:{1} not found!".format(vnf_name, vnf_interface)
136
137 try:
138 # default port direction to monitor
139 if metric is None:
140 metric = 'tx_packets'
141
142 next_node = self.net.getNodeByName(vnf_switch)
143
144 if not isinstance(next_node, OVSSwitch):
145 logging.info("vnf: {0} is not connected to switch".format(vnf_name))
146 return
147
148 flow_metric['previous_measurement'] = 0
149 flow_metric['previous_monitor_time'] = 0
150
151 flow_metric['switch_dpid'] = int(str(next_node.dpid), 16)
152 flow_metric['metric_key'] = metric
153 flow_metric['cookie'] = cookie
154
155 self.monitor_flow_lock.acquire()
156 self.flow_metrics.append(flow_metric)
157 self.monitor_flow_lock.release()
158
159 logging.info('Started monitoring flow:{3} {2} on {0}:{1}'.format(vnf_name, vnf_interface, metric, cookie))
160 return 'Started monitoring flow:{3} {2} on {0}:{1}'.format(vnf_name, vnf_interface, metric, cookie)
161
162 except Exception as ex:
163 logging.exception("setup_metric error.")
164 return ex.message
165
stevenvanrossem9c8a4122016-07-16 03:23:13 +0200166 def stop_flow(self, vnf_name, vnf_interface=None, metric=None, cookie=0,):
167
168 # check if port is specified (vnf:port)
169 if vnf_interface is None and metric is not None:
170 # take first interface by default
171 connected_sw = self.net.DCNetwork_graph.neighbors(vnf_name)[0]
172 link_dict = self.net.DCNetwork_graph[vnf_name][connected_sw]
173 vnf_interface = link_dict[0]['src_port_id']
174
stevenvanrossem1ef77022016-05-12 16:36:10 +0200175 for flow_dict in self.flow_metrics:
176 if flow_dict['vnf_name'] == vnf_name and flow_dict['vnf_interface'] == vnf_interface \
177 and flow_dict['metric_key'] == metric and flow_dict['cookie'] == cookie:
178
179 self.monitor_flow_lock.acquire()
180
181 self.flow_metrics.remove(flow_dict)
182
183 for collector in self.registry._collectors:
184 if (vnf_name, vnf_interface, cookie) in collector._metrics:
stevenvanrossem1ef77022016-05-12 16:36:10 +0200185 collector.remove(vnf_name, vnf_interface, cookie)
186
187 delete_from_gateway(self.pushgateway, job='sonemu-SDNcontroller')
188
189 self.monitor_flow_lock.release()
190
191 logging.info('Stopped monitoring flow {3}: {2} on {0}:{1}'.format(vnf_name, vnf_interface, metric, cookie))
192 return 'Stopped monitoring flow {3}: {2} on {0}:{1}'.format(vnf_name, vnf_interface, metric, cookie)
193
stevenvanrossem9c8a4122016-07-16 03:23:13 +0200194 return 'Error stopping monitoring flow: {0} on {1}:{2}'.format(metric, vnf_name, vnf_interface)
195
stevenvanrossem461941c2016-05-10 11:41:29 +0200196
197 # first set some parameters, before measurement can start
stevenvanrossema24b4372016-04-14 09:55:20 +0200198 def setup_metric(self, vnf_name, vnf_interface=None, metric='tx_packets'):
199
200 network_metric = {}
201
stevenvanrossem9315da42016-04-11 12:10:06 +0200202 # check if port is specified (vnf:port)
stevenvanrossemed711fd2016-04-11 16:59:29 +0200203 if vnf_interface is None:
stevenvanrossem9315da42016-04-11 12:10:06 +0200204 # take first interface by default
205 connected_sw = self.net.DCNetwork_graph.neighbors(vnf_name)[0]
206 link_dict = self.net.DCNetwork_graph[vnf_name][connected_sw]
207 vnf_interface = link_dict[0]['src_port_id']
stevenvanrossem9315da42016-04-11 12:10:06 +0200208
stevenvanrossema24b4372016-04-14 09:55:20 +0200209 network_metric['vnf_name'] = vnf_name
210 network_metric['vnf_interface'] = vnf_interface
stevenvanrossema24b4372016-04-14 09:55:20 +0200211
stevenvanrossem9315da42016-04-11 12:10:06 +0200212 for connected_sw in self.net.DCNetwork_graph.neighbors(vnf_name):
213 link_dict = self.net.DCNetwork_graph[vnf_name][connected_sw]
214 for link in link_dict:
stevenvanrossem9315da42016-04-11 12:10:06 +0200215 if link_dict[link]['src_port_id'] == vnf_interface:
216 # found the right link and connected switch
stevenvanrossem307aa1f2016-05-06 10:35:15 +0200217 network_metric['mon_port'] = link_dict[link]['dst_port_nr']
stevenvanrossem9315da42016-04-11 12:10:06 +0200218 break
219
stevenvanrossema24b4372016-04-14 09:55:20 +0200220 if 'mon_port' not in network_metric:
221 logging.exception("vnf interface {0}:{1} not found!".format(vnf_name,vnf_interface))
222 return "vnf interface {0}:{1} not found!".format(vnf_name,vnf_interface)
223
stevenvanrossem9315da42016-04-11 12:10:06 +0200224 try:
225 # default port direction to monitor
stevenvanrossemed711fd2016-04-11 16:59:29 +0200226 if metric is None:
stevenvanrossema24b4372016-04-14 09:55:20 +0200227 metric = 'tx_packets'
stevenvanrossem9315da42016-04-11 12:10:06 +0200228
stevenvanrossemc5a536a2016-02-16 14:52:39 +0100229 vnf_switch = self.net.DCNetwork_graph.neighbors(str(vnf_name))
230
231 if len(vnf_switch) > 1:
232 logging.info("vnf: {0} has multiple ports".format(vnf_name))
233 return
234 elif len(vnf_switch) == 0:
235 logging.info("vnf: {0} is not connected".format(vnf_name))
236 return
237 else:
238 vnf_switch = vnf_switch[0]
239 next_node = self.net.getNodeByName(vnf_switch)
240
stevenvanrossemed711fd2016-04-11 16:59:29 +0200241 if not isinstance(next_node, OVSSwitch):
stevenvanrossemc5a536a2016-02-16 14:52:39 +0100242 logging.info("vnf: {0} is not connected to switch".format(vnf_name))
243 return
244
stevenvanrossema24b4372016-04-14 09:55:20 +0200245 network_metric['previous_measurement'] = 0
246 network_metric['previous_monitor_time'] = 0
stevenvanrossemb098cb52016-04-15 13:28:23 +0200247
stevenvanrossem9315da42016-04-11 12:10:06 +0200248
stevenvanrossema24b4372016-04-14 09:55:20 +0200249 network_metric['switch_dpid'] = int(str(next_node.dpid), 16)
250 network_metric['metric_key'] = metric
stevenvanrossemb098cb52016-04-15 13:28:23 +0200251
stevenvanrossem300e1e52016-04-22 22:17:51 +0200252 self.monitor_lock.acquire()
253
stevenvanrossema24b4372016-04-14 09:55:20 +0200254 self.network_metrics.append(network_metric)
stevenvanrossem300e1e52016-04-22 22:17:51 +0200255 self.monitor_lock.release()
256
stevenvanrossema24b4372016-04-14 09:55:20 +0200257
258 logging.info('Started monitoring: {2} on {0}:{1}'.format(vnf_name, vnf_interface, metric))
259 return 'Started monitoring: {2} on {0}:{1}'.format(vnf_name, vnf_interface, metric)
stevenvanrossemc5a536a2016-02-16 14:52:39 +0100260
stevenvanrossemed711fd2016-04-11 16:59:29 +0200261 except Exception as ex:
stevenvanrossembbdb5ee2016-04-15 15:18:44 +0200262 logging.exception("setup_metric error.")
stevenvanrossemed711fd2016-04-11 16:59:29 +0200263 return ex.message
stevenvanrossem9315da42016-04-11 12:10:06 +0200264
stevenvanrossem461941c2016-05-10 11:41:29 +0200265 def stop_metric(self, vnf_name, vnf_interface=None, metric=None):
stevenvanrossem300e1e52016-04-22 22:17:51 +0200266
stevenvanrossem9c8a4122016-07-16 03:23:13 +0200267 # check if port is specified (vnf:port)
268 if vnf_interface is None and metric is not None:
269 # take first interface by default
270 connected_sw = self.net.DCNetwork_graph.neighbors(vnf_name)[0]
271 link_dict = self.net.DCNetwork_graph[vnf_name][connected_sw]
272 vnf_interface = link_dict[0]['src_port_id']
273
stevenvanrossemb098cb52016-04-15 13:28:23 +0200274 for metric_dict in self.network_metrics:
275 if metric_dict['vnf_name'] == vnf_name and metric_dict['vnf_interface'] == vnf_interface \
stevenvanrossembbdb5ee2016-04-15 15:18:44 +0200276 and metric_dict['metric_key'] == metric:
277
stevenvanrossem300e1e52016-04-22 22:17:51 +0200278 self.monitor_lock.acquire()
279
stevenvanrossemb098cb52016-04-15 13:28:23 +0200280 self.network_metrics.remove(metric_dict)
stevenvanrossembbdb5ee2016-04-15 15:18:44 +0200281
282 #this removes the complete metric, all labels...
283 #REGISTRY.unregister(self.prom_metrics[metric_dict['metric_key']])
stevenvanrossem300e1e52016-04-22 22:17:51 +0200284 #self.registry.unregister(self.prom_metrics[metric_dict['metric_key']])
285
286 for collector in self.registry._collectors :
stevenvanrosseme131bf52016-07-14 11:42:09 +0200287
stevenvanrossem300e1e52016-04-22 22:17:51 +0200288 """
289 INFO:root:name:sonemu_rx_count_packets
290 labels:('vnf_name', 'vnf_interface')
291 metrics:{(u'tsrc', u'output'): < prometheus_client.core.Gauge
292 object
293 at
294 0x7f353447fd10 >}
295 """
296 logging.info('{0}'.format(collector._metrics.values()))
stevenvanrosseme131bf52016-07-14 11:42:09 +0200297
stevenvanrossem461941c2016-05-10 11:41:29 +0200298 if (vnf_name, vnf_interface, 'None') in collector._metrics:
stevenvanrossem300e1e52016-04-22 22:17:51 +0200299 logging.info('2 name:{0} labels:{1} metrics:{2}'.format(collector._name, collector._labelnames,
300 collector._metrics))
stevenvanrossem461941c2016-05-10 11:41:29 +0200301 collector.remove(vnf_name, vnf_interface, 'None')
stevenvanrossembbdb5ee2016-04-15 15:18:44 +0200302
303 # set values to NaN, prometheus api currently does not support removal of metrics
stevenvanrossem300e1e52016-04-22 22:17:51 +0200304 #self.prom_metrics[metric_dict['metric_key']].labels(vnf_name, vnf_interface).set(float('nan'))
305
306 # this removes the complete metric, all labels...
307 # 1 single monitor job for all metrics of the SDN controller
308 # we can only remove from the pushgateway grouping keys(labels) which we have defined for the add_to_pushgateway
309 # we can not specify labels from the metrics to be removed
310 # if we need to remove the metrics seperatelty, we need to give them a separate grouping key, and probably a diffferent registry also
311 delete_from_gateway(self.pushgateway, job='sonemu-SDNcontroller')
312
313 self.monitor_lock.release()
stevenvanrossembbdb5ee2016-04-15 15:18:44 +0200314
stevenvanrossemb098cb52016-04-15 13:28:23 +0200315 logging.info('Stopped monitoring: {2} on {0}:{1}'.format(vnf_name, vnf_interface, metric))
316 return 'Stopped monitoring: {2} on {0}:{1}'.format(vnf_name, vnf_interface, metric)
stevenvanrossem9315da42016-04-11 12:10:06 +0200317
stevenvanrossem461941c2016-05-10 11:41:29 +0200318 # delete everything from this vnf
319 elif metric_dict['vnf_name'] == vnf_name and vnf_interface is None and metric is None:
320 self.monitor_lock.acquire()
321 self.network_metrics.remove(metric_dict)
322 for collector in self.registry._collectors:
323 collector_dict = collector._metrics.copy()
324 for name, interface, id in collector_dict:
325 if name == vnf_name:
326 logging.info('3 name:{0} labels:{1} metrics:{2}'.format(collector._name, collector._labelnames,
327 collector._metrics))
328 collector.remove(name, interface, 'None')
329
330 delete_from_gateway(self.pushgateway, job='sonemu-SDNcontroller')
331 self.monitor_lock.release()
332 logging.info('Stopped monitoring vnf: {0}'.format(vnf_name))
333 return 'Stopped monitoring: {0}'.format(vnf_name)
334
stevenvanrossem9c8a4122016-07-16 03:23:13 +0200335 return 'Error stopping monitoring metric: {0} on {1}:{2}'.format(metric, vnf_name, vnf_interface)
stevenvanrossemb098cb52016-04-15 13:28:23 +0200336
stevenvanrossem9c8a4122016-07-16 03:23:13 +0200337
338
339
340
341# get all metrics defined in the list and export it to Prometheus
stevenvanrossem461941c2016-05-10 11:41:29 +0200342 def get_flow_metrics(self):
343 while self.start_monitoring:
344
345 self.monitor_flow_lock.acquire()
346
347 for flow_dict in self.flow_metrics:
348 data = {}
349
350 data['cookie'] = flow_dict['cookie']
stevenvanrossem1a5ced92016-08-07 00:52:13 +0200351 data['cookie_mask'] = flow_dict['cookie']
stevenvanrossem461941c2016-05-10 11:41:29 +0200352
353 if 'tx' in flow_dict['metric_key']:
354 data['match'] = {'in_port':flow_dict['mon_port']}
355 elif 'rx' in flow_dict['metric_key']:
356 data['out_port'] = flow_dict['mon_port']
357
358
359 # query Ryu
stevenvanrossem27b6d952016-05-10 16:37:57 +0200360 ret = self.net.ryu_REST('stats/flow', dpid=flow_dict['switch_dpid'], data=data)
stevenvanrossem461941c2016-05-10 11:41:29 +0200361 flow_stat_dict = ast.literal_eval(ret)
362
stevenvanrosseme131bf52016-07-14 11:42:09 +0200363 logging.debug('received flow stat:{0} '.format(flow_stat_dict))
stevenvanrossem461941c2016-05-10 11:41:29 +0200364 self.set_flow_metric(flow_dict, flow_stat_dict)
365
366 self.monitor_flow_lock.release()
367 time.sleep(1)
368
stevenvanrossema24b4372016-04-14 09:55:20 +0200369 def get_network_metrics(self):
stevenvanrossemb098cb52016-04-15 13:28:23 +0200370 while self.start_monitoring:
stevenvanrossem300e1e52016-04-22 22:17:51 +0200371
372 self.monitor_lock.acquire()
373
stevenvanrossema24b4372016-04-14 09:55:20 +0200374 # group metrics by dpid to optimize the rest api calls
375 dpid_list = [metric_dict['switch_dpid'] for metric_dict in self.network_metrics]
376 dpid_set = set(dpid_list)
377
378 for dpid in dpid_set:
379
380 # query Ryu
stevenvanrossem27b6d952016-05-10 16:37:57 +0200381 ret = self.net.ryu_REST('stats/port', dpid=dpid)
stevenvanrossema24b4372016-04-14 09:55:20 +0200382 port_stat_dict = ast.literal_eval(ret)
383
384 metric_list = [metric_dict for metric_dict in self.network_metrics
385 if int(metric_dict['switch_dpid'])==int(dpid)]
stevenvanrosseme131bf52016-07-14 11:42:09 +0200386
stevenvanrossema24b4372016-04-14 09:55:20 +0200387 for metric_dict in metric_list:
388 self.set_network_metric(metric_dict, port_stat_dict)
389
stevenvanrossem300e1e52016-04-22 22:17:51 +0200390 self.monitor_lock.release()
stevenvanrossema24b4372016-04-14 09:55:20 +0200391 time.sleep(1)
392
stevenvanrossemb098cb52016-04-15 13:28:23 +0200393 # add metric to the list to export to Prometheus, parse the Ryu port-stats reply
stevenvanrossema24b4372016-04-14 09:55:20 +0200394 def set_network_metric(self, metric_dict, port_stat_dict):
stevenvanrossemb098cb52016-04-15 13:28:23 +0200395 # vnf tx is the datacenter switch rx and vice-versa
396 metric_key = self.switch_tx_rx(metric_dict['metric_key'])
stevenvanrossema24b4372016-04-14 09:55:20 +0200397 switch_dpid = metric_dict['switch_dpid']
398 vnf_name = metric_dict['vnf_name']
399 vnf_interface = metric_dict['vnf_interface']
400 previous_measurement = metric_dict['previous_measurement']
401 previous_monitor_time = metric_dict['previous_monitor_time']
402 mon_port = metric_dict['mon_port']
403
404 for port_stat in port_stat_dict[str(switch_dpid)]:
405 if int(port_stat['port_no']) == int(mon_port):
406 port_uptime = port_stat['duration_sec'] + port_stat['duration_nsec'] * 10 ** (-9)
407 this_measurement = int(port_stat[metric_key])
stevenvanrossema24b4372016-04-14 09:55:20 +0200408
409 # set prometheus metric
stevenvanrossem300e1e52016-04-22 22:17:51 +0200410 self.prom_metrics[metric_dict['metric_key']].\
stevenvanrossem461941c2016-05-10 11:41:29 +0200411 labels({'vnf_name': vnf_name, 'vnf_interface': vnf_interface, 'flow_id': None}).\
stevenvanrossem300e1e52016-04-22 22:17:51 +0200412 set(this_measurement)
stevenvanrossem300e1e52016-04-22 22:17:51 +0200413
414 # 1 single monitor job for all metrics of the SDN controller
415 pushadd_to_gateway(self.pushgateway, job='sonemu-SDNcontroller', registry=self.registry)
stevenvanrossema24b4372016-04-14 09:55:20 +0200416
stevenvanrosseme131bf52016-07-14 11:42:09 +0200417 # also the rate is calculated here, but not used for now
418 # (rate can be easily queried from prometheus also)
stevenvanrossema24b4372016-04-14 09:55:20 +0200419 if previous_monitor_time <= 0 or previous_monitor_time >= port_uptime:
420 metric_dict['previous_measurement'] = int(port_stat[metric_key])
421 metric_dict['previous_monitor_time'] = port_uptime
422 # do first measurement
stevenvanrossema24b4372016-04-14 09:55:20 +0200423 time.sleep(1)
stevenvanrossem300e1e52016-04-22 22:17:51 +0200424 self.monitor_lock.release()
stevenvanrossem61fd5282016-04-29 12:41:54 +0200425
stevenvanrossem300e1e52016-04-22 22:17:51 +0200426 metric_rate = self.get_network_metrics()
427 return metric_rate
stevenvanrossem61fd5282016-04-29 12:41:54 +0200428
stevenvanrossema24b4372016-04-14 09:55:20 +0200429 else:
430 time_delta = (port_uptime - metric_dict['previous_monitor_time'])
stevenvanrossem300e1e52016-04-22 22:17:51 +0200431 metric_rate = (this_measurement - metric_dict['previous_measurement']) / float(time_delta)
stevenvanrossema24b4372016-04-14 09:55:20 +0200432
433 metric_dict['previous_measurement'] = this_measurement
434 metric_dict['previous_monitor_time'] = port_uptime
stevenvanrossem300e1e52016-04-22 22:17:51 +0200435 return metric_rate
stevenvanrossema24b4372016-04-14 09:55:20 +0200436
437 logging.exception('metric {0} not found on {1}:{2}'.format(metric_key, vnf_name, vnf_interface))
438 return 'metric {0} not found on {1}:{2}'.format(metric_key, vnf_name, vnf_interface)
439
stevenvanrossem461941c2016-05-10 11:41:29 +0200440 def set_flow_metric(self, metric_dict, flow_stat_dict):
441 # vnf tx is the datacenter switch rx and vice-versa
stevenvanrossem461941c2016-05-10 11:41:29 +0200442 metric_key = metric_dict['metric_key']
443 switch_dpid = metric_dict['switch_dpid']
444 vnf_name = metric_dict['vnf_name']
445 vnf_interface = metric_dict['vnf_interface']
446 previous_measurement = metric_dict['previous_measurement']
447 previous_monitor_time = metric_dict['previous_monitor_time']
448 cookie = metric_dict['cookie']
stevenvanrossema24b4372016-04-14 09:55:20 +0200449
stevenvanrossem461941c2016-05-10 11:41:29 +0200450 # TODO aggregate all found flow stats
stevenvanrossem9c8a4122016-07-16 03:23:13 +0200451 #flow_stat = flow_stat_dict[str(switch_dpid)][0]
452 #if 'bytes' in metric_key:
453 # counter = flow_stat['byte_count']
454 #elif 'packet' in metric_key:
455 # counter = flow_stat['packet_count']
stevenvanrossem461941c2016-05-10 11:41:29 +0200456
stevenvanrossem9c8a4122016-07-16 03:23:13 +0200457 counter = 0
458 for flow_stat in flow_stat_dict[str(switch_dpid)]:
459 if 'bytes' in metric_key:
460 counter += flow_stat['byte_count']
461 elif 'packet' in metric_key:
462 counter += flow_stat['packet_count']
463
464 flow_stat = flow_stat_dict[str(switch_dpid)][0]
stevenvanrossem461941c2016-05-10 11:41:29 +0200465 flow_uptime = flow_stat['duration_sec'] + flow_stat['duration_nsec'] * 10 ** (-9)
466
467 self.prom_metrics[metric_dict['metric_key']]. \
468 labels({'vnf_name': vnf_name, 'vnf_interface': vnf_interface, 'flow_id': cookie}). \
469 set(counter)
470 pushadd_to_gateway(self.pushgateway, job='sonemu-SDNcontroller', registry=self.registry)
471
stevenvanrossemc6abf132016-04-14 11:15:58 +0200472
473 def start_Prometheus(self, port=9090):
stevenvanrossemb098cb52016-04-15 13:28:23 +0200474 # prometheus.yml configuration file is located in the same directory as this file
stevenvanrossemc6abf132016-04-14 11:15:58 +0200475 cmd = ["docker",
476 "run",
477 "--rm",
478 "-p", "{0}:9090".format(port),
stevenvanrossemb098cb52016-04-15 13:28:23 +0200479 "-v", "{0}/prometheus.yml:/etc/prometheus/prometheus.yml".format(os.path.dirname(os.path.abspath(__file__))),
stevenvanrossem61fd5282016-04-29 12:41:54 +0200480 "-v", "{0}/profile.rules:/etc/prometheus/profile.rules".format(os.path.dirname(os.path.abspath(__file__))),
stevenvanrossemc6abf132016-04-14 11:15:58 +0200481 "--name", "prometheus",
482 "prom/prometheus"
483 ]
stevenvanrossemb098cb52016-04-15 13:28:23 +0200484 logging.info('Start Prometheus container {0}'.format(cmd))
485 return Popen(cmd)
stevenvanrossemc6abf132016-04-14 11:15:58 +0200486
stevenvanrossemadfd06f2016-04-22 10:39:08 +0200487 def start_PushGateway(self, port=9091):
488 cmd = ["docker",
489 "run",
490 "-d",
491 "-p", "{0}:9091".format(port),
492 "--name", "pushgateway",
493 "prom/pushgateway"
494 ]
495
496 logging.info('Start Prometheus Push Gateway container {0}'.format(cmd))
497 return Popen(cmd)
498
stevenvanrossemb098cb52016-04-15 13:28:23 +0200499 def start_cadvisor(self, port=8090):
stevenvanrossemc6abf132016-04-14 11:15:58 +0200500 cmd = ["docker",
501 "run",
502 "--rm",
503 "--volume=/:/rootfs:ro",
504 "--volume=/var/run:/var/run:rw",
505 "--volume=/sys:/sys:ro",
506 "--volume=/var/lib/docker/:/var/lib/docker:ro",
507 "--publish={0}:8080".format(port),
508 "--name=cadvisor",
509 "google/cadvisor:latest"
510 ]
stevenvanrossemb098cb52016-04-15 13:28:23 +0200511 logging.info('Start cAdvisor container {0}'.format(cmd))
512 return Popen(cmd)
stevenvanrossemc6abf132016-04-14 11:15:58 +0200513
514 def stop(self):
stevenvanrossemb098cb52016-04-15 13:28:23 +0200515 # stop the monitoring thread
516 self.start_monitoring = False
517 self.monitor_thread.join()
stevenvanrossem461941c2016-05-10 11:41:29 +0200518 self.monitor_flow_thread.join()
stevenvanrossemb098cb52016-04-15 13:28:23 +0200519
stevenvanrossem9c8a4122016-07-16 03:23:13 +0200520 # these containers are used for monitoring but are started now outside of son-emu
stevenvanrossem2fdfbf42016-05-13 15:08:47 +0200521 '''
stevenvanrossemc6abf132016-04-14 11:15:58 +0200522 if self.prometheus_process is not None:
stevenvanrossemb098cb52016-04-15 13:28:23 +0200523 logging.info('stopping prometheus container')
stevenvanrossemc6abf132016-04-14 11:15:58 +0200524 self.prometheus_process.terminate()
525 self.prometheus_process.kill()
stevenvanrossemb098cb52016-04-15 13:28:23 +0200526 self._stop_container('prometheus')
stevenvanrossemc6abf132016-04-14 11:15:58 +0200527
stevenvanrossemadfd06f2016-04-22 10:39:08 +0200528 if self.pushgateway_process is not None:
529 logging.info('stopping pushgateway container')
530 self.pushgateway_process.terminate()
531 self.pushgateway_process.kill()
532 self._stop_container('pushgateway')
533
stevenvanrossemb098cb52016-04-15 13:28:23 +0200534 if self.cadvisor_process is not None:
535 logging.info('stopping cadvisor container')
536 self.cadvisor_process.terminate()
537 self.cadvisor_process.kill()
538 self._stop_container('cadvisor')
stevenvanrossem9c8a4122016-07-16 03:23:13 +0200539 '''
stevenvanrossemb098cb52016-04-15 13:28:23 +0200540
541 def switch_tx_rx(self,metric=''):
542 # when monitoring vnfs, the tx of the datacenter switch is actually the rx of the vnf
543 # so we need to change the metric name to be consistent with the vnf rx or tx
544 if 'tx' in metric:
545 metric = metric.replace('tx','rx')
546 elif 'rx' in metric:
547 metric = metric.replace('rx','tx')
548
549 return metric
550
551 def _stop_container(self, name):
552 cmd = ["docker",
553 "stop",
554 name]
555 Popen(cmd).wait()
556
557 cmd = ["docker",
558 "rm",
559 name]
560 Popen(cmd).wait()
561