/******************************************************************************* * =========================================================== * 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.framework.manager; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map; import java.util.PriorityQueue; import java.util.Set; import net.neoremind.sshxcute.core.SSHExec; import com.impetus.ankush.AppStoreWrapper; import com.impetus.ankush.common.domain.Cluster; import com.impetus.ankush.common.domain.Operation; import com.impetus.ankush.common.exception.AnkushException; import com.impetus.ankush2.constant.Constant; import com.impetus.ankush2.db.DBClusterManager; import com.impetus.ankush2.db.DBOperationManager; import com.impetus.ankush2.db.DBServiceManager; import com.impetus.ankush2.framework.Serviceable; import com.impetus.ankush2.framework.config.ClusterConfig; import com.impetus.ankush2.framework.config.ComponentConfig; import com.impetus.ankush2.framework.config.ProgressConfig; import com.impetus.ankush2.framework.config.ServiceConf; import com.impetus.ankush2.framework.utils.DatabaseUtils; import com.impetus.ankush2.framework.utils.ObjectFactory; import com.impetus.ankush2.framework.utils.ServiceComparator; import com.impetus.ankush2.logger.AnkushLogger; import com.impetus.ankush2.utils.AnkushUtils; public class ServiceManager { private AnkushLogger logger = new AnkushLogger(getClass()); private Map<String, Object> result = new HashMap<String, Object>(); private Set<String> errors = new LinkedHashSet<String>(); private ClusterConfig clusterConfig; private Cluster dbCluster; // Priority Queue for components /** The service queue. */ private PriorityQueue<Serviceable> componentQueue; private static final String CLUSTER_IN_MAINTENANCE_STATE = "Could not perform operation as Cluster is not in deployed state."; private static final String COULD_NOT_FIND_CLUSTER = "Could not find cluster. Please provide valid cluster id."; private static final String COULD_NOT_FIND_CLUSTER_CONFIG = "Could not find cluster configuration details."; private static final String SERVICE_MANAGE_OPERATION = "serviceOperation"; private Long tileId; Map<String, Object> input; private String loggedInUser; private String errMessage(String action, String type) { return "Could not " + action + " " + type + ". Please view server logs for more details."; } public ServiceManager(Long clusterId, String loggedInUser) throws AnkushException { // Getting cluster object from db dbCluster = new DBClusterManager().getCluster(clusterId); if (dbCluster == null) { throw new AnkushException(COULD_NOT_FIND_CLUSTER); } if (!dbCluster.getState().equals( Constant.Cluster.State.DEPLOYED.toString())) { throw new AnkushException(CLUSTER_IN_MAINTENANCE_STATE); } clusterConfig = dbCluster.getClusterConfig(); if (clusterConfig == null) { throw new AnkushException(COULD_NOT_FIND_CLUSTER_CONFIG); } clusterConfig.incrementOperation(); this.loggedInUser = loggedInUser; logger.setCluster(clusterConfig); // preparing input data to save in operation confbytes input = new HashMap<String, Object>(); input.put("clusterId", clusterConfig.getClusterId()); } /** * Return result. * * @return The result object. */ private Map returnResult() { if (this.errors.isEmpty()) { this.result.put("status", true); this.result.put("message", "Operation submitted successfully."); } else { this.result.put("status", false); this.result.put("error", this.errors); this.result.put("message", "Operation submition failed."); } return this.result; } public void validateNode(String host) throws AnkushException { // checking node existence in cluster nodes if (!clusterConfig.getNodes().containsKey(host)) { throw new AnkushException("Could not find " + host + " node."); } } public void validateComponent(String component) throws AnkushException { // checking node existence in cluster nodes if (component == null || !clusterConfig.getComponents().containsKey(component)) { throw new AnkushException("Could not find " + component + " component."); } } private boolean preNodeService(String host, boolean isStop, Map<String, Set<String>> componentRolesMap) { // creating component queue if (!createComponentQueue(componentRolesMap.keySet(), isStop)) { return false; } // Connect to node AnkushUtils.connectNodesString(clusterConfig, Arrays.asList(host)); return true; } private void postNodeService(String host) { // Disconnect node SSHExec connection = clusterConfig.getNodes().get(host).getConnection(); if (connection != null) { connection.disconnect(); clusterConfig.getNodes().get(host).setConnection(null); } // updating operation completion time and status updateOperationProgress(); } private boolean preClusterService(boolean order) throws AnkushException, Exception { // Adding Agent to cluster component set Set<String> componentSet = new HashSet<String>(clusterConfig .getComponents().keySet()); componentSet.add(Constant.Component.Name.AGENT); // create component queue if (!createComponentQueue(componentSet, order)) { return false; } // Creating node connection for component nodes AnkushUtils.connectNodes(clusterConfig, clusterConfig.getNodes() .values()); return true; } /** * Method to perform the start action on services * * @param clusterId * {@link Long} * @param serviceConf * {@link ServiceConf} * @return {@link Map} * @throws Exception */ public Map startServices(final String host, final Map<String, Set<String>> services) throws Exception { // creating component queue and updating HAConfig.xml if (preNodeService(host, false, services)) { AppStoreWrapper.getExecutor().execute(new Runnable() { @Override public void run() { // preparing input to save in operation confbytes prepareInputData(host, Constant.ServiceAction.START, null, services); // Save operation details addOperation(Constant.Cluster.Operation.START_SERVICES, clusterConfig.getNodes().get(host).getRoles() .keySet()); boolean status = true; // Update force stop flag DBServiceManager.getManager() .setServicesForceStop(clusterConfig.getClusterId(), host, services, false); while (!componentQueue.isEmpty()) { Serviceable serviceable = componentQueue.remove(); status = status && serviceable.startServices( clusterConfig, host, new HashSet<String>(services .get(serviceable .getComponentName()))); // setting component wise progress clusterConfig.getProgress().setProgress( serviceable.getComponentName(), 100); } postNodeService(host); } }); } return returnResult(); } public Map stopServices(final String host, final Map<String, Set<String>> services) throws Exception { // creating component queue and updating HAConfig.xml if (preNodeService(host, true, services)) { AppStoreWrapper.getExecutor().execute(new Runnable() { @Override public void run() { // preparing input to save in operation confbytes prepareInputData(host, Constant.ServiceAction.STOP, null, services); // Save operation details addOperation(Constant.Cluster.Operation.STOP_SERVICES, clusterConfig.getNodes().get(host).getRoles() .keySet()); boolean status = true; // Update force stop flag DBServiceManager.getManager().setServicesForceStop( clusterConfig.getClusterId(), host, services, true); while (!componentQueue.isEmpty()) { Serviceable service = componentQueue.remove(); status = status && service.stopServices( clusterConfig, host, new HashSet<String>( services.get(service .getComponentName()))); // setting component wise progress clusterConfig.getProgress().setProgress( service.getComponentName(), 100); } postNodeService(host); } }); } return returnResult(); } private Map<String, Set<String>> getComponentRoleMap(String host) throws AnkushException, Exception { Map<String, Set<String>> componentRolesMap = clusterConfig.getNodes() .get(host).getRoles(); if (componentRolesMap == null) { throw new AnkushException("Could not get Component Role map."); } // Adding Agent component in roles componentRolesMap.put(Constant.Component.Name.AGENT, new HashSet<String>(Arrays.asList(Constant.Role.AGENT))); return componentRolesMap; } public Map startNode(final String host) throws Exception { try { // creating component queue and updating HAConfig.xml if (preNodeService(host, false, getComponentRoleMap(host))) { // Starting services AppStoreWrapper.getExecutor().execute(new Runnable() { @Override public void run() { // preparing input to save in operation confbytes prepareInputData(host, Constant.ServiceAction.START, null, null); // Save operation details addOperation(Constant.Cluster.Operation.START_NODE, clusterConfig.getNodes().get(host).getRoles() .keySet()); boolean status = true; // Update force stop flag DBServiceManager.getManager().setForceStop( clusterConfig.getClusterId(), host, null, null, null, null, false); while (!componentQueue.isEmpty()) { Serviceable serviceable = componentQueue.remove(); status = status && serviceable.startNode(clusterConfig, host); // setting component wise progress clusterConfig.getProgress().setProgress( serviceable.getComponentName(), 100); } postNodeService(host); } }); } } catch (AnkushException e) { errors.add(e.getMessage()); } catch (Exception e) { errors.add(errMessage("start", "node")); } return returnResult(); } public Map stopNode(final String host) throws Exception { try { // creating component queue and updating HAConfig.xml if (preNodeService(host, true, getComponentRoleMap(host))) { // Starting services AppStoreWrapper.getExecutor().execute(new Runnable() { @Override public void run() { // preparing input to save in operation confbytes prepareInputData(host, Constant.ServiceAction.STOP, null, null); // Save operation details addOperation(Constant.Cluster.Operation.STOP_NODE, clusterConfig.getNodes().get(host).getRoles() .keySet()); boolean status = true; // Update force stop flag DBServiceManager.getManager().setForceStop( clusterConfig.getClusterId(), host, null, null, null, null, true); while (!componentQueue.isEmpty()) { Serviceable serviceable = componentQueue.remove(); status = status && serviceable .stopNode(clusterConfig, host); // setting component wise progress clusterConfig.getProgress().setProgress( serviceable.getComponentName(), 100); } // node disconnect postNodeService(host); } }); } } catch (AnkushException e) { errors.add(e.getMessage()); } catch (Exception e) { errors.add(errMessage("stop", "node")); } return returnResult(); } /** * Used to start component * * @param clusterId * {@link Long} * @param componentName * {@link String} * * @return {@link Map} */ public Map startComponent(final String componentName, final String role) throws Exception { try { final ComponentConfig componentConfig = clusterConfig .getComponents().get(componentName); final Serviceable componentServiceManager = ObjectFactory .getServiceObject(componentName); if (componentServiceManager == null) { throw new AnkushException("Could not find service manager for " + componentName + "component."); } // Initializing nodes on which to create connection and operation // name depending whether need to start role or component final Set<String> nodeSet; final Constant.Cluster.Operation opName; if (role == null || role.isEmpty()) { nodeSet = componentConfig.getNodes().keySet(); opName = Constant.Cluster.Operation.START_COMPONENT; } else { nodeSet = getRoleNodeSet(componentName, componentConfig .getNodes().keySet(), role); opName = Constant.Cluster.Operation.START_ROLE; input.put("role", role); } // Creating node connection for component nodes AnkushUtils.connectNodesString(clusterConfig, nodeSet); AppStoreWrapper.getExecutor().execute(new Runnable() { @Override public void run() { // preparing input to save in operation confbytes prepareInputData(null, Constant.ServiceAction.START, componentName, null); input.put("component", componentName); input.put("action", Constant.ServiceAction.START); addOperation(opName, new HashSet<String>(Arrays.asList(componentName))); // Update force stop flag if (role == null || role.isEmpty()) { DBServiceManager.getManager().setForceStop( clusterConfig.getClusterId(), null, componentName, null, null, null, false); componentServiceManager.start(clusterConfig); } else { DBServiceManager.getManager().setForceStop( clusterConfig.getClusterId(), null, null, role, null, null, false); componentServiceManager.startRole(clusterConfig, role); } // setting component wise progress clusterConfig.getProgress().setProgress(componentName, 100); // Disconnect node connection for component nodes AnkushUtils.disconnectCompNodes(clusterConfig, nodeSet); updateOperationProgress(); } }); } catch (AnkushException e) { errors.add(e.getMessage()); } catch (Exception e) { errors.add(errMessage("start", "component")); } return returnResult(); } public Map stopComponent(final String componentName, final String role) throws Exception { try { final ComponentConfig componentConfig = clusterConfig .getComponents().get(componentName); final Serviceable componentServiceManager = ObjectFactory .getServiceObject(componentName); if (componentServiceManager == null) { throw new AnkushException("Could not find service manager for " + componentName + "component."); } // Initializing nodes on which to create connection and operation // name depending whether need to start role or component final Set<String> nodeSet; final Constant.Cluster.Operation opName; if (role == null || role.isEmpty()) { nodeSet = componentConfig.getNodes().keySet(); opName = Constant.Cluster.Operation.STOP_COMPONENT; } else { nodeSet = getRoleNodeSet(componentName, componentConfig .getNodes().keySet(), role); opName = Constant.Cluster.Operation.STOP_ROLE; input.put("role", role); } // Creating node connection for component nodes AnkushUtils.connectNodesString(clusterConfig, nodeSet); AppStoreWrapper.getExecutor().execute(new Runnable() { @Override public void run() { // preparing input to save in operation confbytes prepareInputData(null, Constant.ServiceAction.STOP, componentName, null); addOperation(opName, new HashSet<String>(Arrays.asList(componentName))); // Update force stop flag if (role == null || role.isEmpty()) { // Update force stop flag DBServiceManager.getManager().setForceStop( clusterConfig.getClusterId(), null, componentName, null, null, null, true); componentServiceManager.stop(clusterConfig); } else { // Update force stop flag DBServiceManager.getManager().setForceStop( clusterConfig.getClusterId(), null, null, role, null, null, true); componentServiceManager.stopRole(clusterConfig, role); } // setting component wise progress clusterConfig.getProgress().setProgress(componentName, 100); // Disconnect node connection for component nodes AnkushUtils.disconnectCompNodes(clusterConfig, nodeSet); updateOperationProgress(); } }); } catch (AnkushException e) { errors.add(e.getMessage()); } catch (Exception e) { errors.add(errMessage("stop", "component")); } return returnResult(); } /** * @param componentName * {@link String} * @param componentNodes * {@link Set} * @param role * {@link String} * @return Collection of nodes on which the specified role exists */ private Set<String> getRoleNodeSet(String componentName, Set<String> componentNodes, String role) throws AnkushException { Set<String> roleNodes = new HashSet<String>(); try { Map<String, Set<String>> roles; for (String node : componentNodes) { if (!clusterConfig.getNodes().containsKey(node)) { throw new AnkushException("Could not found component node " + node + " in cluster nodes"); } roles = clusterConfig.getNodes().get(node).getRoles(); if (roles.containsKey(componentName) && roles.get(componentName).contains(role)) { roleNodes.add(node); } } if (roleNodes.isEmpty()) { throw new AnkushException("Could not find any node on which " + role + " is installed."); } return roleNodes; } catch (AnkushException e) { throw e; } catch (Exception e) { throw new AnkushException( "There is some exception while getting nodes on which " + role + " exists.", e); } } public Map startCluster() throws Exception { try { // TODO: check if there is any other operation is in progress // creating component queue and nodes connection if (preClusterService(false)) { // managing cluster services AppStoreWrapper.getExecutor().execute(new Runnable() { @Override public void run() { // preparing input to save in operation confbytes prepareInputData(null, Constant.ServiceAction.START, null, null); // Save operation details addOperation(Constant.Cluster.Operation.START_CLUSTER, clusterConfig.getComponents().keySet()); boolean status = true; // Update force stop flag DBServiceManager.getManager().setForceStop( clusterConfig.getClusterId(), null, null, null, null, null, false); while (!componentQueue.isEmpty()) { Serviceable serviceable = componentQueue.remove(); status = status && serviceable.start(clusterConfig); // setting component wise progress clusterConfig.getProgress().setProgress( serviceable.getComponentName(), 100); } AnkushUtils.disconnectNodes(clusterConfig, clusterConfig.getNodes().values()); // updating operation completion time , progress and // status updateOperationProgress(); } }); } } catch (AnkushException e) { errors.add(e.getMessage()); } catch (Exception e) { errors.add(errMessage("start", "cluster")); } return returnResult(); } public Map stopCluster() throws Exception { try { // creating component queue and nodes connection if (preClusterService(true)) { // managing cluster services AppStoreWrapper.getExecutor().execute(new Runnable() { @Override public void run() { // preparing input to save in operation confbytes prepareInputData(null, Constant.ServiceAction.STOP, null, null); // Save operation details addOperation(Constant.Cluster.Operation.STOP_CLUSTER, clusterConfig.getComponents().keySet()); boolean status = true; // Update force stop flag DBServiceManager.getManager().setForceStop( clusterConfig.getClusterId(), null, null, null, null, null, true); while (!componentQueue.isEmpty()) { Serviceable serviceable = componentQueue.remove(); status = status && serviceable.stop(clusterConfig); // setting component wise progress clusterConfig.getProgress().setProgress( serviceable.getComponentName(), 100); } AnkushUtils.disconnectNodes(clusterConfig, clusterConfig.getNodes().values()); // updating operation completion time and status updateOperationProgress(); } }); } } catch (AnkushException e) { errors.add(e.getMessage()); } catch (Exception e) { errors.add(errMessage("stop", "cluster")); } return returnResult(); } private boolean createComponentQueue(Set<String> componentSet, boolean serviceOrder) { // create component queue componentQueue = new PriorityQueue<Serviceable>(10, new ServiceComparator(serviceOrder)); for (String componentName : componentSet) { try { componentQueue.add(ObjectFactory .getServiceObject(componentName)); } catch (AnkushException e) { errors.add(e.getMessage()); return false; } catch (Exception e) { errors.add("Unable to find " + componentName + " component, so could not create component queue."); return false; } } return true; } private void addOperation(Constant.Cluster.Operation opName, Collection<String> components) { // Save operation details clusterConfig.setProgress(new ProgressConfig(components)); DatabaseUtils databaseUtils = new DatabaseUtils(); databaseUtils.addClusterOperation(clusterConfig, opName, this.loggedInUser != null ? this.loggedInUser : ""); // Add operation input data databaseUtils.updateOperationData(clusterConfig, "input", input); } private void updateOperationProgress() { try { Operation operation = new DBOperationManager() .getOperation(clusterConfig); if (operation == null) { logger.error("Could not find operation."); return; } operation.setCompletedAt(new Date()); if (clusterConfig.getErrors().isEmpty()) { operation.setStatus(Constant.Operation.Status.COMPLETED .toString()); } else { operation.setStatus(Constant.Operation.Status.ERROR.toString()); } // setting operation progress DatabaseUtils databaseUtils = new DatabaseUtils(); databaseUtils.updateOperationProgress(clusterConfig); // Add operation output data databaseUtils.updateOperationData(clusterConfig, "output", clusterConfig); new DBOperationManager().saveOperation(operation); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void prepareInputData(String host, Constant.ServiceAction action, String component, Map<String, Set<String>> services) { input.put("action", action); if (services != null) { input.put("services", services); } if (host != null) { input.put("host", host); } if (component != null) { input.put("component", host); } } }