View Javadoc
1   /*
2    * Licensed under the GPL License. You may not use this file except in compliance with the License.
3    * You may obtain a copy of the License at
4    *
5    *   https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
6    *
7    * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
8    * WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
9    * PURPOSE.
10   */
11  package psiprobe.beans.stats.listeners;
12  
13  import java.util.HashMap;
14  import java.util.LinkedList;
15  import java.util.Map;
16  
17  import psiprobe.Utils;
18  
19  /**
20   * The listener interface for receiving flap events. The class that is interested in processing a
21   * flap event implements this interface, and the object created with that class is registered with a
22   * component using the component's {@code addFlapListener} method. When the flap event occurs, that
23   * object's appropriate method is invoked.
24   *
25   * @see <a href="https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/3/en/flapping.html">
26   *      Detection and Handling of State Flapping (nagios)</a>
27   */
28  public abstract class AbstractFlapListener extends AbstractThresholdListener {
29  
30    /** The default flap interval. */
31    private int defaultFlapInterval;
32  
33    /** The default flap start threshold. */
34    private float defaultFlapStartThreshold;
35  
36    /** The default flap stop threshold. */
37    private float defaultFlapStopThreshold;
38  
39    /** The default flap low weight. */
40    private float defaultFlapLowWeight;
41  
42    /** The default flap high weight. */
43    private float defaultFlapHighWeight;
44  
45    /** The flaps. */
46    private final Map<String, LinkedList<Boolean>> flaps = new HashMap<>();
47  
48    /** The flapping states. */
49    private final Map<String, Boolean> flappingStates = new HashMap<>();
50  
51    /**
52     * Flapping started.
53     *
54     * @param sce the sce
55     */
56    protected abstract void flappingStarted(StatsCollectionEvent sce);
57  
58    /**
59     * Above threshold flapping stopped.
60     *
61     * @param sce the sce
62     */
63    protected abstract void aboveThresholdFlappingStopped(StatsCollectionEvent sce);
64  
65    /**
66     * Below threshold flapping stopped.
67     *
68     * @param sce the sce
69     */
70    protected abstract void belowThresholdFlappingStopped(StatsCollectionEvent sce);
71  
72    /**
73     * Above threshold not flapping.
74     *
75     * @param sce the sce
76     */
77    protected abstract void aboveThresholdNotFlapping(StatsCollectionEvent sce);
78  
79    /**
80     * Below threshold not flapping.
81     *
82     * @param sce the sce
83     */
84    protected abstract void belowThresholdNotFlapping(StatsCollectionEvent sce);
85  
86    @Override
87    protected void crossedAboveThreshold(StatsCollectionEvent sce) {
88      statsCollected(sce, true, true);
89    }
90  
91    @Override
92    protected void crossedBelowThreshold(StatsCollectionEvent sce) {
93      statsCollected(sce, true, false);
94    }
95  
96    @Override
97    protected void remainedAboveThreshold(StatsCollectionEvent sce) {
98      statsCollected(sce, false, true);
99    }
100 
101   @Override
102   protected void remainedBelowThreshold(StatsCollectionEvent sce) {
103     statsCollected(sce, false, false);
104   }
105 
106   @Override
107   public void reset() {
108     flaps.clear();
109     flappingStates.clear();
110     super.reset();
111   }
112 
113   /**
114    * Stats collected.
115    *
116    * @param sce the sce
117    * @param crossedThreshold the crossed threshold
118    * @param above the above
119    */
120   protected void statsCollected(StatsCollectionEvent sce, boolean crossedThreshold, boolean above) {
121     String name = sce.getName();
122     boolean flappingStateChanged = checkFlappingStateChanged(name, crossedThreshold);
123     boolean flappingState = getFlappingState(name);
124     if (flappingStateChanged) {
125       if (flappingState) {
126         flappingStarted(sce);
127       } else if (above) {
128         aboveThresholdFlappingStopped(sce);
129       } else {
130         belowThresholdFlappingStopped(sce);
131       }
132     } else if (crossedThreshold) {
133       if (above) {
134         aboveThresholdNotFlapping(sce);
135       } else {
136         belowThresholdNotFlapping(sce);
137       }
138     }
139   }
140 
141   /**
142    * Check flapping state changed.
143    *
144    * @param name the name
145    * @param crossedThreshold the crossed threshold
146    *
147    * @return true, if successful
148    */
149   protected boolean checkFlappingStateChanged(String name, boolean crossedThreshold) {
150     addFlap(name, crossedThreshold);
151     boolean oldFlappingState = getFlappingState(name);
152     float transitionPercent = calculateStateTransitionPercentage(name, oldFlappingState);
153     boolean newFlappingState;
154     if (oldFlappingState) {
155       newFlappingState = transitionPercent <= getFlapStopThreshold(name);
156     } else {
157       newFlappingState = transitionPercent > getFlapStartThreshold(name);
158     }
159     setFlappingState(name, newFlappingState);
160     return oldFlappingState != newFlappingState;
161   }
162 
163   /**
164    * Calculate state transition percentage.
165    *
166    * @param name the name
167    * @param flapping the flapping
168    *
169    * @return the float
170    */
171   protected float calculateStateTransitionPercentage(String name, boolean flapping) {
172     int flapInterval = getFlapInterval(name);
173     LinkedList<Boolean> list = getFlaps(name);
174     float lowWeight = getFlapLowWeight(name);
175     float highWeight = getFlapHighWeight(name);
176     float weightRange = highWeight - lowWeight;
177     float result = 0;
178     for (int i = list.size() - 1; i >= 0; i--) {
179       boolean thisFlap = list.get(i);
180       if (flapping != thisFlap) {
181         float weight = lowWeight + weightRange * i / (flapInterval - 1);
182         result += weight;
183       }
184     }
185     return result / flapInterval;
186   }
187 
188   /**
189    * Adds the flap.
190    *
191    * @param name the name
192    * @param flap the flap
193    */
194   protected void addFlap(String name, boolean flap) {
195     int flapInterval = getFlapInterval(name);
196     LinkedList<Boolean> list = getFlaps(name);
197     Boolean value = flap;
198     list.addLast(value);
199     while (list.size() > flapInterval) {
200       list.removeFirst();
201     }
202   }
203 
204   /**
205    * Gets the flapping state.
206    *
207    * @param name the name
208    *
209    * @return the flapping state
210    */
211   protected boolean getFlappingState(String name) {
212     Boolean flapping = flappingStates.get(name);
213     if (flapping == null) {
214       flapping = Boolean.FALSE;
215       setFlappingState(name, false);
216     }
217     return flapping;
218   }
219 
220   /**
221    * Sets the flapping state.
222    *
223    * @param name the name
224    * @param flapping the flapping
225    */
226   protected void setFlappingState(String name, boolean flapping) {
227     flappingStates.put(name, flapping);
228   }
229 
230   /**
231    * Gets the flaps.
232    *
233    * @param name the name
234    *
235    * @return the flaps
236    */
237   protected LinkedList<Boolean> getFlaps(String name) {
238     return flaps.computeIfAbsent(name, s -> new LinkedList<>());
239   }
240 
241   /**
242    * Gets the flap interval.
243    *
244    * @param name the name
245    *
246    * @return the flap interval
247    */
248   protected int getFlapInterval(String name) {
249     String interval = getPropertyValue(name, "flapInterval");
250     return Utils.toInt(interval, getDefaultFlapInterval());
251   }
252 
253   /**
254    * Gets the flap start threshold.
255    *
256    * @param name the name
257    *
258    * @return the flap start threshold
259    */
260   protected float getFlapStartThreshold(String name) {
261     String startThreshold = getPropertyValue(name, "flapStartThreshold");
262     return Utils.toFloat(startThreshold, getDefaultFlapStartThreshold());
263   }
264 
265   /**
266    * Gets the flap stop threshold.
267    *
268    * @param name the name
269    *
270    * @return the flap stop threshold
271    */
272   protected float getFlapStopThreshold(String name) {
273     String stopThreshold = getPropertyValue(name, "flapStopThreshold");
274     return Utils.toFloat(stopThreshold, getDefaultFlapStopThreshold());
275   }
276 
277   /**
278    * Gets the flap low weight.
279    *
280    * @param name the name
281    *
282    * @return the flap low weight
283    */
284   protected float getFlapLowWeight(String name) {
285     String lowWeight = getPropertyValue(name, "flapLowWeight");
286     return Utils.toFloat(lowWeight, getDefaultFlapLowWeight());
287   }
288 
289   /**
290    * Gets the flap high weight.
291    *
292    * @param name the name
293    *
294    * @return the flap high weight
295    */
296   protected float getFlapHighWeight(String name) {
297     String highWeight = getPropertyValue(name, "flapHighWeight");
298     return Utils.toFloat(highWeight, getDefaultFlapHighWeight());
299   }
300 
301   /**
302    * Gets the default flap interval.
303    *
304    * @return the default flap interval
305    */
306   public int getDefaultFlapInterval() {
307     return defaultFlapInterval;
308   }
309 
310   /**
311    * Sets the default flap interval.
312    *
313    * @param defaultFlapInterval the new default flap interval
314    */
315   public void setDefaultFlapInterval(int defaultFlapInterval) {
316     this.defaultFlapInterval = defaultFlapInterval;
317   }
318 
319   /**
320    * Gets the default flap start threshold.
321    *
322    * @return the default flap start threshold
323    */
324   public float getDefaultFlapStartThreshold() {
325     return defaultFlapStartThreshold;
326   }
327 
328   /**
329    * Sets the default flap start threshold.
330    *
331    * @param defaultFlapStartThreshold the new default flap start threshold
332    */
333   public void setDefaultFlapStartThreshold(float defaultFlapStartThreshold) {
334     this.defaultFlapStartThreshold = defaultFlapStartThreshold;
335   }
336 
337   /**
338    * Gets the default flap stop threshold.
339    *
340    * @return the default flap stop threshold
341    */
342   public float getDefaultFlapStopThreshold() {
343     return defaultFlapStopThreshold;
344   }
345 
346   /**
347    * Sets the default flap stop threshold.
348    *
349    * @param defaultFlapStopThreshold the new default flap stop threshold
350    */
351   public void setDefaultFlapStopThreshold(float defaultFlapStopThreshold) {
352     this.defaultFlapStopThreshold = defaultFlapStopThreshold;
353   }
354 
355   /**
356    * Gets the default flap low weight.
357    *
358    * @return the default flap low weight
359    */
360   public float getDefaultFlapLowWeight() {
361     return defaultFlapLowWeight;
362   }
363 
364   /**
365    * Sets the default flap low weight.
366    *
367    * @param defaultFlapLowWeight the new default flap low weight
368    */
369   public void setDefaultFlapLowWeight(float defaultFlapLowWeight) {
370     this.defaultFlapLowWeight = defaultFlapLowWeight;
371   }
372 
373   /**
374    * Gets the default flap high weight.
375    *
376    * @return the default flap high weight
377    */
378   public float getDefaultFlapHighWeight() {
379     return defaultFlapHighWeight;
380   }
381 
382   /**
383    * Sets the default flap high weight.
384    *
385    * @param defaultFlapHighWeight the new default flap high weight
386    */
387   public void setDefaultFlapHighWeight(float defaultFlapHighWeight) {
388     this.defaultFlapHighWeight = defaultFlapHighWeight;
389   }
390 
391 }