Java tutorial
/** * 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 org.apache.ambari.server.bootstrap; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.apache.ambari.server.bootstrap.BootStrapStatus.BSStat; import org.apache.ambari.server.utils.ShellCommandUtil; import org.apache.ambari.server.utils.ShellCommandUtil.Result; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * @author ncole * */ class BSRunner extends Thread { private static Log LOG = LogFactory.getLog(BSRunner.class); private static final String DEFAULT_USER = "root"; private static final String SCRIPTS_DIR = "/var/lib/tbds-server/resources/scripts"; private boolean finished = false; private SshHostInfo sshHostInfo; private File bootDir; private String bsScript; private File requestIdDir; private File sshKeyFile; private File passwordFile; private int requestId; private String agentSetupScript; private String agentSetupPassword; private String ambariHostname; private boolean verbose; private BootStrapImpl bsImpl; private final String clusterOsFamily; private String projectVersion; private int serverPort; public BSRunner(BootStrapImpl impl, SshHostInfo sshHostInfo, String bootDir, String bsScript, String agentSetupScript, String agentSetupPassword, int requestId, long timeout, String hostName, boolean isVerbose, String clusterOsFamily, String projectVersion, int serverPort) { this.requestId = requestId; this.sshHostInfo = sshHostInfo; this.bsScript = bsScript; this.bootDir = new File(bootDir); this.requestIdDir = new File(bootDir, Integer.toString(requestId)); this.sshKeyFile = new File(this.requestIdDir, "sshKey"); this.agentSetupScript = agentSetupScript; this.agentSetupPassword = agentSetupPassword; this.ambariHostname = hostName; this.verbose = isVerbose; this.clusterOsFamily = clusterOsFamily; this.projectVersion = projectVersion; this.bsImpl = impl; this.serverPort = serverPort; BootStrapStatus status = new BootStrapStatus(); status.setLog("RUNNING"); status.setStatus(BSStat.RUNNING); bsImpl.updateStatus(requestId, status); } /** * Update the gathered data from reading output * */ private class BSStatusCollector implements Runnable { @Override public void run() { BSHostStatusCollector collector = new BSHostStatusCollector(requestIdDir, sshHostInfo.getHosts()); collector.run(); List<BSHostStatus> hostStatus = collector.getHostStatus(); BootStrapStatus status = new BootStrapStatus(); status.setHostsStatus(hostStatus); status.setLog(""); status.setStatus(BSStat.RUNNING); bsImpl.updateStatus(requestId, status); } } private String createHostString(List<String> list) { StringBuilder ret = new StringBuilder(); if (list == null) { return ""; } int i = 0; for (String host : list) { ret.append(host); if (i++ != list.size() - 1) ret.append(","); } return ret.toString(); } /** Create request id dir for each bootstrap call **/ private void createRunDir() throws IOException { if (!bootDir.exists()) { // create the bootdir directory. if (!bootDir.mkdirs()) { throw new IOException("Cannot create " + bootDir); } } /* create the request id directory */ if (requestIdDir.exists()) { /* delete the directory and make sure we start back */ FileUtils.deleteDirectory(requestIdDir); } /* create the directory for the run dir */ if (!requestIdDir.mkdirs()) { throw new IOException("Cannot create " + requestIdDir); } } private void writeSshKeyFile(String data) throws IOException { FileUtils.writeStringToFile(sshKeyFile, data); } private void writePasswordFile(String data) throws IOException { FileUtils.writeStringToFile(passwordFile, data); } public synchronized void finished() { this.finished = true; } public void beforeBootStrap(SshHostInfo sshHostInfo) { //check the ssh_keygen String sshKeygenShellPath = SCRIPTS_DIR + "/bootstrap_agent_ssh_keygen.sh"; try { ShellCommandUtil.runCommand(sshKeygenShellPath, this.bsImpl.getBootstrapSSHUser()); //set the private key String privateKeyContentPath = SCRIPTS_DIR + "/bootstrap_get_private_key_content.sh"; Result runCommand = ShellCommandUtil.runCommand(privateKeyContentPath, this.bsImpl.getBootstrapSSHUser()); sshHostInfo.setSshKey(runCommand.getStdout()); sshHostInfo.setUser(this.bsImpl.getBootstrapSSHUser()); //set the agent environment: create ambari user and copy the public key to agent List<String> hosts = sshHostInfo.getHosts(); String agentEnvSetupShellPath = SCRIPTS_DIR + "/bootstrap_agent_env_setup.sh"; Map<String, BSHostPasser> hostPassers = sshHostInfo.getHostPassers(); if (hosts == null || hosts.size() == 0) { return; } CountDownLatch latch = new CountDownLatch(hosts.size()); for (String host : hosts) { String agentUser = this.bsImpl.getAgentDefaultLoginUser(); String agentPass = this.bsImpl.getAgentDefaultLoginPassword(); BSHostPasser bsHostPasser = hostPassers.get(host); if (bsHostPasser != null && (bsHostPasser.getLoginUser() != null)) { agentUser = bsHostPasser.getLoginUser(); agentPass = bsHostPasser.getPassword(); } AgentSetupThread agentSetupThread = new AgentSetupThread(agentEnvSetupShellPath, host, agentUser, agentPass, latch); agentSetupThread.start(); } latch.await(); } catch (IOException e) { LOG.error(e.getMessage()); e.printStackTrace(); } catch (InterruptedException e) { LOG.error(e.getMessage()); e.printStackTrace(); } } @Override public void run() { if (sshHostInfo.getSshKey() == null || sshHostInfo.getSshKey().equals("")) { beforeBootStrap(sshHostInfo); } String hostString = createHostString(sshHostInfo.getHosts()); String user = sshHostInfo.getUser(); String userRunAs = sshHostInfo.getUserRunAs(); if (user == null || user.isEmpty()) { user = DEFAULT_USER; } String command[] = new String[12]; BSStat stat = BSStat.RUNNING; String scriptlog = ""; try { createRunDir(); if (LOG.isDebugEnabled()) { // FIXME needs to be removed later // security hole LOG.debug("Using ssh key=\"" + sshHostInfo.getSshKey() + "\""); } String password = sshHostInfo.getPassword(); if (password != null && !password.isEmpty()) { this.passwordFile = new File(this.requestIdDir, "host_pass"); // TODO : line separator should be changed // if we are going to support multi platform server-agent solution String lineSeparator = System.getProperty("line.separator"); password = password + lineSeparator; writePasswordFile(password); } writeSshKeyFile(sshHostInfo.getSshKey()); /* Running command: * script hostlist bsdir user sshkeyfile */ command[0] = this.bsScript; command[1] = hostString; command[2] = this.requestIdDir.toString(); command[3] = user; command[4] = this.sshKeyFile.toString(); command[5] = this.agentSetupScript.toString(); command[6] = this.ambariHostname; command[7] = this.clusterOsFamily; command[8] = this.projectVersion; command[9] = this.serverPort + ""; command[10] = userRunAs; command[11] = (this.passwordFile == null) ? "null" : this.passwordFile.toString(); LOG.info("Host= " + hostString + " bs=" + this.bsScript + " requestDir=" + requestIdDir + " user=" + user + " keyfile=" + this.sshKeyFile + " passwordFile " + this.passwordFile + " server=" + this.ambariHostname + " version=" + projectVersion + " serverPort=" + this.serverPort + " userRunAs=" + userRunAs); String[] env = new String[] { "AMBARI_PASSPHRASE=" + agentSetupPassword }; if (this.verbose) env = new String[] { env[0], " BS_VERBOSE=\"-vvv\" " }; if (LOG.isDebugEnabled()) { LOG.debug(Arrays.toString(command)); } String bootStrapOutputFilePath = requestIdDir + File.separator + "bootstrap.out"; String bootStrapErrorFilePath = requestIdDir + File.separator + "bootstrap.err"; Process process = Runtime.getRuntime().exec(command, env); PrintWriter stdOutWriter = null; PrintWriter stdErrWriter = null; try { stdOutWriter = new PrintWriter(bootStrapOutputFilePath); stdErrWriter = new PrintWriter(bootStrapErrorFilePath); IOUtils.copy(process.getInputStream(), stdOutWriter); IOUtils.copy(process.getErrorStream(), stdErrWriter); } finally { if (stdOutWriter != null) stdOutWriter.close(); if (stdErrWriter != null) stdErrWriter.close(); } // Startup a scheduled executor service to look through the logs ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); BSStatusCollector statusCollector = new BSStatusCollector(); ScheduledFuture<?> handle = scheduler.scheduleWithFixedDelay(statusCollector, 0, 10, TimeUnit.SECONDS); LOG.info("Kicking off the scheduler for polling on logs in " + this.requestIdDir); try { LOG.info("Bootstrap output, log=" + bootStrapErrorFilePath + " " + bootStrapOutputFilePath); int exitCode = process.waitFor(); String outMesg = ""; String errMesg = ""; try { outMesg = FileUtils.readFileToString(new File(bootStrapOutputFilePath)); errMesg = FileUtils.readFileToString(new File(bootStrapErrorFilePath)); } catch (IOException io) { LOG.info("Error in reading files ", io); } scriptlog = outMesg + "\n\n" + errMesg; LOG.info("Script log Mesg " + scriptlog); if (exitCode != 0) { stat = BSStat.ERROR; } else { stat = BSStat.SUCCESS; } scheduler.schedule(new BSStatusCollector(), 0, TimeUnit.SECONDS); long startTime = System.currentTimeMillis(); while (true) { if (LOG.isDebugEnabled()) { LOG.debug("Waiting for hosts status to be updated"); } boolean pendingHosts = false; BootStrapStatus tmpStatus = bsImpl.getStatus(requestId); List<BSHostStatus> hostStatusList = tmpStatus.getHostsStatus(); if (hostStatusList != null) { for (BSHostStatus status : hostStatusList) { if (status.getStatus().equals("RUNNING")) { pendingHosts = true; } } } else { //Failed to get host status, waiting for hosts status to be updated pendingHosts = true; } if (LOG.isDebugEnabled()) { LOG.debug("Whether hosts status yet to be updated, pending=" + pendingHosts); } if (!pendingHosts) { break; } try { Thread.sleep(1000); } catch (InterruptedException e) { // continue } long now = System.currentTimeMillis(); if (now >= (startTime + 15000)) { LOG.warn("Gave up waiting for hosts status to be updated"); break; } } } catch (InterruptedException e) { throw new IOException(e); } finally { handle.cancel(true); /* schedule a last update */ scheduler.schedule(new BSStatusCollector(), 0, TimeUnit.SECONDS); scheduler.shutdownNow(); try { scheduler.awaitTermination(10, TimeUnit.SECONDS); } catch (InterruptedException e) { LOG.info("Interruped while waiting for scheduler"); } process.destroy(); } } catch (IOException io) { LOG.info("Error executing bootstrap " + io.getMessage()); stat = BSStat.ERROR; } finally { /* get the bstatus */ BootStrapStatus tmpStatus = bsImpl.getStatus(requestId); List<BSHostStatus> hostStatusList = tmpStatus.getHostsStatus(); if (hostStatusList != null) { for (BSHostStatus hostStatus : hostStatusList) { if ("FAILED".equals(hostStatus.getStatus())) { stat = BSStat.ERROR; break; } } } else { stat = BSStat.ERROR; } tmpStatus.setLog(scriptlog); tmpStatus.setStatus(stat); bsImpl.updateStatus(requestId, tmpStatus); bsImpl.reset(); // Remove private ssh key after bootstrap is complete try { FileUtils.forceDelete(sshKeyFile); } catch (IOException io) { LOG.warn(io.getMessage()); } if (passwordFile != null) { // Remove password file after bootstrap is complete try { FileUtils.forceDelete(passwordFile); } catch (IOException io) { LOG.warn(io.getMessage()); } } finished(); } } public synchronized boolean isRunning() { return !this.finished; } static class AgentSetupThread extends Thread { private CountDownLatch latch; private String hostName; private String agentUser; private String agentPass; private String agentEnvSetupShellPath; public AgentSetupThread(String agentEnvSetupShellPath, String hostName, String agentUser, String agentPass, CountDownLatch latch) { this.latch = latch; this.agentEnvSetupShellPath = agentEnvSetupShellPath; this.hostName = hostName; this.agentUser = agentUser; this.agentPass = agentPass; } public void run() { try { ShellCommandUtil.runCommand(this.agentEnvSetupShellPath, this.hostName, this.agentUser, this.agentPass); } catch (IOException e) { LOG.error(e.getMessage()); e.printStackTrace(); } catch (InterruptedException e) { LOG.error(e.getMessage()); e.printStackTrace(); } finally { latch.countDown(); } } } }