/*******************************************************************************
* ===========================================================
* Ankush : Big Data Cluster Management Solution
* ===========================================================
*
* (C) Copyright 2014, by Impetus Technologies
*
* This is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License (LGPL v3) as
* published by the Free Software Foundation;
*
* This software is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this software; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
******************************************************************************/
package com.impetus.ankush2.agent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.neoremind.sshxcute.core.SSHExec;
import net.neoremind.sshxcute.exception.TaskExecFailException;
import net.neoremind.sshxcute.task.CustomTask;
import net.neoremind.sshxcute.task.impl.ExecCommand;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import com.impetus.ankush.AppStoreWrapper;
import com.impetus.ankush.common.domain.Node;
import com.impetus.ankush.common.exception.AnkushException;
import com.impetus.ankush.common.scripting.AnkushTask;
import com.impetus.ankush.common.scripting.impl.Copy;
import com.impetus.ankush.common.scripting.impl.MakeDirectory;
import com.impetus.ankush.common.scripting.impl.Remove;
import com.impetus.ankush.common.scripting.impl.Untar;
import com.impetus.ankush.common.utils.CommonUtil;
import com.impetus.ankush2.constant.Constant;
import com.impetus.ankush2.framework.config.ClusterConfig;
import com.impetus.ankush2.framework.config.NodeConfig;
import com.impetus.ankush2.utils.SSHUtils;
public class AgentNodeUpgrader extends AgentUpgrader {
private static final String NEW_AGENT_FOLDER = NODE_ANKUSH_HOME
+ "/.newagent/agent";
private static final String AGENT_BACKUP_FOLDER = NODE_ANKUSH_HOME
+ "/agentBackup";
private static final String NEW_AGENT_TAR = NODE_ANKUSH_HOME
+ AgentDeployer.AGENT_BUNDLE_NAME;
private static final String UPGRADE_SCRIPT_PATH = "scripts/agent/upgrade.sh";
private static final String RESOURCE_BUNDLE_PATH = "scripts/agent/"
+ AgentDeployer.AGENT_BUNDLE_NAME;
private Node node;
private NodeConfig nodeConfig;
private ClusterConfig clusterConfig;
/**
* @param node
* @param cluster
* @param clusterConf
*/
public AgentNodeUpgrader(Node node, ClusterConfig clusterConfig) {
super();
this.node = node;
this.nodeConfig = node.getNodeConfig();
this.clusterConfig = clusterConfig;
}
/**
* Method to Upgrade Agent on node.
*/
public void upgradeAgent() {
String host = nodeConfig.getHost();
LOGGER.setCluster(clusterConfig);
LOGGER.info(MESSAGE_UPGRADING_AGENT + ": " + nodeConfig.getHost(),
Constant.Component.Name.AGENT, host);
// connection
SSHExec connection = null;
boolean isRollbackRequired = false;
try {
// getting installed Agent version
String installedAgentVersion = node.getAgentVersion();
if (agentBuildVersion.equals(installedAgentVersion)) {
return;
}
// connecting to node.
connection = SSHUtils.connectToNode(host,
clusterConfig.getAuthConf());
if (connection == null) {
throw new AnkushException("Could not connect to node: " + host);
}
// stopping Agent and taking Agent's backup
stoppingAgentWithBackup(host, connection);
System.out
.println("Stopping Agent and taking Agent's backup done ");
LOGGER.info("Stopping Agent and taking Agent's backup done",
Constant.Component.Name.AGENT, host);
// creating upgrade script directory
LOGGER.info("Creating Agent upgrade directory.",
Constant.Component.Name.AGENT, host);
String upgradeScriptDirectory = AgentUpgrader.NODE_ANKUSH_HOME
+ "upgrade/";
// creating Agent upgrade directory
createUpgradeDirectory(connection, upgradeScriptDirectory);
// upload Agent upgrade script
// setting isRollbackRequired to true to do rollback in
// AnkushException catch clause after backup is done.
isRollbackRequired = true;
// Uploading the agent jar to node
LOGGER.info("Copying Agent bundle...",
Constant.Component.Name.AGENT, host);
uploadBundle(connection, AppStoreWrapper.getResourcePath()
+ RESOURCE_BUNDLE_PATH, NODE_ANKUSH_HOME);
// extracting new Agent
extractNewAgent(connection);
// uploading upgrade.sh to node
String updateScriptPath = AppStoreWrapper.getResourcePath()
+ UPGRADE_SCRIPT_PATH;
// node update script path.
String nodeUpdateScriptPath = upgradeScriptDirectory
+ FilenameUtils.getName(updateScriptPath);
uploadBundle(connection, updateScriptPath, nodeUpdateScriptPath);
// running agent ugrade script
if (!executeUpgradeScript(connection, upgradeScriptDirectory)) {
throw new AnkushException(
"Please run the upgrade scripts available under "
+ CommonUtil.getUserHome(clusterConfig
.getAuthConf().getUsername())
+ ".ankush/upgrade/ directory to upgrade the agent manually and restart Agent.");
}
// TODO: Component wise upgrade changes
// Removing upgrade and backup directory after successful upgrade
removeUpgradeAndBackupDir(connection, upgradeScriptDirectory);
// starting Agent
startAgent(connection);
node.setAgentVersion(agentBuildVersion);
nodeConfig.setStatus(true);
} catch (AnkushException e) {
LOGGER.error(e.getMessage(), Constant.Component.Name.AGENT, host, e);
nodeConfig.setStatus(false);
if (isRollbackRequired) {
rollBack(connection);
}
} finally {
// disconnecting node
if (connection != null) {
connection.disconnect();
}
}
// saving node with status true/false in nodeConfig object
node.setNodeConfig(nodeConfig);
nodeManager.save(node);
}
private void stoppingAgentWithBackup(String host, SSHExec connection)
throws AnkushException {
LOGGER.info("Stopping Ankush " + Constant.Component.Name.AGENT
+ " and taking its backup.", Constant.Component.Name.AGENT,
host);
String errMsg = "Could not take Agent Backup.";
try {
// Stopping Ankush Agent
CustomTask killProcess = new ExecCommand("sh "
+ clusterConfig.getAgentHomeDir()
+ AgentConstant.Relative_Path.STOP_SCRIPT);
connection.exec(killProcess);
// backup old existing agent folder.
Copy backUpAgent = new Copy(clusterConfig.getAgentHomeDir(),
AGENT_BACKUP_FOLDER, true);
if (connection.exec(backUpAgent).rc != 0) {
throw new AnkushException("Could not take Agent backup.");
}
} catch (TaskExecFailException e) {
throw new AnkushException(errMsg);
} catch (Exception e) {
throw new AnkushException(errMsg);
}
}
private void uploadBundle(SSHExec connection, String source,
String destination) throws AnkushException {
// Uploading the jar files to node
try {
connection.uploadSingleDataToServer(source, destination);
} catch (Exception e) {
throw new AnkushException("Could not upload bundle: " + source);
}
}
private void createUpgradeDirectory(SSHExec connection, String source)
throws AnkushException {
try {
if (connection.exec(new MakeDirectory(source)).rc != 0) {
throw new AnkushException(
"Could not create Agent upgrade directory.");
}
} catch (TaskExecFailException e) {
throw new AnkushException(
"Could not execute task for creating Agent upgrade directory.");
}
}
private void extractNewAgent(SSHExec connection) throws AnkushException {
try {
AnkushTask ankushTask = new MakeDirectory(NEW_AGENT_FOLDER);
if (connection.exec(ankushTask).rc != 0) {
throw new AnkushException(
"Could not create directory for new agent bundle.");
}
ankushTask = new Untar(NEW_AGENT_TAR, NEW_AGENT_FOLDER, false);
if (connection.exec(ankushTask).rc != 0) {
throw new AnkushException("Could not extract agent bundle.");
}
} catch (TaskExecFailException e) {
throw new AnkushException(
"Could not execute task for extracting agent bundle.");
}
}
private boolean executeUpgradeScript(SSHExec connection,
String upgradeScriptDirectory) {
String componentName = Constant.Component.Name.AGENT;
String host = nodeConfig.getHost();
try {
String updateScriptPath = AppStoreWrapper.getResourcePath()
+ UPGRADE_SCRIPT_PATH;
// Getting the dependency file name for
// the fetched OS name
File file = new File(updateScriptPath);
// if file exists.
if (file.exists()) {
LOGGER.info("Copying Agent upgrade script path to node: "
+ nodeConfig.getHost(), Constant.Component.Name.AGENT,
nodeConfig.getHost());
// List of upgrade commands
List<String> upgradeCommands = FileUtils.readLines(new File(
updateScriptPath));
// Iterating over the commands.
for (String command : upgradeCommands) {
// executing script.
if (connection.exec(new ExecCommand(command)).rc != 0) {
throw new AnkushException(
"Could not execute agent upgrade command: "
+ command);
}
}
}
return true;
} catch (AnkushException e) {
LOGGER.error(e.getMessage(), componentName, host, e);
} catch (IOException e) {
LOGGER.error("Could not read agent upgrade script.", componentName,
host, e);
} catch (TaskExecFailException e) {
LOGGER.error("Could not execute agent upgrade task.",
componentName, host, e);
} catch (Exception e) {
LOGGER.error("Could not upgrade agent.", componentName, host, e);
}
return false;
}
private void rollBack(SSHExec connection) {
try {
// Removing Agent upgrade directory, agent.zip, .newAgent folder and
// agnet installation path
List<String> dirs = new ArrayList<String>(Arrays.asList(
NODE_ANKUSH_HOME + "upgrade/", NODE_ANKUSH_HOME
+ AgentDeployer.AGENT_BUNDLE_NAME,
NEW_AGENT_FOLDER, clusterConfig.getAgentHomeDir()));
Remove remove;
for (String dir : dirs) {
remove = new Remove(dir);
connection.exec(remove);
}
// backup old existing agent folder.
Copy copyAgentBackup = new Copy(AGENT_BACKUP_FOLDER,
clusterConfig.getAgentHomeDir(), true);
if (connection.exec(copyAgentBackup).rc != 0) {
LOGGER.error("Could not copy Agent backup folder.");
}
} catch (TaskExecFailException e) {
LOGGER.error("Could not execute Agent rollback task.",
Constant.Component.Name.AGENT, nodeConfig.getHost(), e);
}
}
private void removeUpgradeAndBackupDir(SSHExec connection,
String upgradeScriptDirectory) {
LOGGER.info("Removing agent upgrade script folder...",
Constant.Component.Name.AGENT, nodeConfig.getHost());
// remove agent upgrade script folder.
Remove remove = new Remove(upgradeScriptDirectory);
try {
connection.exec(remove);
// remove Agnet backup directory
remove = new Remove(AGENT_BACKUP_FOLDER);
connection.exec(remove);
} catch (TaskExecFailException e) {
LOGGER.error(
"Could not execute task for removing Agent upgrade and backup directory.",
Constant.Component.Name.AGENT, nodeConfig.getHost(), e);
}
}
private void startAgent(SSHExec connection) throws AnkushException {
// start agent
// String startAgent = "sh " + Constant.Agent.AGENT_START_SCRIPT;
String startAgent = "sh " + clusterConfig.getAgentHomeDir()
+ AgentConstant.Relative_Path.START_SCRIPT;
// create task
CustomTask task = new ExecCommand(startAgent);
try {
if (connection.exec(task).rc != 0) {
throw new AnkushException("Could not start Agent");
}
} catch (TaskExecFailException e) {
throw new AnkushException(
"Could not execute task for starting Agent");
}
}
}