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 }