/* * This file is part of DRBD Management Console by LINBIT HA-Solutions GmbH * written by Rasto Levrinc. * * Copyright (C) 2009, LINBIT HA-Solutions GmbH. * Copyright (C) 2011-2012, Rastislav Levrinc. * * DRBD Management Console 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 2, or (at your option) * any later version. * * DRBD Management Console 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 drbd; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ package lcmc.host.domain; import java.awt.Color; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.JComponent; import lcmc.Exceptions; import lcmc.cluster.domain.Cluster; import lcmc.cluster.service.ssh.ExecCommandConfig; import lcmc.cluster.service.ssh.ExecCommandThread; import lcmc.cluster.service.ssh.Ssh; import lcmc.cluster.service.ssh.SshOutput; import lcmc.cluster.service.storage.BlockDeviceService; import lcmc.cluster.ui.ClusterBrowser; import lcmc.cluster.ui.SSHGui; import lcmc.common.domain.Application; import lcmc.common.domain.ConnectionCallback; import lcmc.common.domain.ConvertCmdCallback; import lcmc.common.domain.ExecCallback; import lcmc.common.domain.NewOutputCallback; import lcmc.common.domain.Unit; import lcmc.common.domain.Value; import lcmc.common.domain.util.Tools; import lcmc.common.ui.ProgressBar; import lcmc.common.ui.main.MainData; import lcmc.common.ui.main.ProgressIndicator; import lcmc.common.ui.utils.SwingUtils; import lcmc.configs.DistResource; import lcmc.drbd.domain.BlockDevice; import lcmc.drbd.domain.DrbdHost; import lcmc.drbd.domain.DrbdXml; import lcmc.drbd.service.DRBD; import lcmc.host.domain.parser.HostParser; import lcmc.host.ui.HostBrowser; import lcmc.host.ui.TerminalPanel; import lcmc.logger.Logger; import lcmc.logger.LoggerFactory; import lcmc.robotest.RoboTest; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; /** * This class holds host data and implementation of host related methods. */ @RequiredArgsConstructor public class Host implements Comparable<Host>, Value { private final DrbdHost drbdHost; private final TerminalPanel terminalPanel; private final MainData mainData; private final ProgressIndicator progressIndicator; private final Ssh ssh; private final HostBrowser hostBrowser; private final Hosts allHosts; private final Application application; private final RoboTest roboTest; private final BlockDeviceService blockDeviceService; private final SwingUtils swingUtils; @Getter @Setter private HostParser hostParser; //TODO cycle private static final Logger LOG = LoggerFactory.getLogger(Host.class); public static final String NOT_CONNECTED_MENU_TOOLTIP_TEXT = "not connected to the host"; public static final String PROXY_NOT_CONNECTED_MENU_TOOLTIP_TEXT = "not connected to the proxy host"; /** Timeout after which the connection is considered to be dead. */ private static final int PING_TIMEOUT = 40000; private static final int DRBD_EVENTS_TIMEOUT = 40000; private static final int CLUSTER_EVENTS_TIMEOUT = 40000; public static final String DEFAULT_HOSTNAME = "unknown"; public static final String VM_FILESYSTEM_SOURCE_DIR_LXC = "vm.filesystem.source.dir.lxc"; public static final String ROOT_USER = "root"; public static final String DEFAULT_SSH_PORT = "22"; public static final boolean UPDATE_LVM = true; private String name; private String enteredHostOrIp = Tools.getDefault("SSH.Host"); private String ipAddress; /** Ips in the combo in Dialog.Host.Configuration. */ private final Map<Integer, String[]> allIps = new HashMap<Integer, String[]>(); private Cluster cluster = null; private String hostname = DEFAULT_HOSTNAME; private String username = null; private Color defaultHostColorInGraph; private Color savedHostColorInGraphs; private ExecCommandThread drbdStatusThread = null; private ExecCommandThread crmStatusThread = null; private String sshPort = null; private Boolean useSudo = null; private String sudoPassword = ""; /** A gate that is used to synchronize the loading sequence. */ private CountDownLatch isLoadingGate; private final Collection<JComponent> enableOnConnectElements = new ArrayList<JComponent>(); private String pacemakerInstallMethodIndex; private String heartbeatPacemakerInstallMethodIndex; private String vmInfoFromServerMD5 = null; private int positionInTheCluster = 0; private volatile boolean lastConnectionCheckPositive = false; private boolean savable = true; /** Ping is set every 10s. */ private volatile AtomicBoolean ping = new AtomicBoolean(true); private boolean inCluster = false; private boolean crmStatusOk = false; public void init() { if (allHosts.size() == 1) { enteredHostOrIp = Tools.getDefault("SSH.SecHost"); } } public HostBrowser getBrowser() { return hostBrowser; } /** * Sets cluster in which this host is in. Set null, * if it is removed from the cluster. One host can be * only in one cluster. */ public void setCluster(final Cluster cluster) { this.cluster = cluster; if (cluster == null) { LOG.debug1("setCluster: " + getName() + " set cluster: null"); } else { inCluster = true; LOG.debug1("setCluster: " + getName() + " set cluster name: " + cluster.getName()); } } public void removeFromCluster() { inCluster = false; } public Cluster getCluster() { return cluster; } /** Returns color objects of this host for drbd graph. */ public Color[] getDrbdColors() { if (defaultHostColorInGraph == null) { defaultHostColorInGraph = Tools.getDefaultColor("Host.DefaultColor"); } final Color col; if (savedHostColorInGraphs == null) { col = defaultHostColorInGraph; } else { col = savedHostColorInGraphs; } final Color secColor; if (isConnected()) { if (hostParser.isDrbdStatusOk() && drbdHost.isDrbdLoaded()) { return new Color[]{col}; } else { secColor = Tools.getDefaultColor("Host.NoStatusColor"); } } else { secColor = Tools.getDefaultColor("Host.ErrorColor"); } return new Color[]{col, secColor}; } /** Returns color objects of this host. */ public Color[] getPmColors() { if (defaultHostColorInGraph == null) { defaultHostColorInGraph = Tools.getDefaultColor("Host.DefaultColor"); } final Color col; if (savedHostColorInGraphs == null) { col = defaultHostColorInGraph; } else { col = savedHostColorInGraphs; } final Color secColor; if (isConnected()) { if (isCrmStatusOk()) { return new Color[]{col}; } else { secColor = Tools.getDefaultColor("Host.NoStatusColor"); } } else { secColor = Tools.getDefaultColor("Host.ErrorColor"); } return new Color[]{col, secColor}; } public void setColor(final Color defaultColor) { this.defaultHostColorInGraph = defaultColor; if (savedHostColorInGraphs == null) { savedHostColorInGraphs = defaultColor; } terminalPanel.resetPromptColor(); } public void setSavedHostColorInGraphs(final Color savedHostColorInGraphs) { this.savedHostColorInGraphs = savedHostColorInGraphs; terminalPanel.resetPromptColor(); } public boolean isInCluster() { return inCluster; } /** * Returns true when this host is in a cluster and is different than the * specified cluster. */ public boolean isInCluster(final Cluster otherCluster) { return isInCluster() && !cluster.equals(otherCluster); } /** * Sets hostname as entered by user, this can be also ipAddress. If * hostnameEntered changed, it reinitilizes the name. */ public void setEnteredHostOrIp(final String enteredHostOrIp) { if (enteredHostOrIp != null && !enteredHostOrIp.equals(this.enteredHostOrIp)) { /* back button and hostname changed */ setName(null); setIpAddress(null); setHostname(null); } this.enteredHostOrIp = enteredHostOrIp; } /** Sets hostname of the host. */ public void setHostname(final String hostname) { this.hostname = hostname; } /** * Sets user name for the host. This username is used to connect * to the host. The default is "root". If username changed disconnect * the old connection. */ public void setUsername(final String username) { if (this.username != null && !username.equals(this.username)) { ssh.disconnect(); } this.username = username; } /** * Sets ipAddress. If ipAddress has changed, disconnect the * old connection. */ public void setIpAddress(final String ipAddress) { if (ipAddress != null) { if (this.ipAddress != null && !ipAddress.equals(this.ipAddress)) { ssh.disconnect(); } } else if (this.ipAddress != null) { ssh.disconnect(); } this.ipAddress = ipAddress; } public void setIps(final int hop, final String[] ipsForHop) { allIps.put(hop, ipsForHop); } public String[] getIps(final int hop) { return allIps.get(hop); } public void disconnect() { if (ssh.isConnected()) { ssh.forceDisconnect(); } setVMInfoMD5(null); } /** * Executes command. Command is executed in a new thread, after command * is finished execCallback.done function will be called. In case of error, * callback.doneError is called. */ public ExecCommandThread execCommand(final ExecCommandConfig execCommandConfig) { return ssh.execCommand(execCommandConfig); } public SshOutput captureCommand(final ExecCommandConfig execCommandConfig) { return ssh.captureCommand(execCommandConfig); } public SshOutput captureCommandProgressIndicator(final String text, final ExecCommandConfig execCommandConfig) { final String hostName = getName(); progressIndicator.startProgressIndicator(hostName, text); try { return ssh.captureCommand(execCommandConfig); } finally { progressIndicator.stopProgressIndicator(hostName, text); } } public void execCommandProgressIndicator(final String text, final ExecCommandConfig execCommandConfig) { final String hostName = getName(); progressIndicator.startProgressIndicator(hostName, text); try { ssh.execCommand(execCommandConfig); } finally { progressIndicator.stopProgressIndicator(hostName, text); } } /** * Executes command with bash -c. Command is executed in a new thread, * after command * is finished callback.done function will be called. * In case of error, callback.doneError is called. */ public ExecCommandThread execCommandInBash(ExecCommandConfig execCommandConfig) { return ssh.execCommand(execCommandConfig.inBash(true).inSudo(true)); } /** * Executes get status command which runs in the background and updates the * block device object. The command is 'drbdsetup /dev/drbdX events' * The session is stored, so that in can be stopped with 'stop' button. */ public void execDrbdStatusCommand(final ExecCallback execCallback, final NewOutputCallback outputCallback) { if (drbdStatusThread == null) { drbdStatusThread = ssh.execCommand(new ExecCommandConfig() .commandString("DRBD.getDrbdStatus") .inBash(false) .inSudo(false) .execCallback(execCallback) .newOutputCallback(outputCallback) .silentCommand() .silentOutput() .sshCommandTimeout(DRBD_EVENTS_TIMEOUT)); } else { LOG.appWarning("execDrbdStatusCommand: trying to start started drbd status"); } } /** Stops drbd status background process. */ public void stopDrbdStatus() { final ExecCommandThread dst = drbdStatusThread; if (dst == null) { LOG.appWarning("execDrbdStatusCommand: trying to stop stopped drbd status"); return; } dst.cancelTheSession(); drbdStatusThread = null; } public void waitForDrbdStatusFinish() { final ExecCommandThread dst = drbdStatusThread; if (dst != null) { try { /* it probably hangs after this timeout, so it will be * killed. */ dst.join(); } catch (final InterruptedException e) { Thread.currentThread().interrupt(); } stopDrbdStatus(); } } public void execCrmStatusCommand(final ExecCallback execCallback, final NewOutputCallback outputCallback) { if (crmStatusThread == null) { crmStatusThread = ssh.execCommand(new ExecCommandConfig() .commandString("Heartbeat.getClStatus") .inBash(false) .inSudo(false) .execCallback(execCallback) .newOutputCallback(outputCallback) .silentCommand() .silentOutput() .sshCommandTimeout(CLUSTER_EVENTS_TIMEOUT)); } else { LOG.appWarning("execClStatusCommand: trying to start started status"); } } public void waitForCrmStatusFinish() { final ExecCommandThread cst = crmStatusThread; if (cst == null) { return; } try { cst.join(); } catch (final InterruptedException e) { Thread.currentThread().interrupt(); } crmStatusThread = null; } public void stopCrmStatus() { final ExecCommandThread cst = crmStatusThread; if (cst == null) { LOG.appWarning("stopClStatus: trying to stop stopped status"); return; } cst.cancelTheSession(); } /** Gets ipAddress. There can be more ips, delimited with "," */ public String getIpAddress() { return ipAddress; } /** Returns the ipAddress for the hop. */ public String getIp(final int hop) { if (ipAddress == null) { return null; } final String[] ipsA = ipAddress.split(","); if (ipsA.length < hop + 1) { return null; } return ipsA[hop]; } /** Return first hop ipAddress. */ public String getFirstIp() { if (ipAddress == null) { return null; } final String[] ipsA = ipAddress.split(","); return ipsA[0]; } public String getUsername() { return username; } /** Returns first username in a hop. */ public String getFirstUsername() { final String[] usernames = username.split(","); return usernames[0]; } public String getEnteredHostOrIp() { return enteredHostOrIp; } String getSudoPrefix(final boolean sudoTest) { if (useSudo != null && useSudo) { if (sudoTest) { return "sudo -E -n "; } else { return "sudo -E -p '" + Ssh.SUDO_PROMPT + "' "; } } else { return ""; } } /** Returns command exclosed in sh -c "". */ public String getSudoCommand(final String command, final boolean sudoTest) { if (useSudo != null && useSudo) { final String sudoPrefix = getSudoPrefix(sudoTest); return command.replaceAll(DistResource.SUDO, sudoPrefix); } else { return command.replaceAll(DistResource.SUDO, " "); /* must be " " */ } } /** * Returns command with all the sshs that will be hopped. * * ssh -A -tt -l root x.x.x.x "ssh -A -tt -l root x.x.x.x \"ssh * -A -tt -l root x.x.x.x \\\"ls\\\"\"" */ public String getHoppedCommand(final String command) { final int hops = Tools.charCount(ipAddress, ',') + 1; final String[] usernames = username.split(","); final String[] ipsA = ipAddress.split(","); final StringBuilder s = new StringBuilder(200); if (hops > 1) { String sshAgentPid = ""; String sshAgentSock = ""; final Map<String, String> variables = System.getenv(); for (final String var : variables.keySet()) { final String value = variables.get(var); if ("SSH_AGENT_PID".equals(var)) { sshAgentPid = value; } else if ("SSH_AUTH_SOCK".equals(var)) { sshAgentSock = value; } } s.append("SSH_AGENT_PID="); s.append(sshAgentPid); s.append(" SSH_AUTH_SOCK="); s.append(sshAgentSock); s.append(' '); } for (int i = 1; i < hops; i++) { s.append("ssh -q -A -tt -o 'StrictHostKeyChecking no' -o 'ForwardAgent yes' -l "); if (i < usernames.length) { s.append(usernames[i]); } else { s.append(ROOT_USER); } s.append(' '); s.append(ipsA[i]); s.append(' '); s.append(Tools.escapeQuotes("\"", i - 1)); } s.append(Tools.escapeQuotes(command, hops - 1)); for (int i = hops - 1; i > 0; i--) { s.append(Tools.escapeQuotes("\"", i - 1)); } return s.toString(); } public String getHostname() { return hostname; } @Override public String toString() { return getName(); } /** * Gets name, that is shown in the tab. Name is either host name, if it is * set or ipAddress. */ public String getName() { if (name == null) { final String nodeName; if (hostname != null) { final int i = hostname.indexOf(','); if (i > 0) { nodeName = hostname.substring(i + 1); } else { nodeName = hostname; } } else if (enteredHostOrIp != null) { final int i = enteredHostOrIp.indexOf(','); if (i > 0) { nodeName = enteredHostOrIp.substring(i + 1); } else { nodeName = enteredHostOrIp; } } else { return ipAddress; } return nodeName; } else { return name; } } /** Sets name of the host as it will be identified. */ public void setName(final String name) { this.name = name; } /** * Gets string with user and hostname as used in prompt or ssh like * rasto@linbit.at. */ public String getUserAtHost() { return username + '@' + getHostname(); } public Ssh getSSH() { return ssh; } public TerminalPanel getTerminalPanel() { return terminalPanel; } /** * Connects host with ssh. Dialog is needed, in case if password etc. * has to be entered. Connection is made in the background, after * connection is established, callback.done() is called. In case * of error callback.doneError() is called. */ public void connect(SSHGui sshGui, final ConnectionCallback callback) { if (sshGui == null) { sshGui = new SSHGui(mainData.getMainFrame(), this, null); } ssh.connect(sshGui, callback, this); } /** * Connects host with ssh. Dialog is needed, in case if password etc. * has to be entered. Connection is made in the background, after * connection is established, callback.done() is called. In case * of error callback.doneError() is called. * * @param sshGui * ssh gui dialog * * @param progressBar * progress bar that is used to show progress through connecting * * @param callback * callback class that implements ConnectionCallback interface */ public void connect(final SSHGui sshGui, final ProgressBar progressBar, final ConnectionCallback callback) { LOG.debug1("connect: host: " + sshGui); ssh.connect(sshGui, progressBar, callback, this); } /** * Register a component that will be enabled if the host connected and * disabled if disconnected. */ public void registerEnableOnConnect(final JComponent c) { if (!enableOnConnectElements.contains(c)) { enableOnConnectElements.add(c); } swingUtils.invokeLater(new Runnable() { @Override public void run() { c.setEnabled(isConnected()); } }); } /** * Is called after the host is connected or disconnected and * enables/disables the conponents that are registered to be enabled on * connect. */ public void setConnected() { final boolean con = isConnected(); swingUtils.invokeLater(new Runnable() { @Override public void run() { for (final JComponent c : enableOnConnectElements) { c.setEnabled(con); } } }); if (lastConnectionCheckPositive != con) { lastConnectionCheckPositive = con; if (con) { LOG.info("setConnected: " + getName() + ": connection established"); } else { LOG.info("setConnected: " + getName() + ": connection lost"); } final ClusterBrowser cb = getBrowser().getClusterBrowser(); if (cb != null) { cb.getCrmGraph().repaint(); cb.getDrbdGraph().repaint(); } } } /** Make an ssh connection to the host. */ public void connect(SSHGui sshGui, final boolean useProgressIndicator, final int index) { if (!isConnected()) { final String hostName = getName(); if (useProgressIndicator) { progressIndicator.startProgressIndicator(hostName, Tools.getString("Dialog.Host.SSH.Connecting") + " (" + index + ')'); } if (sshGui == null) { sshGui = new SSHGui(mainData.getMainFrame(), this, null); } connect(sshGui, new ConnectionCallback() { @Override public void done(final int flag) { setConnected(); getSSH().execCommandAndWait(new ExecCommandConfig() .command(":") /* activate sudo */ .silentCommand() .silentOutput() .sshCommandTimeout(10000)); getSSH().installGuiHelper(); hostParser.getAllInfo(); if (useProgressIndicator) { progressIndicator.stopProgressIndicator( hostName, Tools.getString("Dialog.Host.SSH.Connecting") + " (" + index + ')'); } } @Override public void doneError(final String errorText) { setLoadingError(); setConnected(); if (useProgressIndicator) { progressIndicator.stopProgressIndicator( hostName, Tools.getString("Dialog.Host.SSH.Connecting") + " (" + index + ')'); progressIndicator.progressIndicatorFailed( hostName, Tools.getString("Dialog.Host.SSH.Connecting") + " (" + index + ')'); progressIndicator.stopProgressIndicator( hostName, Tools.getString("Dialog.Host.SSH.Connecting") + " (" + index + ')'); } } }); } } public void startPing() { ssh.execCommand(new ExecCommandConfig() .commandString("PingCommand") .inBash(true) .inSudo(false) .execCallback(new ExecCallback() { @Override public void done(final String ans) { } @Override public void doneError(final String ans, final int exitCode) { } }) .newOutputCallback(new NewOutputCallback() { @Override public void output(final String output) { ping.set(true); } }) .silentCommand() .silentOutput() .sshCommandTimeout(PING_TIMEOUT)).block(); } public void startConnectionStatus() { final Thread thread = new Thread(new Runnable() { @Override public void run() { while (true) { if (ping.get()) { LOG.debug2("startConnectionStatus: connection ok on " + getName()); setConnected(); ping.set(false); } else { LOG.debug2("startConnectionStatus: connection lost on " + getName()); getSSH().forceReconnect(); setConnected(); } Tools.sleep(PING_TIMEOUT); final ClusterBrowser cb = getBrowser().getClusterBrowser(); /* cluster could be removed */ if (cb == null || cb.isCancelServerStatus()) { break; } } } }); thread.start(); } /** Returns whether host ssh connection was established. */ public boolean isConnected() { if (ssh == null) { return false; } return ssh.isConnected(); } public void saveGraphPositions(final Map<String, Point2D> positions) { final StringBuilder lines = new StringBuilder(); for (final String id : positions.keySet()) { final Point2D p = positions.get(id); double x = p.getX(); if (x < 0) { x = 0; } double y = p.getY(); if (y < 0) { y = 0; } lines.append(id).append(";x=").append(x).append(";y=").append(y).append('\n'); } getSSH().createConfig(lines.toString(), "drbdgui.cf", "/var/lib/heartbeat/", "0600", false, null, null); } /** * Sets the 'is loading' latch, so that something can wait while the load * sequence is running. */ public void setIsLoading() { isLoadingGate = new CountDownLatch(1); } /** Waits on the 'is loading' latch. */ public void waitOnLoading() { if (isLoadingGate == null) { return; } try { isLoadingGate.await(); } catch (final InterruptedException ignored) { Thread.currentThread().interrupt(); } } /** * When loading is done, this latch is opened and whatever is waiting on it * is notified. */ public void setLoadingDone() { isLoadingGate.countDown(); } /** * When loading is done but with an error. Currently it is the same as * setLoadingDone(). */ public void setLoadingError() { isLoadingGate.countDown(); } public String getSSHPort() { return sshPort; } public int getSSHPortInt() { return Integer.valueOf(sshPort); } /** Sets ssh port. */ public void setSSHPort(final String sshPort) { if (this.sshPort != null && !sshPort.equals(this.sshPort)) { ssh.disconnect(); } this.sshPort = sshPort; } public String getSudoPassword() { return sudoPassword; } public void setSudoPassword(final String sudoPassword) { this.sudoPassword = sudoPassword; } public Boolean isUseSudo() { return useSudo; } public void setUseSudo(final Boolean useSudo) { this.useSudo = useSudo; } public void setPacemakerInstallMethodIndex(final String pacemakerInstallMethodIndex) { this.pacemakerInstallMethodIndex = pacemakerInstallMethodIndex; } public String getPacemakerInstallMethodIndex() { return pacemakerInstallMethodIndex; } public void setHeartbeatPacemakerInstallMethodIndex(final String heartbeatPacemakerInstallMethodIndex) { this.heartbeatPacemakerInstallMethodIndex = heartbeatPacemakerInstallMethodIndex; } public String getHeartbeatPacemakerInstallMethodIndex() { return heartbeatPacemakerInstallMethodIndex; } /** Returns MD5 checksum of VM Info from server. */ public String getVMInfoMD5() { return vmInfoFromServerMD5; } /** Sets MD5 checksum of VM Info from server. */ public void setVMInfoMD5(final String vmInfoMD5) { this.vmInfoFromServerMD5 = vmInfoMD5; } public void setPositionInTheCluster(final int positionInTheCluster) { this.positionInTheCluster = positionInTheCluster; } public int getPositionInTheCluster() { return positionInTheCluster; } /** This is part of testsuite. */ boolean checkTest(final String checkCommand, final String test, final double index, final String name, final int maxHosts) { Tools.sleep(1500); final StringBuilder command = new StringBuilder(50); command.append(DistResource.SUDO).append(hostParser.replaceVars("@GUI-HELPER@")); command.append(' '); command.append(checkCommand); command.append(' '); command.append(test); command.append(' '); final String indexString = Double.toString(index).replaceFirst("\\.0+$", ""); command.append(indexString); if (name != null) { command.append(' '); command.append(name); } int h = 1; for (final Host host : getCluster().getHosts()) { LOG.debug1("checkTest: host" + h + " = " + host.getName()); command.append(' '); command.append(host.getName()); if (maxHosts > 0 && h >= maxHosts) { break; } h++; } command.append(" 2>&1"); int i = 0; SshOutput out; do { out = getSSH().execCommandAndWait(new ExecCommandConfig().command(command.toString()) .sshCommandTimeout(60000)); if (out.getExitCode() == 0 || out.getExitCode() == 10) { break; } i++; roboTest.sleepNoFactor(i * 2000); } while (i < 5); String nameS = ' ' + name; if (name == null) { nameS = ""; } if (i > 0) { roboTest.info(getName() + ' ' + test + ' ' + index + nameS + " tries: " + (i + 1)); } roboTest.info(getName() + ' ' + test + ' ' + index + nameS + ' ' + out.getOutput()); return out.getExitCode() == 0; } /** This is part of testsuite, it checks Pacemaker. */ public boolean checkPCMKTest(final String test, final double index) { return checkTest("gui-test", test, index, null, 0); } /** This is part of testsuite, it checks DRBD. */ public boolean checkDRBDTest(final String test, final double index) { final StringBuilder testName = new StringBuilder(20); if (application.getBigDRBDConf()) { testName.append("big-"); } if (!hostParser.hasVolumes()) { testName.append("novolumes-"); } testName.append(test); return checkTest("gui-drbd-test", testName.toString(), index, null, 2); } /** This is part of testsuite, it checks VMs. */ public boolean checkVMTest(final String test, final double index, final String name) { return checkTest("gui-vm-test", test, index, name, 0); } /** Returns color of this host. Null if it is default color. */ public String getColor() { if (savedHostColorInGraphs == null || defaultHostColorInGraph == savedHostColorInGraphs) { return null; } return Integer.toString(savedHostColorInGraphs.getRGB()); } /** Sets color of this host. Don't if it is default color. */ public void setSavedColor(final String colorString) { try { savedHostColorInGraphs = new Color(Integer.parseInt(colorString)); } catch (final NumberFormatException e) { LOG.appWarning("setSavedColor: could not parse: " + colorString); /* ignore it */ } } public void setSavable(final boolean savable) { this.savable = savable; } public boolean isSavable() { return savable; } public boolean isRoot() { return ROOT_USER.equals(username); } public void updateDrbdParameters() { final ClusterBrowser cb = getBrowser().getClusterBrowser(); final DrbdXml drbdXml = cb.getDrbdXml(); final String output = drbdXml.updateDrbdParameters(this); if (output != null) { drbdXml.parseDrbdParameters(this, output, cb.getClusterHosts()); cb.getHostDrbdParameters().put(this, output); } } /** Compares ignoring case. */ @Override public int compareTo(final Host h) { return Tools.compareNames(getName(), h.getName()); } @Override public String getValueForGui() { return getName(); } @Override public String getValueForConfig() { return getName(); } @Override public boolean isNothingSelected() { return getName() == null; } @Override public Unit getUnit() { return null; } @Override public String getValueForConfigWithUnit() { return getValueForConfig(); } @Override public String getNothingSelected() { return NOTHING_SELECTED; } public String isDrbdUtilCompatibleWithDrbdModule() { if (!DRBD.compatibleVersions(drbdHost.getDrbdUtilVersion(), drbdHost.getDrbdModuleVersion())) { return "DRBD util and module versions are not compatible: " + drbdHost.getDrbdUtilVersion() + " / " + drbdHost.getDrbdModuleVersion(); } return null; } public String getDrbdInfoAboutInstallation() { final StringBuilder tt = new StringBuilder(40); final String drbdV = drbdHost.getDrbdUtilVersion(); final String drbdModuleV = drbdHost.getDrbdModuleVersion(); final String drbdS; if (drbdV == null || drbdV.isEmpty()) { drbdS = "not installed"; } else { drbdS = drbdV; } final String drbdModuleS; if (drbdModuleV == null || drbdModuleV.isEmpty()) { drbdModuleS = "not installed"; } else { drbdModuleS = drbdModuleV; } tt.append("\nDRBD "); tt.append(drbdS); if (!drbdS.equals(drbdModuleS)) { tt.append("\nDRBD module "); tt.append(drbdModuleS); } if (drbdHost.isDrbdLoaded()) { tt.append(" (running)"); } else { tt.append(" (not loaded)"); } return tt.toString(); } public void waitForHostAndDrbd() { while (!isConnected() || !drbdHost.isDrbdLoaded()) { try { Thread.sleep(10000); } catch (final InterruptedException ex) { Thread.currentThread().interrupt(); } } } public boolean drbdVersionHigherOrEqual(final String drbdVersion) throws Exceptions.IllegalVersionException { return Tools.compareVersions(drbdHost.getDrbdUtilVersion(), drbdVersion) >= 0; } public boolean drbdVersionSmaller(final String drbdVersion) throws Exceptions.IllegalVersionException { return Tools.compareVersions(drbdHost.getDrbdUtilVersion(), drbdVersion) < 0; } public boolean isDrbdLoaded() { return drbdHost.isDrbdLoaded(); } public boolean isDrbdProxyRunning() { return drbdHost.isDrbdProxyRunning(); } public boolean hasDrbd() { return drbdHost.getDrbdUtilVersion() != null; } public boolean drbdVersionSmallerOrEqual(final String drbdVersion) throws Exceptions.IllegalVersionException { return Tools.compareVersions(drbdHost.getDrbdUtilVersion(), drbdVersion) <= 0; } public Collection<BlockDevice> getBlockDevices() { return blockDeviceService.getBlockDevices(this); } public void setCrmStatusOk(final boolean crmStatusOk) { this.crmStatusOk = crmStatusOk; } public boolean isCrmStatusOk() { return crmStatusOk && isConnected(); } public String getDistString(String command) { return hostParser.getDistString(command); } /** * Returns info string about Pacemaker installation. */ public String getPacemakerInfo() { final StringBuilder pacemakerInfo = new StringBuilder(40); final String pmV = hostParser.getPacemakerVersion(); final String hbV = hostParser.getHeartbeatVersion(); final StringBuilder hbRunning = new StringBuilder(20); if (hostParser.isHeartbeatRunning()) { hbRunning.append("running"); if (!hostParser.isHeartbeatInRc()) { hbRunning.append("/no rc.d"); } } else { hbRunning.append("not running"); } if (hostParser.isHeartbeatInRc()) { hbRunning.append("/rc.d"); } if (pmV == null) { if (hbV != null) { pacemakerInfo.append(" \nHeartbeat "); pacemakerInfo.append(hbV); pacemakerInfo.append(" ("); pacemakerInfo.append(hbRunning); pacemakerInfo.append(')'); } } else { final String pmRunning; if (isCrmStatusOk()) { pmRunning = "running"; } else { pmRunning = "not running"; } pacemakerInfo.append(" \nPacemaker "); pacemakerInfo.append(pmV); pacemakerInfo.append(" ("); pacemakerInfo.append(pmRunning); pacemakerInfo.append(')'); String corOrAis = null; final String corV = hostParser.getCorosyncVersion(); final String aisV = hostParser.getOpenaisVersion(); if (corV != null) { corOrAis = "Corosync " + corV; } else if (aisV != null) { corOrAis = "Openais " + aisV; } if (hbV != null && hostParser.isHeartbeatRunning()) { pacemakerInfo.append(" \nHeartbeat "); pacemakerInfo.append(hbV); pacemakerInfo.append(" ("); pacemakerInfo.append(hbRunning); pacemakerInfo.append(')'); } if (corOrAis != null) { pacemakerInfo.append(" \n"); pacemakerInfo.append(corOrAis); pacemakerInfo.append(" ("); if (hostParser.isCorosyncRunning() || hostParser.isOpenaisRunning()) { pacemakerInfo.append("running"); if (!hostParser.isCorosyncInRc() && !hostParser.isOpenaisInRc()) { pacemakerInfo.append("/no rc.d"); } } else { pacemakerInfo.append("not running"); } if (hostParser.isCorosyncInRc() || hostParser.isOpenaisInRc()) { pacemakerInfo.append("/rc.d"); } pacemakerInfo.append(')'); } if (hbV != null && !hostParser.isHeartbeatRunning()) { pacemakerInfo.append(" \nHeartbeat "); pacemakerInfo.append(hbV); pacemakerInfo.append(" ("); pacemakerInfo.append(hbRunning); pacemakerInfo.append(')'); } } return pacemakerInfo.toString(); } public boolean hasVolumes() { return hostParser.hasVolumes(); } public boolean isDrbdStatusOk() { return hostParser.isDrbdStatusOk(); } public void setDrbdStatusOk(boolean status) { hostParser.setDrbdStatusOk(status); } public void drbdStatusLock() { hostParser.drbdStatusLock(); } public void drbdStatusUnlock() { hostParser.drbdStatusUnlock(); } public void vmStatusLock() { hostParser.vmStatusLock(); } public void vmStatusUnlock() { hostParser.vmStatusUnlock(); } public String getDistCommand(final String command, final ConvertCmdCallback convertCmdCallback) { return hostParser.getDistCommand(command, convertCmdCallback); } public String getDistCommand(final String command, final Map<String, String> resVolReplaceHash) { return hostParser.getDistCommand(command, resVolReplaceHash); } public String getArch() { return hostParser.getArch(); } public Set<String> getAvailableCryptoModules() { return hostParser.getAvailableCryptoModules(); } public String getHeartbeatLibPath() { return hostParser.getHeartbeatLibPath(); } }