| /** |
| * Copyright 2013, Big Switch Networks, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may |
| * not use this file except in compliance with the License. You may obtain |
| * a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| **/ |
| |
| package net.floodlightcontroller.util; |
| |
| import java.io.BufferedReader; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.ScheduledExecutorService; |
| import java.util.concurrent.ScheduledFuture; |
| import java.util.concurrent.TimeUnit; |
| |
| import org.slf4j.Logger; |
| |
| import net.floodlightcontroller.core.annotations.LogMessageDocs; |
| import net.floodlightcontroller.core.annotations.LogMessageDoc; |
| |
| public class LoadMonitor implements Runnable { |
| |
| public enum LoadLevel { |
| OK, |
| HIGH, |
| VERYHIGH, |
| } |
| |
| public LoadLevel getLoadLevel() { |
| return loadlevel ; |
| } |
| |
| public double getLoad() { |
| return load ; |
| } |
| |
| public static final int LOADMONITOR_SAMPLING_INTERVAL = 1000; // mili-sec |
| public static final double THRESHOLD_HIGH = 0.90; |
| public static final double THRESHOLD_VERYHIGH = 0.95; |
| public static final int MAX_LOADED_ITERATIONS = 5; |
| public static final int MAX_LOAD_HISTORY = 5; |
| |
| protected volatile double load; |
| protected volatile LoadLevel loadlevel; |
| protected int itersLoaded; |
| |
| protected boolean isLinux; |
| protected int numcores; |
| protected int jiffyNanos; |
| protected long[] lastNanos; |
| protected long[] lastIdle; |
| protected Logger log; |
| |
| public LoadMonitor(Logger log_) { |
| log = log_; |
| loadlevel = LoadLevel.OK; |
| load = 0.0; |
| itersLoaded = 0; |
| |
| lastNanos = new long[MAX_LOAD_HISTORY]; |
| lastIdle = new long[MAX_LOAD_HISTORY]; |
| for (int i=0 ; i<MAX_LOAD_HISTORY ; i++) { |
| lastNanos[i] = 0L; |
| lastIdle[i] = 0L; |
| } |
| |
| isLinux = System.getProperty("os.name").equals("Linux"); |
| numcores = 1; |
| jiffyNanos = 10 * 1000 * 1000; |
| if (isLinux) { |
| try { |
| numcores = Integer.parseInt( |
| this.runcmd("/usr/bin/nproc")); |
| jiffyNanos = (1000 * 1000 * 1000) / Integer.parseInt( |
| this.runcmd("/usr/bin/getconf CLK_TCK")); |
| } |
| catch (NumberFormatException ex) { |
| if (log != null) { |
| // Log message documented on runcmd function |
| log.error("Exception in inializing load monitor ", ex); |
| } |
| else { |
| ex.printStackTrace(); |
| } |
| } |
| } |
| } |
| |
| @Override |
| @LogMessageDocs({ |
| @LogMessageDoc( |
| message="System under very heavy load, dropping some packet-ins", |
| explanation="We detcted that the system was under very heavy" + |
| " load, dropping some packet-ins temporarily"), |
| @LogMessageDoc( |
| message="System under heavy load, dropping some new flows", |
| explanation="We detcted that the system was under heavy load," + |
| " dropping some new flows temporarily") |
| }) |
| public void run() { |
| if (!isLinux) return; |
| |
| long currNanos = System.nanoTime(); |
| long currIdle = this.readIdle(); |
| for (int i=0 ; i < (MAX_LOAD_HISTORY - 1) ; i++) { |
| lastNanos[i] = lastNanos[i+1]; |
| lastIdle[i] = lastIdle[i+1]; |
| } |
| lastNanos[MAX_LOAD_HISTORY - 1] = currNanos; |
| lastIdle[MAX_LOAD_HISTORY - 1] = currIdle; |
| |
| if (itersLoaded >= MAX_LOADED_ITERATIONS) { |
| loadlevel = LoadLevel.OK; |
| itersLoaded = 0; |
| return; |
| } |
| |
| long nanos = lastNanos[MAX_LOAD_HISTORY - 1] - lastNanos[0]; |
| long idle = lastIdle[MAX_LOAD_HISTORY - 1] - lastIdle[0]; |
| load = |
| 1.0 - ((double)(idle * jiffyNanos) / (double)(nanos * numcores)); |
| |
| if (load > THRESHOLD_VERYHIGH) { |
| loadlevel = LoadLevel.VERYHIGH; |
| itersLoaded += 1; |
| String msg = "System under very heavy load, dropping packet-ins."; |
| |
| if (log != null) { |
| log.error(msg); |
| } |
| else { |
| System.out.println(msg); |
| } |
| return; |
| } |
| |
| if (load > THRESHOLD_HIGH) { |
| loadlevel = LoadLevel.HIGH; |
| itersLoaded += 1; |
| String msg = "System under heavy load, dropping new flows."; |
| |
| if (log != null) { |
| log.error(msg); |
| } |
| else { |
| System.out.println(msg); |
| } |
| return; |
| } |
| |
| loadlevel = LoadLevel.OK; |
| itersLoaded = 0; |
| return; |
| } |
| |
| @LogMessageDoc( |
| message="Exception in reading load monitor params, using defaults", |
| explanation="There was an error in inializing load monitor's props," + |
| " using default parameters") |
| protected String runcmd(String cmd) { |
| String line; |
| StringBuilder ret = new StringBuilder(); |
| try { |
| Process p = Runtime.getRuntime().exec(cmd); |
| BufferedReader input = |
| new BufferedReader( |
| new InputStreamReader(p.getInputStream())); |
| while ((line = input.readLine()) != null) { |
| ret.append(line); |
| } |
| input.close(); |
| p.waitFor(); |
| } |
| catch (InterruptedException ex) { |
| if (log != null) { |
| log.error("Exception in inializing load monitor ", ex); |
| } |
| else { |
| ex.printStackTrace(); |
| } |
| } |
| catch (IOException ex) { |
| if (log != null) { |
| log.error("Exception in inializing load monitor ", ex); |
| } |
| else { |
| ex.printStackTrace(); |
| } |
| } |
| return ret.toString(); |
| |
| } |
| |
| protected long readIdle() { |
| long idle = 0; |
| FileInputStream fs = null; |
| BufferedReader reader = null; |
| try { |
| try { |
| fs = new FileInputStream("/proc/stat"); |
| reader = new BufferedReader(new InputStreamReader(fs)); |
| String line = reader.readLine(); |
| if (line == null) throw new IOException("Empty file"); |
| idle = Long.parseLong(line.split("\\s+")[4]); |
| } finally { |
| if (reader != null) |
| reader.close(); |
| if (fs != null) |
| fs.close(); |
| } |
| } catch (IOException ex) { |
| log.error("Error reading idle time from /proc/stat", ex); |
| } |
| return idle; |
| |
| } |
| |
| public ScheduledFuture<?> startMonitoring(ScheduledExecutorService ses) |
| { |
| ScheduledFuture<?> monitorTask = |
| ses.scheduleAtFixedRate( |
| this, 0, |
| LOADMONITOR_SAMPLING_INTERVAL, TimeUnit.MILLISECONDS); |
| return monitorTask; |
| } |
| |
| /* |
| * For testing |
| */ |
| public ScheduledFuture<?> printMonitoring(ScheduledExecutorService ses) |
| { |
| final LoadMonitor mon = this; |
| ScheduledFuture<?> monitorTask = |
| ses.scheduleAtFixedRate( |
| new Runnable() { |
| public void run() { |
| System.out.println(mon.getLoad()); |
| } |
| }, LOADMONITOR_SAMPLING_INTERVAL/2, |
| LOADMONITOR_SAMPLING_INTERVAL, TimeUnit.MILLISECONDS); |
| return monitorTask; |
| } |
| |
| public static void main(String[] args) { |
| final LoadMonitor monitor = new LoadMonitor(null); |
| final ScheduledExecutorService scheduler = |
| Executors.newScheduledThreadPool(1); |
| final ScheduledFuture<?> monitorTask = |
| monitor.startMonitoring(scheduler); |
| final ScheduledFuture<?> printTask = |
| monitor.printMonitoring(scheduler); |
| |
| // Run the tasks for 2 minutes |
| scheduler.schedule( |
| new Runnable() { |
| public void run() { |
| monitorTask.cancel(true); |
| printTask.cancel(true); |
| } |
| }, 5*60, TimeUnit.SECONDS); |
| } |
| |
| } |