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.plugin;
17  
18  import it.jnrpe.ICommandLine;
19  import it.jnrpe.Status;
20  import it.jnrpe.plugin.utils.SshUtils;
21  import it.jnrpe.plugin.utils.Utils;
22  import it.jnrpe.plugins.Metric;
23  import it.jnrpe.plugins.MetricGatheringException;
24  import it.jnrpe.plugins.PluginBase;
25  import it.jnrpe.plugins.annotations.Option;
26  import it.jnrpe.plugins.annotations.Plugin;
27  import it.jnrpe.plugins.annotations.PluginOptions;
28  import it.jnrpe.utils.BadThresholdException;
29  import it.jnrpe.utils.thresholds.ThresholdsEvaluatorBuilder;
30  
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.math.BigDecimal;
34  import java.util.ArrayList;
35  import java.util.Collection;
36  import java.util.List;
37  
38  import com.jcraft.jsch.Channel;
39  import com.jcraft.jsch.ChannelExec;
40  import com.jcraft.jsch.JSchException;
41  import com.jcraft.jsch.Session;
42  
43  /**
44   * This plugins executes remote commands through ssh and returns the command's
45   * output (if successul).
46   * 
47   * @author Frederico Campos
48   * 
49   */
50  @Plugin(name = "CHECK_BY_SSH", description = "Use ssh to execute commands on a remote host.\n"
51  		+ "EXAMPLES:\n"
52  		+ "The example will be based upon the following command definition (ini file)\n\n"
53  		+ "check_by_ssh : CHECK_BY_SSH --hostname $ARG1$ --port $ARG2$ --password $ARG3$ -C $ARG4$\n"
54  		+ "check_nrpe -H myjnrpeserver -c check_ssh -a myhostname 22 password uptime")
55  @PluginOptions({
56  		@Option(shortName = "h", longName = "hostname", description = "IP or hostname", required = true, hasArgs = true, argName = "hostname", optionalArgs = false, option = "hostname"),
57  		@Option(shortName = "p", longName = "port", description = "Port number. Default is 22.", required = false, hasArgs = true, argName = "port", optionalArgs = false, option = "port"),
58  		@Option(shortName = "u", longName = "username", description = "Username.", required = true, hasArgs = true, argName = "username", optionalArgs = false, option = "username"),
59  		@Option(shortName = "P", longName = "password", description = "Password.", required = true, hasArgs = true, argName = "hostname", optionalArgs = false, option = "password"),
60  		@Option(shortName = "t", longName = "timeout", description = "Seconds before connection times out (default: 10)", required = false, hasArgs = true, argName = "timeout", optionalArgs = false, option = "timeout"),
61  		@Option(shortName = "w", longName = "warning", description = "Response time to result in warning status (seconds)", required = false, hasArgs = true, argName = "warning", optionalArgs = false, option = "warning"),
62  		@Option(shortName = "c", longName = "critical", description = "Response time to result in critical status (seconds)", required = false, hasArgs = true, argName = "critical", optionalArgs = false, option = "critical"),
63  		@Option(shortName = "C", longName = "command", description = "command to execute on the remote machine", required = true, hasArgs = true, argName = "command", optionalArgs = false, option = "command") })
64  public class CheckBySsh extends PluginBase {
65  
66  	/**
67  	 * Possible unix return codes
68  	 */
69  	/**
70  	 * command not found
71  	 */
72  	private static final int ERR_CMD_NOT_FOUND = 127;
73  
74  	/**
75  	 * no permision
76  	 */
77  	private static final int ERR_NO_PERMISSION = 126;
78  
79  	@Override
80  	protected String getPluginName() {
81  		return "CHECK_BY_SSH";
82  	}
83  
84  	@Override
85  	public final void configureThresholdEvaluatorBuilder(
86  			final ThresholdsEvaluatorBuilder thrb, final ICommandLine cl)
87  			throws BadThresholdException {
88  		thrb.withLegacyThreshold("session", "1", null, "0");
89  		thrb.withLegacyThreshold("response", null,
90  				cl.getOptionValue("warning"), cl.getOptionValue("critical"));
91  		thrb.withLegacyThreshold("result", "1", null, "0");
92  
93  	}
94  
95  	@Override
96  	public final Collection<Metric> gatherMetrics(final ICommandLine cl)
97  			throws MetricGatheringException {
98  		List<Metric> metrics = new ArrayList<Metric>();
99  		Session session = null;
100 		Channel channel = null;
101 		String command = cl.getOptionValue("command");
102 		InputStream in = null;
103 		boolean hasSession = false;
104 		long then = System.currentTimeMillis();
105 		try {
106 			session = SshUtils.getSession(cl);
107 			channel = session.openChannel("exec");
108 
109 			hasSession = true;
110 			metrics.add(new Metric("session", "", new BigDecimal(1), null, null));
111 		} catch (Exception e) {
112 			// metrics.add(new Metric("session",
113 			// "SSH not started, permission denied. " + e.getMessage(),
114 			// new BigDecimal(0), null, null));
115 			log.debug(e.getMessage(), e);
116 			throw new MetricGatheringException(
117 					"SSH not started, permission denied.", Status.UNKNOWN, e);
118 		}
119 		try {
120 			if (hasSession) {
121 				((ChannelExec) channel).setCommand(command);
122 				channel.setInputStream(null);
123 				((ChannelExec) channel).setErrStream(System.err);
124 				in = channel.getInputStream();
125 			} else {
126 				return metrics;
127 			}
128 		} catch (IOException e1) {
129 			// e1.printStackTrace();
130 			throw new MetricGatheringException(e1.getMessage(), Status.UNKNOWN,
131 					e1);
132 		}
133 
134 		try {
135 			channel.connect();
136 			metrics.add(new Metric("connected", "", new BigDecimal(1), null,
137 					null));
138 		} catch (JSchException e2) {
139 			// e2.printStackTrace();
140 			throw new MetricGatheringException(e2.getMessage(), Status.UNKNOWN,
141 					e2);
142 		}
143 
144 		StringBuffer sb = new StringBuffer();
145 		byte[] tmp = new byte[1024];
146 		int exitStatus = 0;
147 		while (true) {
148 			try {
149 				while (in.available() > 0) {
150 					int i = in.read(tmp, 0, 1024);
151 					if (i < 0)
152 						break;
153 					sb.append(new String(tmp, 0, i, "UTF-8"));
154 				}
155 			} catch (IOException e1) {
156 				throw new MetricGatheringException(e1.getMessage(),
157 						Status.UNKNOWN, e1);
158 			}
159 			if (channel.isClosed()) {
160 				exitStatus = channel.getExitStatus();
161 				break;
162 			}
163 			try {
164 				Thread.sleep(1000);
165 			} catch (Exception e) {
166 				e.printStackTrace();
167 			}
168 		}
169 		if (channel != null) {
170 			channel.disconnect();
171 		}
172 		if (session != null) {
173 			session.disconnect();
174 		}
175 		long response = (System.currentTimeMillis() - then) / 1000;
176 		metrics.add(new Metric("response", "", new BigDecimal(response), null,
177 				null));
178 		// sb.append("\nexit-status: " + channel.getExitStatus());
179 		String msg = "";
180 		switch (channel.getExitStatus()) {
181 		case ERR_CMD_NOT_FOUND:
182 			msg = "Command not found.";
183 			break;
184 		case ERR_NO_PERMISSION:
185 			msg = "Not enough permission to execute command.";
186 			break;
187 		default:
188 			break;
189 		}
190 
191 		metrics.add(new Metric("result", msg + " " + sb.toString(),
192 				new BigDecimal(Utils.getIntValue(exitStatus == 0)), null, null));
193 		return metrics;
194 	}
195 }