/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 backtype.storm.command;
import backtype.storm.utils.ShellUtils;
import backtype.storm.utils.Utils;
import com.alibaba.jstorm.client.ConfigExtension;
import com.alibaba.jstorm.daemon.supervisor.HealthStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class HealthCheck {
private static final Logger LOG = LoggerFactory.getLogger(HealthCheck.class);
private static final String NO_RESOURCES = "no_resource";
public static HealthStatus check() {
Map conf = Utils.readStormConfig();
long timeout = ConfigExtension.getStormHealthTimeoutMs(conf);
// 1. check panic dir
String panicPath = ConfigExtension.getStormMachineResourcePanicCheckDir(conf);
if (!isHealthyUnderPath(panicPath, timeout)) {
return HealthStatus.PANIC;
}
// 2. check error dir
String errorPath = ConfigExtension.getStormMachineResourceErrorCheckDir(conf);
if (!isHealthyUnderPath(errorPath, timeout)) {
return HealthStatus.ERROR;
}
// 3. check warn dir
String warnPath = ConfigExtension.getStormMachineResourceWarningCheckDir(conf);
if (!isHealthyUnderPath(warnPath, timeout)) {
return HealthStatus.WARN;
}
return HealthStatus.INFO;
}
/**
* return false if the health check failed when running the scripts under the path
**/
private static boolean isHealthyUnderPath(String path, long timeout) {
if (path == null) {
return true;
}
List<String> commands = generateCommands(path);
if (commands != null && commands.size() > 0) {
for (String command : commands) {
ScriptProcessLauncher scriptProcessLauncher = new ScriptProcessLauncher(command, timeout);
ExitStatus exit = scriptProcessLauncher.launch();
if (exit.equals(ExitStatus.FAILED)) {
return false;
}
}
return true;
} else {
return true;
}
}
private static List<String> generateCommands(String filePath) {
File path = new File(filePath);
List<String> commands = new ArrayList<>();
if (path.exists()) {
File[] files = path.listFiles();
if (files == null) {
return commands;
}
for (File file : files) {
if (!file.isDirectory() && file.canExecute()) {
commands.add(file.getAbsolutePath());
}
}
}
LOG.debug("The generated check commands are {}", commands);
return commands;
}
static class ScriptProcessLauncher {
ShellUtils.ShellCommandExecutor executor = null;
String command;
public ScriptProcessLauncher(String command, long timeOut) {
this.command = command;
this.executor = new ShellUtils.ShellCommandExecutor(new String[]{command}, null, null, timeOut);
}
public ExitStatus launch() {
ExitStatus exitStatus = ExitStatus.SUCCESS;
try {
executor.execute();
} catch (Exception e) {
if (executor.isTimedOut()) {
exitStatus = ExitStatus.TIMED_OUT;
} else {
exitStatus = ExitStatus.EXCEPTION;
}
LOG.warn(command + " exception, the exit status is: " + exitStatus, e);
} finally {
if (exitStatus == ExitStatus.SUCCESS && hasNoResource(executor.getOutput())) {
exitStatus = ExitStatus.FAILED;
LOG.info("Script execute output: " + executor.getOutput());
}
}
return exitStatus;
}
private boolean hasNoResource(String output) {
String[] splits = output.split("\n");
for (String split : splits) {
if (split.startsWith(NO_RESOURCES)) {
return true;
}
}
return false;
}
}
private enum ExitStatus {
SUCCESS, TIMED_OUT, EXCEPTION, FAILED
}
}