blob: b5f02049e6d7e33b18949cec23045499733a85c4 [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
stevenvanrossem51d4ae72016-08-10 13:22:53 +020030import sys
stevenvanrossemc5a536a2016-02-16 14:52:39 +010031from mininet.node import OVSSwitch
32import ast
stevenvanrossem9315da42016-04-11 12:10:06 +020033import time
stevenvanrossem300e1e52016-04-22 22:17:51 +020034from prometheus_client import start_http_server, Summary, Histogram, Gauge, Counter, REGISTRY, CollectorRegistry, \
35 pushadd_to_gateway, push_to_gateway, delete_from_gateway
stevenvanrossema24b4372016-04-14 09:55:20 +020036import threading
stevenvanrosseme131bf52016-07-14 11:42:09 +020037from subprocess import Popen
stevenvanrossemb098cb52016-04-15 13:28:23 +020038import os
stevenvanrossema24b4372016-04-14 09:55:20 +020039
stevenvanrossem461941c2016-05-10 11:41:29 +020040
stevenvanrossemc5a536a2016-02-16 14:52:39 +010041logging.basicConfig(level=logging.INFO)
42
43"""
stevenvanrossema24b4372016-04-14 09:55:20 +020044class to read openflow stats from the Ryu controller of the DCNetwork
stevenvanrossemc5a536a2016-02-16 14:52:39 +010045"""
46
stevenvanrossema5aeb372016-08-18 17:32:24 +020047PUSHGATEWAY_PORT = 9091
stevenvanrosseme50b0a72016-08-18 17:42:50 +020048# we cannot use port 8080 because ryu-ofrest api is already using that one
49CADVISOR_PORT = 8081
stevenvanrossema5aeb372016-08-18 17:32:24 +020050
stevenvanrossemb7715d32016-08-26 16:22:21 +020051COOKIE_MASK = 0xffffffff
52
stevenvanrossemc5a536a2016-02-16 14:52:39 +010053class DCNetworkMonitor():
54 def __init__(self, net):
55 self.net = net
stevenvanrossem27b6d952016-05-10 16:37:57 +020056
stevenvanrossema5aeb372016-08-18 17:32:24 +020057 # pushgateway address
58 self.pushgateway = 'localhost:{0}'.format(PUSHGATEWAY_PORT)
stevenvanrosseme131bf52016-07-14 11:42:09 +020059
stevenvanrossema24b4372016-04-14 09:55:20 +020060 # supported Prometheus metrics
stevenvanrossem300e1e52016-04-22 22:17:51 +020061 self.registry = CollectorRegistry()
stevenvanrossema24b4372016-04-14 09:55:20 +020062 self.prom_tx_packet_count = Gauge('sonemu_tx_count_packets', 'Total number of packets sent',
stevenvanrossem461941c2016-05-10 11:41:29 +020063 ['vnf_name', 'vnf_interface', 'flow_id'], registry=self.registry)
stevenvanrossema24b4372016-04-14 09:55:20 +020064 self.prom_rx_packet_count = Gauge('sonemu_rx_count_packets', 'Total number of packets received',
stevenvanrossem461941c2016-05-10 11:41:29 +020065 ['vnf_name', 'vnf_interface', 'flow_id'], registry=self.registry)
stevenvanrossema24b4372016-04-14 09:55:20 +020066 self.prom_tx_byte_count = Gauge('sonemu_tx_count_bytes', 'Total number of bytes 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_byte_count = Gauge('sonemu_rx_count_bytes', 'Total number of bytes received',
stevenvanrossem461941c2016-05-10 11:41:29 +020069 ['vnf_name', 'vnf_interface', 'flow_id'], registry=self.registry)
stevenvanrossema24b4372016-04-14 09:55:20 +020070
71 self.prom_metrics={'tx_packets':self.prom_tx_packet_count, 'rx_packets':self.prom_rx_packet_count,
72 'tx_bytes':self.prom_tx_byte_count,'rx_bytes':self.prom_rx_byte_count}
73
74 # list of installed metrics to monitor
75 # each entry can contain this data
76 '''
77 {
78 switch_dpid = 0
79 vnf_name = None
80 vnf_interface = None
81 previous_measurement = 0
82 previous_monitor_time = 0
83 metric_key = None
84 mon_port = None
85 }
86 '''
stevenvanrossem300e1e52016-04-22 22:17:51 +020087 self.monitor_lock = threading.Lock()
stevenvanrossem461941c2016-05-10 11:41:29 +020088 self.monitor_flow_lock = threading.Lock()
stevenvanrossemc6abf132016-04-14 11:15:58 +020089 self.network_metrics = []
stevenvanrossem461941c2016-05-10 11:41:29 +020090 self.flow_metrics = []
stevenvanrossema24b4372016-04-14 09:55:20 +020091
92 # start monitoring thread
stevenvanrossemb098cb52016-04-15 13:28:23 +020093 self.start_monitoring = True
stevenvanrossema24b4372016-04-14 09:55:20 +020094 self.monitor_thread = threading.Thread(target=self.get_network_metrics)
95 self.monitor_thread.start()
96
stevenvanrossem461941c2016-05-10 11:41:29 +020097 self.monitor_flow_thread = threading.Thread(target=self.get_flow_metrics)
98 self.monitor_flow_thread.start()
99
stevenvanrossemc6abf132016-04-14 11:15:58 +0200100 # helper tools
stevenvanrossema5aeb372016-08-18 17:32:24 +0200101 # cAdvisor, Prometheus pushgateway are started as external container, to gather monitoring metric in son-emu
stevenvanrosseme50b0a72016-08-18 17:42:50 +0200102 self.pushgateway_process = self.start_PushGateway()
103 self.cadvisor_process = self.start_cAdvisor()
stevenvanrossem89706802016-07-19 02:54:45 +0200104
stevenvanrossema24b4372016-04-14 09:55:20 +0200105
stevenvanrossemed711fd2016-04-11 16:59:29 +0200106 # first set some parameters, before measurement can start
stevenvanrossem461941c2016-05-10 11:41:29 +0200107 def setup_flow(self, vnf_name, vnf_interface=None, metric='tx_packets', cookie=0):
108
109 flow_metric = {}
110
111 # check if port is specified (vnf:port)
112 if vnf_interface is None:
113 # take first interface by default
114 connected_sw = self.net.DCNetwork_graph.neighbors(vnf_name)[0]
115 link_dict = self.net.DCNetwork_graph[vnf_name][connected_sw]
116 vnf_interface = link_dict[0]['src_port_id']
117
118 flow_metric['vnf_name'] = vnf_name
119 flow_metric['vnf_interface'] = vnf_interface
120
121 vnf_switch = None
122 for connected_sw in self.net.DCNetwork_graph.neighbors(vnf_name):
123 link_dict = self.net.DCNetwork_graph[vnf_name][connected_sw]
124 for link in link_dict:
stevenvanrossem461941c2016-05-10 11:41:29 +0200125 if link_dict[link]['src_port_id'] == vnf_interface:
126 # found the right link and connected switch
stevenvanrossem461941c2016-05-10 11:41:29 +0200127 vnf_switch = connected_sw
128 flow_metric['mon_port'] = link_dict[link]['dst_port_nr']
129 break
130
131 if not vnf_switch:
132 logging.exception("vnf switch of {0}:{1} not found!".format(vnf_name, vnf_interface))
133 return "vnf switch of {0}:{1} not found!".format(vnf_name, vnf_interface)
134
135 try:
136 # default port direction to monitor
137 if metric is None:
138 metric = 'tx_packets'
139
140 next_node = self.net.getNodeByName(vnf_switch)
141
142 if not isinstance(next_node, OVSSwitch):
143 logging.info("vnf: {0} is not connected to switch".format(vnf_name))
144 return
145
146 flow_metric['previous_measurement'] = 0
147 flow_metric['previous_monitor_time'] = 0
148
149 flow_metric['switch_dpid'] = int(str(next_node.dpid), 16)
150 flow_metric['metric_key'] = metric
151 flow_metric['cookie'] = cookie
152
153 self.monitor_flow_lock.acquire()
154 self.flow_metrics.append(flow_metric)
155 self.monitor_flow_lock.release()
156
157 logging.info('Started monitoring flow:{3} {2} on {0}:{1}'.format(vnf_name, vnf_interface, metric, cookie))
158 return 'Started monitoring flow:{3} {2} on {0}:{1}'.format(vnf_name, vnf_interface, metric, cookie)
159
160 except Exception as ex:
161 logging.exception("setup_metric error.")
162 return ex.message
163
stevenvanrossem9c8a4122016-07-16 03:23:13 +0200164 def stop_flow(self, vnf_name, vnf_interface=None, metric=None, cookie=0,):
165
166 # check if port is specified (vnf:port)
167 if vnf_interface is None and metric is not None:
168 # take first interface by default
169 connected_sw = self.net.DCNetwork_graph.neighbors(vnf_name)[0]
170 link_dict = self.net.DCNetwork_graph[vnf_name][connected_sw]
171 vnf_interface = link_dict[0]['src_port_id']
172
stevenvanrossem1ef77022016-05-12 16:36:10 +0200173 for flow_dict in self.flow_metrics:
174 if flow_dict['vnf_name'] == vnf_name and flow_dict['vnf_interface'] == vnf_interface \
175 and flow_dict['metric_key'] == metric and flow_dict['cookie'] == cookie:
176
177 self.monitor_flow_lock.acquire()
178
179 self.flow_metrics.remove(flow_dict)
180
181 for collector in self.registry._collectors:
182 if (vnf_name, vnf_interface, cookie) in collector._metrics:
stevenvanrossem1ef77022016-05-12 16:36:10 +0200183 collector.remove(vnf_name, vnf_interface, cookie)
184
185 delete_from_gateway(self.pushgateway, job='sonemu-SDNcontroller')
186
187 self.monitor_flow_lock.release()
188
189 logging.info('Stopped monitoring flow {3}: {2} on {0}:{1}'.format(vnf_name, vnf_interface, metric, cookie))
190 return 'Stopped monitoring flow {3}: {2} on {0}:{1}'.format(vnf_name, vnf_interface, metric, cookie)
191
stevenvanrossem9c8a4122016-07-16 03:23:13 +0200192 return 'Error stopping monitoring flow: {0} on {1}:{2}'.format(metric, vnf_name, vnf_interface)
193
stevenvanrossem461941c2016-05-10 11:41:29 +0200194
195 # first set some parameters, before measurement can start
stevenvanrossema24b4372016-04-14 09:55:20 +0200196 def setup_metric(self, vnf_name, vnf_interface=None, metric='tx_packets'):
197
198 network_metric = {}
199
stevenvanrossem9315da42016-04-11 12:10:06 +0200200 # check if port is specified (vnf:port)
stevenvanrossemed711fd2016-04-11 16:59:29 +0200201 if vnf_interface is None:
stevenvanrossem9315da42016-04-11 12:10:06 +0200202 # take first interface by default
203 connected_sw = self.net.DCNetwork_graph.neighbors(vnf_name)[0]
204 link_dict = self.net.DCNetwork_graph[vnf_name][connected_sw]
205 vnf_interface = link_dict[0]['src_port_id']
stevenvanrossem9315da42016-04-11 12:10:06 +0200206
stevenvanrossema24b4372016-04-14 09:55:20 +0200207 network_metric['vnf_name'] = vnf_name
208 network_metric['vnf_interface'] = vnf_interface
stevenvanrossema24b4372016-04-14 09:55:20 +0200209
stevenvanrossem9315da42016-04-11 12:10:06 +0200210 for connected_sw in self.net.DCNetwork_graph.neighbors(vnf_name):
211 link_dict = self.net.DCNetwork_graph[vnf_name][connected_sw]
212 for link in link_dict:
stevenvanrossem9315da42016-04-11 12:10:06 +0200213 if link_dict[link]['src_port_id'] == vnf_interface:
214 # found the right link and connected switch
stevenvanrossem307aa1f2016-05-06 10:35:15 +0200215 network_metric['mon_port'] = link_dict[link]['dst_port_nr']
stevenvanrossem9315da42016-04-11 12:10:06 +0200216 break
217
stevenvanrossema24b4372016-04-14 09:55:20 +0200218 if 'mon_port' not in network_metric:
219 logging.exception("vnf interface {0}:{1} not found!".format(vnf_name,vnf_interface))
220 return "vnf interface {0}:{1} not found!".format(vnf_name,vnf_interface)
221
stevenvanrossem9315da42016-04-11 12:10:06 +0200222 try:
223 # default port direction to monitor
stevenvanrossemed711fd2016-04-11 16:59:29 +0200224 if metric is None:
stevenvanrossema24b4372016-04-14 09:55:20 +0200225 metric = 'tx_packets'
stevenvanrossem9315da42016-04-11 12:10:06 +0200226
stevenvanrossemc5a536a2016-02-16 14:52:39 +0100227 vnf_switch = self.net.DCNetwork_graph.neighbors(str(vnf_name))
228
229 if len(vnf_switch) > 1:
230 logging.info("vnf: {0} has multiple ports".format(vnf_name))
231 return
232 elif len(vnf_switch) == 0:
233 logging.info("vnf: {0} is not connected".format(vnf_name))
234 return
235 else:
236 vnf_switch = vnf_switch[0]
237 next_node = self.net.getNodeByName(vnf_switch)
238
stevenvanrossemed711fd2016-04-11 16:59:29 +0200239 if not isinstance(next_node, OVSSwitch):
stevenvanrossemc5a536a2016-02-16 14:52:39 +0100240 logging.info("vnf: {0} is not connected to switch".format(vnf_name))
241 return
242
stevenvanrossema24b4372016-04-14 09:55:20 +0200243 network_metric['previous_measurement'] = 0
244 network_metric['previous_monitor_time'] = 0
stevenvanrossemb098cb52016-04-15 13:28:23 +0200245
stevenvanrossem9315da42016-04-11 12:10:06 +0200246
stevenvanrossema24b4372016-04-14 09:55:20 +0200247 network_metric['switch_dpid'] = int(str(next_node.dpid), 16)
248 network_metric['metric_key'] = metric
stevenvanrossemb098cb52016-04-15 13:28:23 +0200249
stevenvanrossem300e1e52016-04-22 22:17:51 +0200250 self.monitor_lock.acquire()
251
stevenvanrossema24b4372016-04-14 09:55:20 +0200252 self.network_metrics.append(network_metric)
stevenvanrossem300e1e52016-04-22 22:17:51 +0200253 self.monitor_lock.release()
254
stevenvanrossema24b4372016-04-14 09:55:20 +0200255
256 logging.info('Started monitoring: {2} on {0}:{1}'.format(vnf_name, vnf_interface, metric))
257 return 'Started monitoring: {2} on {0}:{1}'.format(vnf_name, vnf_interface, metric)
stevenvanrossemc5a536a2016-02-16 14:52:39 +0100258
stevenvanrossemed711fd2016-04-11 16:59:29 +0200259 except Exception as ex:
stevenvanrossembbdb5ee2016-04-15 15:18:44 +0200260 logging.exception("setup_metric error.")
stevenvanrossemed711fd2016-04-11 16:59:29 +0200261 return ex.message
stevenvanrossem9315da42016-04-11 12:10:06 +0200262
stevenvanrossem461941c2016-05-10 11:41:29 +0200263 def stop_metric(self, vnf_name, vnf_interface=None, metric=None):
stevenvanrossem300e1e52016-04-22 22:17:51 +0200264
stevenvanrossem9c8a4122016-07-16 03:23:13 +0200265 # check if port is specified (vnf:port)
266 if vnf_interface is None and metric is not None:
267 # take first interface by default
268 connected_sw = self.net.DCNetwork_graph.neighbors(vnf_name)[0]
269 link_dict = self.net.DCNetwork_graph[vnf_name][connected_sw]
270 vnf_interface = link_dict[0]['src_port_id']
271
stevenvanrossemb098cb52016-04-15 13:28:23 +0200272 for metric_dict in self.network_metrics:
273 if metric_dict['vnf_name'] == vnf_name and metric_dict['vnf_interface'] == vnf_interface \
stevenvanrossembbdb5ee2016-04-15 15:18:44 +0200274 and metric_dict['metric_key'] == metric:
275
stevenvanrossem300e1e52016-04-22 22:17:51 +0200276 self.monitor_lock.acquire()
277
stevenvanrossemb098cb52016-04-15 13:28:23 +0200278 self.network_metrics.remove(metric_dict)
stevenvanrossembbdb5ee2016-04-15 15:18:44 +0200279
280 #this removes the complete metric, all labels...
281 #REGISTRY.unregister(self.prom_metrics[metric_dict['metric_key']])
stevenvanrossem300e1e52016-04-22 22:17:51 +0200282 #self.registry.unregister(self.prom_metrics[metric_dict['metric_key']])
283
284 for collector in self.registry._collectors :
stevenvanrosseme131bf52016-07-14 11:42:09 +0200285
stevenvanrossem300e1e52016-04-22 22:17:51 +0200286 """
287 INFO:root:name:sonemu_rx_count_packets
288 labels:('vnf_name', 'vnf_interface')
289 metrics:{(u'tsrc', u'output'): < prometheus_client.core.Gauge
290 object
291 at
292 0x7f353447fd10 >}
293 """
294 logging.info('{0}'.format(collector._metrics.values()))
stevenvanrosseme131bf52016-07-14 11:42:09 +0200295
stevenvanrossem461941c2016-05-10 11:41:29 +0200296 if (vnf_name, vnf_interface, 'None') in collector._metrics:
stevenvanrossem300e1e52016-04-22 22:17:51 +0200297 logging.info('2 name:{0} labels:{1} metrics:{2}'.format(collector._name, collector._labelnames,
298 collector._metrics))
stevenvanrossem461941c2016-05-10 11:41:29 +0200299 collector.remove(vnf_name, vnf_interface, 'None')
stevenvanrossembbdb5ee2016-04-15 15:18:44 +0200300
301 # set values to NaN, prometheus api currently does not support removal of metrics
stevenvanrossem300e1e52016-04-22 22:17:51 +0200302 #self.prom_metrics[metric_dict['metric_key']].labels(vnf_name, vnf_interface).set(float('nan'))
303
304 # this removes the complete metric, all labels...
305 # 1 single monitor job for all metrics of the SDN controller
306 # we can only remove from the pushgateway grouping keys(labels) which we have defined for the add_to_pushgateway
307 # we can not specify labels from the metrics to be removed
308 # if we need to remove the metrics seperatelty, we need to give them a separate grouping key, and probably a diffferent registry also
309 delete_from_gateway(self.pushgateway, job='sonemu-SDNcontroller')
310
311 self.monitor_lock.release()
stevenvanrossembbdb5ee2016-04-15 15:18:44 +0200312
stevenvanrossemb098cb52016-04-15 13:28:23 +0200313 logging.info('Stopped monitoring: {2} on {0}:{1}'.format(vnf_name, vnf_interface, metric))
314 return 'Stopped monitoring: {2} on {0}:{1}'.format(vnf_name, vnf_interface, metric)
stevenvanrossem9315da42016-04-11 12:10:06 +0200315
stevenvanrossem461941c2016-05-10 11:41:29 +0200316 # delete everything from this vnf
317 elif metric_dict['vnf_name'] == vnf_name and vnf_interface is None and metric is None:
318 self.monitor_lock.acquire()
319 self.network_metrics.remove(metric_dict)
320 for collector in self.registry._collectors:
321 collector_dict = collector._metrics.copy()
322 for name, interface, id in collector_dict:
323 if name == vnf_name:
324 logging.info('3 name:{0} labels:{1} metrics:{2}'.format(collector._name, collector._labelnames,
325 collector._metrics))
326 collector.remove(name, interface, 'None')
327
328 delete_from_gateway(self.pushgateway, job='sonemu-SDNcontroller')
329 self.monitor_lock.release()
330 logging.info('Stopped monitoring vnf: {0}'.format(vnf_name))
331 return 'Stopped monitoring: {0}'.format(vnf_name)
332
stevenvanrossem9c8a4122016-07-16 03:23:13 +0200333 return 'Error stopping monitoring metric: {0} on {1}:{2}'.format(metric, vnf_name, vnf_interface)
stevenvanrossemb098cb52016-04-15 13:28:23 +0200334
stevenvanrossem9c8a4122016-07-16 03:23:13 +0200335
336
337
338
339# get all metrics defined in the list and export it to Prometheus
stevenvanrossem461941c2016-05-10 11:41:29 +0200340 def get_flow_metrics(self):
341 while self.start_monitoring:
342
343 self.monitor_flow_lock.acquire()
344
345 for flow_dict in self.flow_metrics:
346 data = {}
347
348 data['cookie'] = flow_dict['cookie']
stevenvanrossemb7715d32016-08-26 16:22:21 +0200349 data['cookie_mask'] = COOKIE_MASK
stevenvanrossem461941c2016-05-10 11:41:29 +0200350
351 if 'tx' in flow_dict['metric_key']:
352 data['match'] = {'in_port':flow_dict['mon_port']}
353 elif 'rx' in flow_dict['metric_key']:
354 data['out_port'] = flow_dict['mon_port']
355
356
357 # query Ryu
stevenvanrossem27b6d952016-05-10 16:37:57 +0200358 ret = self.net.ryu_REST('stats/flow', dpid=flow_dict['switch_dpid'], data=data)
stevenvanrossem51d4ae72016-08-10 13:22:53 +0200359 if isinstance(ret, dict):
360 flow_stat_dict = ret
361 elif isinstance(ret, basestring):
362 flow_stat_dict = ast.literal_eval(ret.rstrip())
363 else:
364 flow_stat_dict = None
365
stevenvanrosseme131bf52016-07-14 11:42:09 +0200366 logging.debug('received flow stat:{0} '.format(flow_stat_dict))
stevenvanrossem3fc13932016-08-09 23:39:16 +0200367
stevenvanrossem461941c2016-05-10 11:41:29 +0200368 self.set_flow_metric(flow_dict, flow_stat_dict)
369
370 self.monitor_flow_lock.release()
371 time.sleep(1)
372
stevenvanrossema24b4372016-04-14 09:55:20 +0200373 def get_network_metrics(self):
stevenvanrossemb098cb52016-04-15 13:28:23 +0200374 while self.start_monitoring:
stevenvanrossem300e1e52016-04-22 22:17:51 +0200375
376 self.monitor_lock.acquire()
377
stevenvanrossema24b4372016-04-14 09:55:20 +0200378 # group metrics by dpid to optimize the rest api calls
379 dpid_list = [metric_dict['switch_dpid'] for metric_dict in self.network_metrics]
380 dpid_set = set(dpid_list)
381
382 for dpid in dpid_set:
383
384 # query Ryu
stevenvanrossem27b6d952016-05-10 16:37:57 +0200385 ret = self.net.ryu_REST('stats/port', dpid=dpid)
stevenvanrossemb7715d32016-08-26 16:22:21 +0200386 if isinstance(ret, dict):
387 port_stat_dict = ret
388 elif isinstance(ret, basestring):
389 port_stat_dict = ast.literal_eval(ret.rstrip())
390 else:
391 port_stat_dict = None
stevenvanrossema24b4372016-04-14 09:55:20 +0200392
393 metric_list = [metric_dict for metric_dict in self.network_metrics
394 if int(metric_dict['switch_dpid'])==int(dpid)]
stevenvanrosseme131bf52016-07-14 11:42:09 +0200395
stevenvanrossema24b4372016-04-14 09:55:20 +0200396 for metric_dict in metric_list:
397 self.set_network_metric(metric_dict, port_stat_dict)
398
stevenvanrossem300e1e52016-04-22 22:17:51 +0200399 self.monitor_lock.release()
stevenvanrossema24b4372016-04-14 09:55:20 +0200400 time.sleep(1)
401
stevenvanrossemb098cb52016-04-15 13:28:23 +0200402 # add metric to the list to export to Prometheus, parse the Ryu port-stats reply
stevenvanrossema24b4372016-04-14 09:55:20 +0200403 def set_network_metric(self, metric_dict, port_stat_dict):
stevenvanrossemb098cb52016-04-15 13:28:23 +0200404 # vnf tx is the datacenter switch rx and vice-versa
405 metric_key = self.switch_tx_rx(metric_dict['metric_key'])
stevenvanrossema24b4372016-04-14 09:55:20 +0200406 switch_dpid = metric_dict['switch_dpid']
407 vnf_name = metric_dict['vnf_name']
408 vnf_interface = metric_dict['vnf_interface']
409 previous_measurement = metric_dict['previous_measurement']
410 previous_monitor_time = metric_dict['previous_monitor_time']
411 mon_port = metric_dict['mon_port']
412
413 for port_stat in port_stat_dict[str(switch_dpid)]:
414 if int(port_stat['port_no']) == int(mon_port):
415 port_uptime = port_stat['duration_sec'] + port_stat['duration_nsec'] * 10 ** (-9)
416 this_measurement = int(port_stat[metric_key])
stevenvanrossema24b4372016-04-14 09:55:20 +0200417
418 # set prometheus metric
stevenvanrossem300e1e52016-04-22 22:17:51 +0200419 self.prom_metrics[metric_dict['metric_key']].\
stevenvanrossem461941c2016-05-10 11:41:29 +0200420 labels({'vnf_name': vnf_name, 'vnf_interface': vnf_interface, 'flow_id': None}).\
stevenvanrossem300e1e52016-04-22 22:17:51 +0200421 set(this_measurement)
stevenvanrossem300e1e52016-04-22 22:17:51 +0200422
423 # 1 single monitor job for all metrics of the SDN controller
424 pushadd_to_gateway(self.pushgateway, job='sonemu-SDNcontroller', registry=self.registry)
stevenvanrossema24b4372016-04-14 09:55:20 +0200425
stevenvanrosseme131bf52016-07-14 11:42:09 +0200426 # also the rate is calculated here, but not used for now
427 # (rate can be easily queried from prometheus also)
stevenvanrossema24b4372016-04-14 09:55:20 +0200428 if previous_monitor_time <= 0 or previous_monitor_time >= port_uptime:
429 metric_dict['previous_measurement'] = int(port_stat[metric_key])
430 metric_dict['previous_monitor_time'] = port_uptime
431 # do first measurement
stevenvanrossema24b4372016-04-14 09:55:20 +0200432 time.sleep(1)
stevenvanrossem300e1e52016-04-22 22:17:51 +0200433 self.monitor_lock.release()
stevenvanrossem61fd5282016-04-29 12:41:54 +0200434
stevenvanrossem300e1e52016-04-22 22:17:51 +0200435 metric_rate = self.get_network_metrics()
436 return metric_rate
stevenvanrossem61fd5282016-04-29 12:41:54 +0200437
stevenvanrossema24b4372016-04-14 09:55:20 +0200438 else:
439 time_delta = (port_uptime - metric_dict['previous_monitor_time'])
stevenvanrossem300e1e52016-04-22 22:17:51 +0200440 metric_rate = (this_measurement - metric_dict['previous_measurement']) / float(time_delta)
stevenvanrossema24b4372016-04-14 09:55:20 +0200441
442 metric_dict['previous_measurement'] = this_measurement
443 metric_dict['previous_monitor_time'] = port_uptime
stevenvanrossem300e1e52016-04-22 22:17:51 +0200444 return metric_rate
stevenvanrossema24b4372016-04-14 09:55:20 +0200445
446 logging.exception('metric {0} not found on {1}:{2}'.format(metric_key, vnf_name, vnf_interface))
447 return 'metric {0} not found on {1}:{2}'.format(metric_key, vnf_name, vnf_interface)
448
stevenvanrossem461941c2016-05-10 11:41:29 +0200449 def set_flow_metric(self, metric_dict, flow_stat_dict):
450 # vnf tx is the datacenter switch rx and vice-versa
stevenvanrossem461941c2016-05-10 11:41:29 +0200451 metric_key = metric_dict['metric_key']
452 switch_dpid = metric_dict['switch_dpid']
453 vnf_name = metric_dict['vnf_name']
454 vnf_interface = metric_dict['vnf_interface']
455 previous_measurement = metric_dict['previous_measurement']
456 previous_monitor_time = metric_dict['previous_monitor_time']
457 cookie = metric_dict['cookie']
stevenvanrossema24b4372016-04-14 09:55:20 +0200458
stevenvanrossem9c8a4122016-07-16 03:23:13 +0200459 counter = 0
460 for flow_stat in flow_stat_dict[str(switch_dpid)]:
461 if 'bytes' in metric_key:
462 counter += flow_stat['byte_count']
463 elif 'packet' in metric_key:
464 counter += flow_stat['packet_count']
465
466 flow_stat = flow_stat_dict[str(switch_dpid)][0]
stevenvanrossem461941c2016-05-10 11:41:29 +0200467 flow_uptime = flow_stat['duration_sec'] + flow_stat['duration_nsec'] * 10 ** (-9)
468
469 self.prom_metrics[metric_dict['metric_key']]. \
470 labels({'vnf_name': vnf_name, 'vnf_interface': vnf_interface, 'flow_id': cookie}). \
471 set(counter)
stevenvanrossem51d4ae72016-08-10 13:22:53 +0200472 try:
473 pushadd_to_gateway(self.pushgateway, job='sonemu-SDNcontroller', registry=self.registry)
474 except Exception, e:
475 logging.warning("Pushgateway not reachable: {0} {1}".format(Exception, e))
stevenvanrossem461941c2016-05-10 11:41:29 +0200476
stevenvanrossemc6abf132016-04-14 11:15:58 +0200477
stevenvanrosseme50b0a72016-08-18 17:42:50 +0200478 def start_Prometheus(self, port=9090):
stevenvanrossemb098cb52016-04-15 13:28:23 +0200479 # prometheus.yml configuration file is located in the same directory as this file
stevenvanrossemc6abf132016-04-14 11:15:58 +0200480 cmd = ["docker",
481 "run",
482 "--rm",
483 "-p", "{0}:9090".format(port),
stevenvanrossemb098cb52016-04-15 13:28:23 +0200484 "-v", "{0}/prometheus.yml:/etc/prometheus/prometheus.yml".format(os.path.dirname(os.path.abspath(__file__))),
stevenvanrossem61fd5282016-04-29 12:41:54 +0200485 "-v", "{0}/profile.rules:/etc/prometheus/profile.rules".format(os.path.dirname(os.path.abspath(__file__))),
stevenvanrossemc6abf132016-04-14 11:15:58 +0200486 "--name", "prometheus",
487 "prom/prometheus"
488 ]
stevenvanrossemb098cb52016-04-15 13:28:23 +0200489 logging.info('Start Prometheus container {0}'.format(cmd))
490 return Popen(cmd)
stevenvanrossemc6abf132016-04-14 11:15:58 +0200491
stevenvanrossema5aeb372016-08-18 17:32:24 +0200492 def start_PushGateway(self, port=PUSHGATEWAY_PORT):
stevenvanrossemadfd06f2016-04-22 10:39:08 +0200493 cmd = ["docker",
494 "run",
495 "-d",
496 "-p", "{0}:9091".format(port),
497 "--name", "pushgateway",
498 "prom/pushgateway"
499 ]
500
501 logging.info('Start Prometheus Push Gateway container {0}'.format(cmd))
502 return Popen(cmd)
503
stevenvanrosseme50b0a72016-08-18 17:42:50 +0200504 def start_cAdvisor(self, port=CADVISOR_PORT):
stevenvanrossemc6abf132016-04-14 11:15:58 +0200505 cmd = ["docker",
506 "run",
507 "--rm",
508 "--volume=/:/rootfs:ro",
509 "--volume=/var/run:/var/run:rw",
510 "--volume=/sys:/sys:ro",
511 "--volume=/var/lib/docker/:/var/lib/docker:ro",
512 "--publish={0}:8080".format(port),
513 "--name=cadvisor",
514 "google/cadvisor:latest"
515 ]
stevenvanrossemb098cb52016-04-15 13:28:23 +0200516 logging.info('Start cAdvisor container {0}'.format(cmd))
517 return Popen(cmd)
stevenvanrossemc6abf132016-04-14 11:15:58 +0200518
519 def stop(self):
stevenvanrossemb098cb52016-04-15 13:28:23 +0200520 # stop the monitoring thread
521 self.start_monitoring = False
522 self.monitor_thread.join()
stevenvanrossem461941c2016-05-10 11:41:29 +0200523 self.monitor_flow_thread.join()
stevenvanrossemb098cb52016-04-15 13:28:23 +0200524
stevenvanrossem9c8a4122016-07-16 03:23:13 +0200525 # these containers are used for monitoring but are started now outside of son-emu
stevenvanrossem2fdfbf42016-05-13 15:08:47 +0200526 '''
stevenvanrossemc6abf132016-04-14 11:15:58 +0200527 if self.prometheus_process is not None:
stevenvanrossemb098cb52016-04-15 13:28:23 +0200528 logging.info('stopping prometheus container')
stevenvanrossemc6abf132016-04-14 11:15:58 +0200529 self.prometheus_process.terminate()
530 self.prometheus_process.kill()
stevenvanrossemb098cb52016-04-15 13:28:23 +0200531 self._stop_container('prometheus')
stevenvanrosseme50b0a72016-08-18 17:42:50 +0200532 '''
stevenvanrossemadfd06f2016-04-22 10:39:08 +0200533 if self.pushgateway_process is not None:
534 logging.info('stopping pushgateway container')
535 self.pushgateway_process.terminate()
536 self.pushgateway_process.kill()
537 self._stop_container('pushgateway')
538
stevenvanrossemb098cb52016-04-15 13:28:23 +0200539 if self.cadvisor_process is not None:
540 logging.info('stopping cadvisor container')
541 self.cadvisor_process.terminate()
542 self.cadvisor_process.kill()
543 self._stop_container('cadvisor')
544
545 def switch_tx_rx(self,metric=''):
546 # when monitoring vnfs, the tx of the datacenter switch is actually the rx of the vnf
547 # so we need to change the metric name to be consistent with the vnf rx or tx
548 if 'tx' in metric:
549 metric = metric.replace('tx','rx')
550 elif 'rx' in metric:
551 metric = metric.replace('rx','tx')
552
553 return metric
554
555 def _stop_container(self, name):
556 cmd = ["docker",
557 "stop",
558 name]
559 Popen(cmd).wait()
560
561 cmd = ["docker",
562 "rm",
563 name]
564 Popen(cmd).wait()
565