/******************************************************************************* * Copyright (c) 2006-2011 Gluster, Inc. <http://www.gluster.com> * This file is part of Gluster Management Gateway. * * Gluster Management Gateway is free software; you can redistribute * it and/or modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * Gluster Management Gateway 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see * <http://www.gnu.org/licenses/>. *******************************************************************************/ package org.gluster.storage.management.gateway.services; import static org.gluster.storage.management.core.constants.RESTConstants.FORM_PARAM_SERVER_NAME; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.log4j.Logger; import org.gluster.storage.management.core.constants.CoreConstants; import org.gluster.storage.management.core.exceptions.ConnectionException; import org.gluster.storage.management.core.exceptions.GlusterRuntimeException; import org.gluster.storage.management.core.exceptions.GlusterValidationException; import org.gluster.storage.management.core.model.GlusterServer; import org.gluster.storage.management.core.model.Server; import org.gluster.storage.management.core.model.Server.SERVER_STATUS; import org.gluster.storage.management.core.utils.GlusterCoreUtil; import org.gluster.storage.management.core.utils.ProcessUtil; import org.gluster.storage.management.core.utils.StringUtil; import org.gluster.storage.management.gateway.data.ClusterInfo; import org.gluster.storage.management.gateway.data.ServerInfo; import org.gluster.storage.management.gateway.utils.ServerUtil; import org.gluster.storage.management.gateway.utils.SshUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * */ @Component public class GlusterServerService { private static final String HOSTNAME_PFX = "Hostname:"; private static final String UUID_PFX = "Uuid:"; private static final String STATE_PFX = "State:"; private static final String GLUSTER_SERVER_STATUS_ONLINE = "Peer in Cluster (Connected)"; private static final String GLUSTERD_INFO_FILE = "/etc/glusterd/glusterd.info"; @Autowired protected ServerUtil serverUtil; @Autowired private ClusterService clusterService; @Autowired private GlusterInterfaceService glusterUtil; @Autowired private SshUtil sshUtil; @Autowired private VolumeService volumeService; @Autowired private DiscoveredServerService discoveredServerService; private static final Logger logger = Logger.getLogger(GlusterServerService.class); public List<GlusterServer> getGlusterServers(String clusterName, boolean fetchDetails, Integer maxCount, String previousServerName) { List<GlusterServer> glusterServers; GlusterServer onlineServer = clusterService.getOnlineServer(clusterName); if (onlineServer == null) { throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]"); } try { glusterServers = getGlusterServers(clusterName, onlineServer, fetchDetails, maxCount, previousServerName); } catch (Exception e) { // check if online server has gone offline. If yes, try again one more time. if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) { // online server has gone offline! try with a different one. onlineServer = clusterService.getNewOnlineServer(clusterName); if (onlineServer == null) { throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]"); } glusterServers = getGlusterServers(clusterName, onlineServer, fetchDetails, maxCount, previousServerName); } else { throw new GlusterRuntimeException(e.getMessage()); } } return glusterServers; } private List<GlusterServer> getGlusterServers(String clusterName, GlusterServer onlineServer, boolean fetchDetails, Integer maxCount, String previousServerName) { List<GlusterServer> glusterServers; try { glusterServers = getGlusterServers(onlineServer.getName()); } catch (Exception e) { // check if online server has gone offline. If yes, try again one more time. if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) { // online server has gone offline! try with a different one. onlineServer = clusterService.getNewOnlineServer(clusterName); if (onlineServer == null) { throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]"); } glusterServers = getGlusterServers(onlineServer.getName()); } else { throw new GlusterRuntimeException(e.getMessage()); } } // skip the servers by maxCount / previousServerName glusterServers = GlusterCoreUtil.skipEntities(glusterServers, maxCount, previousServerName); if (fetchDetails) { String errMsg = fetchDetailsOfServers(Collections.synchronizedList(glusterServers)); if (!errMsg.isEmpty()) { throw new GlusterRuntimeException("Couldn't fetch details for server(s): " + errMsg); } } return glusterServers; } /* (non-Javadoc) * @see org.gluster.storage.management.gateway.utils.GlusterInterface#getGlusterServer(org.gluster.storage.management.core.model.GlusterServer, java.lang.String) */ public GlusterServer getGlusterServer(String onlineServer, String serverName) { List<GlusterServer> servers = getGlusterServers(onlineServer); for (GlusterServer server : servers) { if (server.getName().equalsIgnoreCase(serverName)) { return server; } } // Server not found. It's possible that the server name returned by glusterfs is actually IP address // Hence fetch details of all servers and then compare the host names again. String errMsg = fetchDetailsOfServers(Collections.synchronizedList(servers)); if (!errMsg.isEmpty()) { throw new GlusterRuntimeException("Couldn't fetch details for server(s): " + errMsg); } for (GlusterServer server : servers) { if (server.getName().equalsIgnoreCase(serverName)) { return server; } } // still not found! return null; } private String getUuid(String serverName) { return serverUtil.executeOnServer(serverName, "cat " + GLUSTERD_INFO_FILE, String.class).split("=")[1]; } /* (non-Javadoc) * @see org.gluster.storage.management.gateway.utils.GlusterInterface#getGlusterServers(org.gluster.storage.management.core.model.GlusterServer) */ public List<GlusterServer> getGlusterServers(String knownServerName) { String output = getPeerStatus(knownServerName); GlusterServer knownServer = new GlusterServer(knownServerName); knownServer.setUuid(getUuid(knownServerName)); knownServer.setStatus(SERVER_STATUS.ONLINE); List<GlusterServer> glusterServers = new ArrayList<GlusterServer>(); glusterServers.add(knownServer); GlusterServer server = null; boolean foundHost = false; boolean foundUuid = false; for (String line : output.split(CoreConstants.NEWLINE)) { if (foundHost && foundUuid) { // Host and UUID is found, we should look for state String state = StringUtil.extractToken(line, STATE_PFX); if (state != null) { server.setStatus(state.contains(GLUSTER_SERVER_STATUS_ONLINE) ? SERVER_STATUS.ONLINE : SERVER_STATUS.OFFLINE); // Completed populating current server. Add it to the list // and reset all related variables. glusterServers.add(server); foundHost = false; foundUuid = false; server = null; } } else if (foundHost) { // Host is found, look for UUID String uuid = StringUtil.extractToken(line, UUID_PFX); if (uuid != null) { server.setUuid(uuid); foundUuid = true; } } else { // Look for the next host if (server == null) { server = new GlusterServer(); } String hostName = StringUtil.extractToken(line, HOSTNAME_PFX); if (hostName != null) { server.setName(hostName); foundHost = true; } } } return glusterServers; } /** * @param knownServer * A known server on which the gluster command will be executed to fetch peer status * @return Outout of the "gluster peer status" command */ private String getPeerStatus(String knownServer) { return serverUtil.executeOnServer(knownServer, "gluster peer status", String.class); } private String fetchDetailsOfServers(List<GlusterServer> glusterServers) { try { List<String> errors = Collections.synchronizedList(new ArrayList<String>()); List<Thread> threads = createThreads(glusterServers, errors); ProcessUtil.waitForThreads(threads); return prepareErrorMessage(errors); } catch(InterruptedException e) { String errMsg = "Exception while fetching details of servers! Error: [" + e.getMessage() + "]"; logger.error(errMsg, e); throw new GlusterRuntimeException(errMsg, e); } } private String prepareErrorMessage(List<String> errors) { String errMsg = ""; for(String error : errors) { if(!errMsg.isEmpty()) { errMsg += CoreConstants.NEWLINE; } errMsg += error; } return errMsg; } /** * Creates threads that will run in parallel and fetch details of given gluster servers * @param discoveredServers The list to be populated with details of gluster servers * @param errors List to be populated with errors if any * @return * @throws InterruptedException */ private List<Thread> createThreads(List<GlusterServer> glusterServers, List<String> errors) throws InterruptedException { List<Thread> threads = new ArrayList<Thread>(); for (int i = glusterServers.size()-1; i >= 0 ; i--) { Thread thread = new ServerDetailsThread(glusterServers.get(i), errors); threads.add(thread); thread.start(); if(i >= 5 && i % 5 == 0) { // After every 5 servers, wait for 1 second so that we don't end up with too many running threads Thread.sleep(1000); } } return threads; } public class ServerDetailsThread extends Thread { private List<String> errors; private GlusterServer server; private final Logger logger = Logger.getLogger(ServerDetailsThread.class); /** * Private constructor called on each thread * @param server The server whose details are to be fetched by this thread * @param errors */ private ServerDetailsThread(GlusterServer server, List<String> errors) { this.errors = errors; this.server = server; } @Override public void run() { try { logger.info("fetching details of server [" + server.getName() + "] - start"); serverUtil.fetchServerDetails(server); logger.info("fetching details of server [" + server.getName() + "] - end"); } catch (Exception e) { logger.error("fetching details of server [" + server.getName() + "] - error", e); errors.add(server.getName() + " : [" + e.getMessage() + "]"); } } } public GlusterServer getGlusterServer(String clusterName, String serverName, Boolean fetchDetails) { if (clusterName == null || clusterName.isEmpty()) { throw new GlusterValidationException("Cluster name must not be empty!"); } if (serverName == null || serverName.isEmpty()) { throw new GlusterValidationException("Server name must not be empty!"); } ClusterInfo cluster = clusterService.getCluster(clusterName); if (cluster == null) { throw new GlusterRuntimeException("Cluster [" + clusterName + "] not found!"); } GlusterServer onlineServer = clusterService.getOnlineServer(clusterName); if (onlineServer == null) { throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]"); } return getGlusterServer(clusterName, serverName, onlineServer, fetchDetails); } private GlusterServer getGlusterServer(String clusterName, String serverName, GlusterServer onlineServer, Boolean fetchDetails) { GlusterServer server = null; try { server = getGlusterServer(onlineServer.getName(), serverName); } catch (Exception e) { // check if online server has gone offline. If yes, try again one more time. if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) { // online server has gone offline! try with a different one. onlineServer = clusterService.getNewOnlineServer(clusterName); if (onlineServer == null) { throw new GlusterRuntimeException("No online servers found in cluster [" + clusterName + "]"); } server = getGlusterServer(onlineServer.getName(), serverName); } else { throw new GlusterRuntimeException(e.getMessage()); } } if (fetchDetails && server.isOnline()) { serverUtil.fetchServerDetails(server); } return server; } public boolean isValidServer(String clusterName, String serverName) { try { GlusterServer server = getGlusterServer(clusterName, serverName, false); return server != null; } catch(Exception e) { return false; } } public void removeServerFromCluster(String clusterName, String serverName) { if (clusterName == null || clusterName.isEmpty()) { throw new GlusterValidationException("Cluster name must not be empty!"); } if (serverName == null || serverName.isEmpty()) { throw new GlusterValidationException("Server name must not be empty!"); } ClusterInfo cluster = clusterService.getCluster(clusterName); if (cluster == null) { throw new GlusterValidationException("Cluster [" + clusterName + "] not found!"); } List<ServerInfo> servers = cluster.getServers(); if (servers == null || servers.isEmpty() || !containsServer(servers, serverName)) { throw new GlusterValidationException("Server [" + serverName + "] is not attached to cluster [" + clusterName + "]!"); } if (servers.size() == 1) { // Only one server mapped to the cluster, no "peer detach" required. // remove the cached online server for this cluster if present clusterService.removeOnlineServer(clusterName); } else { // get an online server that is not same as the server being removed GlusterServer onlineServer = clusterService.getOnlineServer(clusterName, serverName); if (onlineServer == null) { throw new GlusterRuntimeException("No online server found in cluster [" + clusterName + "]"); } try { glusterUtil.removeServer(onlineServer.getName(), serverName); } catch (Exception e) { // check if online server has gone offline. If yes, try again one more time. if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) { // online server has gone offline! try with a different one. onlineServer = clusterService.getNewOnlineServer(clusterName, serverName); if (onlineServer == null) { throw new GlusterRuntimeException("No online server found in cluster [" + clusterName + "]"); } glusterUtil.removeServer(onlineServer.getName(), serverName); } else { throw new GlusterRuntimeException(e.getMessage()); } } clusterService.unmapServerFromCluster(clusterName, serverName); // since the server is removed from the cluster, it is now available to be added to other clusters. // Hence add it back to the discovered servers list. discoveredServerService.addDiscoveredServer(serverName); try { if (serverUtil.isServerOnline(new Server(serverName))) { volumeService.clearCifsConfiguration(clusterName, onlineServer.getName(), serverName); } } catch (Exception e1) { throw new GlusterRuntimeException( "Server removed from cluster, however deleting cifs configuration failed ! [ " + e1.getMessage() + "]"); } if (onlineServer.getName().equals(serverName)) { // since the cached server has been removed from the cluster, remove it from the cache clusterService.removeOnlineServer(clusterName); } } } private boolean containsServer(List<ServerInfo> servers, String serverName) { for (ServerInfo server : servers) { if (server.getName().toUpperCase().equals(serverName.toUpperCase())) { return true; } } return false; } /** * Adds given server to cluster and returns its host name. e.g. If serverName passed is an IP address, this method * will return the host name of the machine with given IP address. * * @param clusterName * @param serverName * @return */ public String addServerToCluster(String clusterName, String serverName) { if (clusterName == null || clusterName.isEmpty()) { throw new GlusterValidationException("Cluster name must not be empty!"); } if (serverName == null || serverName.isEmpty()) { throw new GlusterValidationException("Parameter [" + FORM_PARAM_SERVER_NAME + "] is missing in request!"); } ClusterInfo cluster = clusterService.getCluster(clusterName); if (cluster == null) { throw new GlusterValidationException("Cluster [" + clusterName + "] not found!"); } boolean publicKeyInstalled = sshUtil.isPublicKeyInstalled(serverName); if (!publicKeyInstalled && !sshUtil.hasDefaultPassword(serverName)) { // public key not installed, default password doesn't work. return with error. throw new GlusterRuntimeException("Gluster Management Gateway uses the default password to set up keys on the server." + CoreConstants.NEWLINE + "However it seems that the password on server [" + serverName + "] has been changed manually." + CoreConstants.NEWLINE + "Please reset it back to the standard default password and try again."); } String hostName = serverUtil.fetchHostName(serverName); List<ServerInfo> servers = cluster.getServers(); if (servers != null && !servers.isEmpty()) { // cluster has at least one existing server, so that peer probe can be performed performAddServer(clusterName, hostName); } else { // this is the first server to be added to the cluster, which means no // gluster CLI operation required. just add it to the cluster-server mapping } // add the cluster-server mapping clusterService.mapServerToCluster(clusterName, hostName); // since the server is added to a cluster, it should not more be considered as a // discovered server available to other clusters discoveredServerService.removeDiscoveredServer(hostName); if (!publicKeyInstalled) { try { // install public key (this will also disable password based ssh login) sshUtil.installPublicKey(hostName); } catch (Exception e) { throw new GlusterRuntimeException("Public key could not be installed on [" + hostName + "]! Error: [" + e.getMessage() + "]"); } } return hostName; } private void performAddServer(String clusterName, String serverName) { GlusterServer onlineServer = clusterService.getOnlineServer(clusterName); if (onlineServer == null) { throw new GlusterRuntimeException("No online server found in cluster [" + clusterName + "]"); } try { glusterUtil.addServer(onlineServer.getName(), serverName); } catch (Exception e) { // check if online server has gone offline. If yes, try again one more time. if (e instanceof ConnectionException || serverUtil.isServerOnline(onlineServer) == false) { // online server has gone offline! try with a different one. onlineServer = clusterService.getNewOnlineServer(clusterName); if (onlineServer == null) { throw new GlusterRuntimeException("No online server found in cluster [" + clusterName + "]"); } glusterUtil.addServer(onlineServer.getName(), serverName); } else { throw new GlusterRuntimeException(e.getMessage()); } } } public List<String> getFsTypes(String clusterName, String serverName) { if (isValidServer(clusterName, serverName)) { return serverUtil.getFsTypes(serverName); } else { throw new GlusterRuntimeException(serverName + " does not belong to the cluster [" + clusterName + "]"); } } }