View Javadoc

1   /*******************************************************************************
2    * Copyright (c) 2007, 2014 Massimiliano Ziccardi
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain 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,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *******************************************************************************/
16  package it.jnrpe.utils.thresholds;
17  
18  import it.jnrpe.Status;
19  import it.jnrpe.utils.BadThresholdException;
20  
21  import java.math.BigDecimal;
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import org.apache.commons.lang.StringUtils;
26  
27  /**
28   * The threshold interface. This object must be used to verify if a value falls
29   * inside one of the ok, warning or critical ranges.
30   *
31   * According to nagios specifications, the evaluation order is the following:
32   *
33   * <ul>
34   * <li>If no levels are specified, return OK
35   * <li>If an ok level is specified and value is within range, return OK
36   * <li>If a critical level is specified and value is within range, return
37   * CRITICAL
38   * <li>If a warning level is specified and value is within range, return WARNING
39   * <li>If an ok level is specified, return CRITICAL
40   * <li>Otherwise return OK
41   * </ul>
42   *
43   * @author Massimiliano Ziccardi
44   *
45   */
46  class Threshold implements IThreshold {
47  
48      /**
49       * The name of the metric attached to this threshold.
50       */
51      private String metricName = null;
52  
53      /**
54       * The list of ok ranges.
55       */
56      private List<Range> okThresholdList =
57              new ArrayList<Range>();
58      /**
59       * The list of warning ranges.
60       */
61      private List<Range> warningThresholdList =
62              new ArrayList<Range>();
63      /**
64       * The list of critical ranges.
65       */
66      private List<Range> criticalThresholdList =
67              new ArrayList<Range>();
68  
69      /**
70       * The unit of measures. It is a free string and should be used only for
71       * checks where the check plugin do not know the unit of measure (for
72       * example, JMX).
73       */
74      private String unit = null;
75  
76      /**
77       * The prefix to be used to multiply the range values and divide the metric
78       * values.
79       */
80      private Prefixes prefix = null;
81  
82      /**
83       * Build a threshold object parsing the string received. A threshold can be
84       * in the format: <blockquote>
85       * metric={metric},ok={range},warn={range},crit={
86       * range},unit={unit},prefix={SI prefix} </blockquote>
87       *
88       * Where :
89       * <ul>
90       * <li>ok, warn, crit are called "levels"
91       * <li>any of ok, warn, crit, unit or prefix are optional
92       * <li>if ok, warning and critical are not specified, then ok is always
93       * returned
94       * <li>the unit can be specified with plugins that do not know about the
95       * type of value returned (SNMP, Windows performance counters, etc.)
96       * <li>the prefix is used to multiply the input range and possibly for
97       * display data. The prefixes allowed are defined by NIST:
98       * <ul>
99       * <li>http://physics.nist.gov/cuu/Units/prefixes.html
100      * <li>http://physics.nist.gov/cuu/Units/binary.html
101      * </ul>
102      * <li>ok, warning or critical can be repeated to define an additional
103      * range. This allows non-continuous ranges to be defined
104      * <li>warning can be abbreviated to warn or w
105      * <li>critical can be abbreviated to crit or c
106      * </ul>
107      *
108      * @param definition
109      *            The threshold string
110      * @throws BadThresholdException
111      *             -
112      */
113     Threshold(final String definition)
114             throws BadThresholdException {
115         parse(definition);
116     }
117 
118     /**
119      * Parses a threshold definition.
120      *
121      * @param definition
122      *            The threshold definition
123      * @throws BadThresholdException
124      *             -
125      */
126     private void parse(final String definition)
127             throws BadThresholdException {
128         String[] thresholdComponentAry = definition.split(",");
129 
130         for (String thresholdComponent : thresholdComponentAry) {
131             String[] nameValuePair = thresholdComponent.split("=");
132 
133             if (nameValuePair == null || nameValuePair.length != 2
134                     || StringUtils.isEmpty(nameValuePair[0])
135                     || StringUtils.isEmpty(nameValuePair[1])) {
136                 throw new BadThresholdException("Invalid threshold syntax : "
137                         + definition);
138             }
139 
140             if (nameValuePair[0].equalsIgnoreCase("metric")) {
141                 metricName = nameValuePair[1];
142                 continue;
143             }
144             if (nameValuePair[0].equalsIgnoreCase("ok")) {
145 
146                 Range thr = new Range(nameValuePair[1]);
147 
148                 okThresholdList.add(thr);
149                 continue;
150             }
151             if (nameValuePair[0].equalsIgnoreCase("warning")
152                     || nameValuePair[0].equalsIgnoreCase("warn")
153                     || nameValuePair[0].equalsIgnoreCase("w")) {
154                 Range thr = new Range(nameValuePair[1]);
155                 warningThresholdList.add(thr);
156                 continue;
157             }
158             if (nameValuePair[0].equalsIgnoreCase("critical")
159                     || nameValuePair[0].equalsIgnoreCase("crit")
160                     || nameValuePair[0].equalsIgnoreCase("c")) {
161                 Range thr = new Range(nameValuePair[1]);
162                 criticalThresholdList.add(thr);
163                 continue;
164             }
165             if (nameValuePair[0].equalsIgnoreCase("unit")) {
166                 unit = nameValuePair[1];
167                 continue;
168             }
169             if (nameValuePair[0].equalsIgnoreCase("prefix")) {
170                 prefix = Prefixes.fromString(nameValuePair[1].toLowerCase());
171                 continue;
172             }
173 
174             // Threshold specification error
175         }
176     }
177 
178     /**
179      * @return The name of the metric associated to this threshold.
180      */
181     public final String getMetric() {
182         return metricName;
183     }
184 
185     /**
186      * @return The unit of measure attached to the appropriate prefix if
187      *         specified.
188      */
189     public final String getUnitString() {
190         StringBuffer res = new StringBuffer();
191         if (prefix != null) {
192             res.append(prefix.toString());
193         }
194         if (unit != null) {
195             res.append(unit);
196         }
197 
198         if (res.length() != 0) {
199             return res.toString();
200         }
201 
202         return null;
203     }
204 
205     /**
206      * Returns the requested range list as comma separated string.
207      *
208      * @param status
209      *            The status for wich we are requesting the ranges.
210      * @return the requested range list as comma separated string.
211      */
212     public final String getRangesAsString(final Status status) {
213         List<String> ranges = new ArrayList<String>();
214 
215         List<Range> rangeList = null;
216 
217         switch (status) {
218         case OK:
219             rangeList = okThresholdList;
220             break;
221         case WARNING:
222             rangeList = warningThresholdList;
223             break;
224         case CRITICAL:
225         default:
226             rangeList = criticalThresholdList;
227             break;
228         }
229 
230         for (Range r : rangeList) {
231             ranges.add(r.getRangeString());
232         }
233 
234         if (ranges.isEmpty()) {
235             return null;
236         }
237 
238         return StringUtils.join(ranges, ",");
239     }
240 
241     /**
242      * Evaluates this threshold against the passed in value.
243      * The returned status is computed this way:
244      * <ol>
245      * <li>If at least one ok range is specified, if the value falls inside
246      * one of the ok ranges, {@link Status#OK} is returned.
247      * <li>If at lease one critical range is specified, if the value falls
248      * inside one of the critical ranges, {@link Status#CRITICAL} is returned.
249      * <li>If at lease one warning range is specified, if the value falls
250      * inside one of the warning ranges, {@link Status#WARNING} is returned.
251      * <li>If neither of the previous match, but at least an OK range has
252      * been specified, return {@link Status#CRITICAL}.
253      * <li>Otherwise return {@link Status#OK}
254      * </ol>
255      * @param value The value to be evaluated.
256      * @return The computes status.
257      */
258     public final Status evaluate(final BigDecimal value) {
259         if (okThresholdList.isEmpty() && warningThresholdList.isEmpty()
260                 && criticalThresholdList.isEmpty()) {
261             return Status.OK;
262         }
263 
264         BigDecimal convertedValue = value;
265 
266         // Perform evaluation escalation
267         for (Range range : okThresholdList) {
268             if (range.isValueInside(convertedValue, prefix)) {
269                 return Status.OK;
270             }
271         }
272 
273         for (Range range : criticalThresholdList) {
274             if (range.isValueInside(convertedValue, prefix)) {
275                 return Status.CRITICAL;
276             }
277         }
278 
279         for (Range range : warningThresholdList) {
280             if (range.isValueInside(convertedValue, prefix)) {
281                 return Status.WARNING;
282             }
283         }
284 
285         if (!okThresholdList.isEmpty()) {
286             return Status.CRITICAL;
287         }
288 
289         return Status.OK;
290     }
291 
292     /**
293      * @param metric The name of the metric we want to evaluate.
294      * @return <code>true</code> if this threshold is about the passed in
295      * metric.
296      */
297     public final boolean isAboutMetric(final String metric) {
298         return metric.equalsIgnoreCase(metricName);
299     }
300 }