blob: 5b234cd5e01113cf4311a6eaf56d6cf48b4843a4 [file] [log] [blame]
Brian O'Connorc67f9fa2014-08-07 18:17:46 -07001/**
2 * Copyright 2013, Big Switch Networks, Inc.
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
17package net.floodlightcontroller.util;
18
19import java.io.BufferedReader;
20import java.io.FileInputStream;
21import java.io.IOException;
22import java.io.InputStreamReader;
23import java.util.concurrent.Executors;
24import java.util.concurrent.ScheduledExecutorService;
25import java.util.concurrent.ScheduledFuture;
26import java.util.concurrent.TimeUnit;
27
28import org.slf4j.Logger;
29
30import net.floodlightcontroller.core.annotations.LogMessageDocs;
31import net.floodlightcontroller.core.annotations.LogMessageDoc;
32
33public class LoadMonitor implements Runnable {
34
35 public enum LoadLevel {
36 OK,
37 HIGH,
38 VERYHIGH,
39 }
40
41 public LoadLevel getLoadLevel() {
42 return loadlevel ;
43 }
44
45 public double getLoad() {
46 return load ;
47 }
48
49 public static final int LOADMONITOR_SAMPLING_INTERVAL = 1000; // mili-sec
50 public static final double THRESHOLD_HIGH = 0.90;
51 public static final double THRESHOLD_VERYHIGH = 0.95;
52 public static final int MAX_LOADED_ITERATIONS = 5;
53 public static final int MAX_LOAD_HISTORY = 5;
54
55 protected volatile double load;
56 protected volatile LoadLevel loadlevel;
57 protected int itersLoaded;
58
59 protected boolean isLinux;
60 protected int numcores;
61 protected int jiffyNanos;
62 protected long[] lastNanos;
63 protected long[] lastIdle;
64 protected Logger log;
65
66 public LoadMonitor(Logger log_) {
67 log = log_;
68 loadlevel = LoadLevel.OK;
69 load = 0.0;
70 itersLoaded = 0;
71
72 lastNanos = new long[MAX_LOAD_HISTORY];
73 lastIdle = new long[MAX_LOAD_HISTORY];
74 for (int i=0 ; i<MAX_LOAD_HISTORY ; i++) {
75 lastNanos[i] = 0L;
76 lastIdle[i] = 0L;
77 }
78
79 isLinux = System.getProperty("os.name").equals("Linux");
80 numcores = 1;
81 jiffyNanos = 10 * 1000 * 1000;
82 if (isLinux) {
83 try {
84 numcores = Integer.parseInt(
85 this.runcmd("/usr/bin/nproc"));
86 jiffyNanos = (1000 * 1000 * 1000) / Integer.parseInt(
87 this.runcmd("/usr/bin/getconf CLK_TCK"));
88 }
89 catch (NumberFormatException ex) {
90 if (log != null) {
91 // Log message documented on runcmd function
92 log.error("Exception in inializing load monitor ", ex);
93 }
94 else {
95 ex.printStackTrace();
96 }
97 }
98 }
99 }
100
101 @Override
102 @LogMessageDocs({
103 @LogMessageDoc(
104 message="System under very heavy load, dropping some packet-ins",
105 explanation="We detcted that the system was under very heavy" +
106 " load, dropping some packet-ins temporarily"),
107 @LogMessageDoc(
108 message="System under heavy load, dropping some new flows",
109 explanation="We detcted that the system was under heavy load," +
110 " dropping some new flows temporarily")
111 })
112 public void run() {
113 if (!isLinux) return;
114
115 long currNanos = System.nanoTime();
116 long currIdle = this.readIdle();
117 for (int i=0 ; i < (MAX_LOAD_HISTORY - 1) ; i++) {
118 lastNanos[i] = lastNanos[i+1];
119 lastIdle[i] = lastIdle[i+1];
120 }
121 lastNanos[MAX_LOAD_HISTORY - 1] = currNanos;
122 lastIdle[MAX_LOAD_HISTORY - 1] = currIdle;
123
124 if (itersLoaded >= MAX_LOADED_ITERATIONS) {
125 loadlevel = LoadLevel.OK;
126 itersLoaded = 0;
127 return;
128 }
129
130 long nanos = lastNanos[MAX_LOAD_HISTORY - 1] - lastNanos[0];
131 long idle = lastIdle[MAX_LOAD_HISTORY - 1] - lastIdle[0];
132 load =
133 1.0 - ((double)(idle * jiffyNanos) / (double)(nanos * numcores));
134
135 if (load > THRESHOLD_VERYHIGH) {
136 loadlevel = LoadLevel.VERYHIGH;
137 itersLoaded += 1;
138 String msg = "System under very heavy load, dropping packet-ins.";
139
140 if (log != null) {
141 log.error(msg);
142 }
143 else {
144 System.out.println(msg);
145 }
146 return;
147 }
148
149 if (load > THRESHOLD_HIGH) {
150 loadlevel = LoadLevel.HIGH;
151 itersLoaded += 1;
152 String msg = "System under heavy load, dropping new flows.";
153
154 if (log != null) {
155 log.error(msg);
156 }
157 else {
158 System.out.println(msg);
159 }
160 return;
161 }
162
163 loadlevel = LoadLevel.OK;
164 itersLoaded = 0;
165 return;
166 }
167
168 @LogMessageDoc(
169 message="Exception in reading load monitor params, using defaults",
170 explanation="There was an error in inializing load monitor's props," +
171 " using default parameters")
172 protected String runcmd(String cmd) {
173 String line;
174 StringBuilder ret = new StringBuilder();
175 try {
176 Process p = Runtime.getRuntime().exec(cmd);
177 BufferedReader input =
178 new BufferedReader(
179 new InputStreamReader(p.getInputStream()));
180 while ((line = input.readLine()) != null) {
181 ret.append(line);
182 }
183 input.close();
184 p.waitFor();
185 }
186 catch (InterruptedException ex) {
187 if (log != null) {
188 log.error("Exception in inializing load monitor ", ex);
189 }
190 else {
191 ex.printStackTrace();
192 }
193 }
194 catch (IOException ex) {
195 if (log != null) {
196 log.error("Exception in inializing load monitor ", ex);
197 }
198 else {
199 ex.printStackTrace();
200 }
201 }
202 return ret.toString();
203
204 }
205
206 protected long readIdle() {
207 long idle = 0;
208 FileInputStream fs = null;
209 BufferedReader reader = null;
210 try {
211 try {
212 fs = new FileInputStream("/proc/stat");
213 reader = new BufferedReader(new InputStreamReader(fs));
214 String line = reader.readLine();
215 if (line == null) throw new IOException("Empty file");
216 idle = Long.parseLong(line.split("\\s+")[4]);
217 } finally {
218 if (reader != null)
219 reader.close();
220 if (fs != null)
221 fs.close();
222 }
223 } catch (IOException ex) {
224 log.error("Error reading idle time from /proc/stat", ex);
225 }
226 return idle;
227
228 }
229
230 public ScheduledFuture<?> startMonitoring(ScheduledExecutorService ses)
231 {
232 ScheduledFuture<?> monitorTask =
233 ses.scheduleAtFixedRate(
234 this, 0,
235 LOADMONITOR_SAMPLING_INTERVAL, TimeUnit.MILLISECONDS);
236 return monitorTask;
237 }
238
239 /*
240 * For testing
241 */
242 public ScheduledFuture<?> printMonitoring(ScheduledExecutorService ses)
243 {
244 final LoadMonitor mon = this;
245 ScheduledFuture<?> monitorTask =
246 ses.scheduleAtFixedRate(
247 new Runnable() {
248 public void run() {
249 System.out.println(mon.getLoad());
250 }
251 }, LOADMONITOR_SAMPLING_INTERVAL/2,
252 LOADMONITOR_SAMPLING_INTERVAL, TimeUnit.MILLISECONDS);
253 return monitorTask;
254 }
255
256 public static void main(String[] args) {
257 final LoadMonitor monitor = new LoadMonitor(null);
258 final ScheduledExecutorService scheduler =
259 Executors.newScheduledThreadPool(1);
260 final ScheduledFuture<?> monitorTask =
261 monitor.startMonitoring(scheduler);
262 final ScheduledFuture<?> printTask =
263 monitor.printMonitoring(scheduler);
264
265 // Run the tasks for 2 minutes
266 scheduler.schedule(
267 new Runnable() {
268 public void run() {
269 monitorTask.cancel(true);
270 printTask.cancel(true);
271 }
272 }, 5*60, TimeUnit.SECONDS);
273 }
274
275}