Package lcmc.host.domain

Source Code of lcmc.host.domain.Host

/*
* 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.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javax.swing.JComponent;

import com.google.common.base.Optional;
import lcmc.ClusterEventBus;
import lcmc.Exceptions;
import lcmc.cluster.domain.Cluster;
import lcmc.configs.DistResource;
import lcmc.cluster.ui.ClusterBrowser;
import lcmc.common.ui.GUIData;
import lcmc.event.BlockDevicesChangedEvent;
import lcmc.event.BlockDevicesDiskSpaceEvent;
import lcmc.event.BridgesChangedEvent;
import lcmc.event.DrbdStatusChangedEvent;
import lcmc.event.NetInterfacesChangedEvent;
import lcmc.host.service.BlockDeviceService;
import lcmc.host.service.NetInterfaceService;
import lcmc.host.ui.HostBrowser;
import lcmc.common.ui.ProgressBar;
import lcmc.common.ui.ResourceGraph;
import lcmc.cluster.ui.SSHGui;
import lcmc.common.ui.TerminalPanel;
import lcmc.drbd.domain.DrbdHost;
import lcmc.drbd.domain.DrbdXml;
import lcmc.common.domain.Application;
import lcmc.common.domain.StringValue;
import lcmc.common.domain.Value;
import lcmc.drbd.domain.BlockDevice;
import lcmc.drbd.domain.NetInterface;
import lcmc.vm.domain.VmsXml;
import lcmc.common.ui.CategoryInfo;
import lcmc.robotest.RoboTest;
import lcmc.common.domain.ConnectionCallback;
import lcmc.common.domain.ConvertCmdCallback;
import lcmc.drbd.service.DRBD;
import lcmc.common.domain.ExecCallback;
import lcmc.logger.Logger;
import lcmc.logger.LoggerFactory;
import lcmc.common.domain.NewOutputCallback;
import lcmc.common.domain.util.Tools;
import lcmc.common.domain.Unit;
import lcmc.cluster.service.ssh.ExecCommandConfig;
import lcmc.cluster.service.ssh.Ssh;
import lcmc.cluster.service.ssh.ExecCommandThread;
import lcmc.cluster.service.ssh.SshOutput;

/**
* This class holds host data and implementation of host related methods.
*/
@Named
public class Host implements Comparable<Host>, Value {
    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";
    public static final Pattern BLOCK_DEV_FILE_PATTERN = Pattern.compile("(\\D+)\\d+");
    public static final Pattern DRBD_DEV_FILE_PATTERN = Pattern.compile(".*\\/drbd\\d+$");
    public static final Pattern USED_DISK_SPACE_PATTERN = Pattern.compile("^(.*) (\\d+)$");
    /** 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;
    private static final int HW_INFO_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";
    private static final String LOG_COMMANDS_ON_SERVER_OPTION = "--cmd-log";

    private static final String NET_INFO_DELIM = "net-info";
    private static final String BRIDGE_INFO_DELIM = "bridge-info";
    private static final String DISK_INFO_DELIM = "disk-info";
    private static final String DISK_SPACE_DELIM = "disk-space";
    private static final String VG_INFO_DELIM = "vg-info";
    private static final String FILESYSTEMS_INFO_DELIM = "filesystems-info";
    private static final String CRYPTO_INFO_DELIM = "crypto-info";
    private static final String QEMU_KEYMAPS_INFO_DELIM = "qemu-keymaps-info";
    private static final String CPU_MAP_MODEL_INFO_DELIM = "cpu-map-model-info";
    private static final String CPU_MAP_VENDOR_INFO_DELIM = "cpu-map-vendor-info";
    private static final String MOUNT_POINTS_INFO_DELIM = "mount-points-info";
    private static final String GUI_INFO_DELIM = "gui-info";
    private static final String INSTALLATION_INFO_DELIM = "installation-info";
    private static final String GUI_OPTIONS_INFO_DELIM = "gui-options-info";
    private static final String VERSION_INFO_DELIM = "version-info";
    private static final String DRBD_PROXY_INFO_DELIM = "drbd-proxy-info";

    private static final Collection<String> INFO_TYPES =
             new HashSet<String>(Arrays.asList(new String[]{NET_INFO_DELIM,
                     BRIDGE_INFO_DELIM,
                     DISK_INFO_DELIM,
                     DISK_SPACE_DELIM,
                     VG_INFO_DELIM,
                     FILESYSTEMS_INFO_DELIM,
                     CRYPTO_INFO_DELIM,
                     QEMU_KEYMAPS_INFO_DELIM,
                     CPU_MAP_MODEL_INFO_DELIM,
                     CPU_MAP_VENDOR_INFO_DELIM,
                     MOUNT_POINTS_INFO_DELIM,
                     GUI_INFO_DELIM,
                     INSTALLATION_INFO_DELIM,
                     GUI_OPTIONS_INFO_DELIM,
                     VERSION_INFO_DELIM,
                     DRBD_PROXY_INFO_DELIM}));
    public static final boolean UPDATE_LVM = true;

    private static final String TOKEN_DISK_ID = "disk-id";
    private static final String TOKEN_UUID    = "uuid";
    private static final String TOKEN_SIZE    = "size";
    private static final String TOKEN_MP      = "mp";
    private static final String TOKEN_FS      = "fs";
    private static final String TOKEN_VG      = "vg";
    private static final String TOKEN_LV      = "lv";
    private static final String TOKEN_PV      = "pv";

    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 String detectedKernelName = "";
    private String detectedDist = "";
    private String detectedDistVersion = "";
    private String detectedKernelVersion = "";
    private String detectedArch = "";
    private String kernelName = "";
    private String distributionName = "";
    private String distributionVersion = "";
    private String distributionVersionString = "";
    private String kernelVersion = "";
    private String arch = "";
    private Set<String> availableFileSystems = new TreeSet<String>();
    private Set<String> availableCryptoModules = new TreeSet<String>();
    private Set<Value> availableQemuKeymaps = new TreeSet<Value>();
    private Set<Value> availableCpuMapModels = new TreeSet<Value>();
    private Set<Value> availableCpuMapVendors = new TreeSet<Value>();
    private Set<String> mountPoints = new TreeSet<String>();
    private Map<String, BlockDevice> drbdBlockDevices = new LinkedHashMap<String, BlockDevice>();
    /** Options for GUI drop down lists. */
    private Map<String, List<String>> guiOptions = new HashMap<String, List<String>>();
    private Set<String> drbdResourcesWithProxy = new HashSet<String>();
    private Color defaultHostColorInGraph;
    private Color savedHostColorInGraphs;
    private ExecCommandThread drbdStatusThread = null;
    private ExecCommandThread crmStatusThread = null;
    private ExecCommandThread serverStatusThread = null;
    /** List of positions of the services.
     *  Question is this: the saved positions can be different on different
     *  hosts, but only one can be used in the crm graph.
     *  Only one will be used and by next save the problem solves itself.
     */
    private final Map<String, Point2D> servicePositions = new HashMap<String, Point2D>();
    private String pacemakerVersion = null;
    private String openaisVersion = null;
    private boolean commLayerStopping = false;
    private boolean commLayerStarting = false;
    private boolean pacemakerStarting = false;
    private boolean drbdProxyStarting = false;
    private boolean corosyncInRc = false;
    private boolean openaisInRc = false;
    private boolean corosyncHasInitScript = false;
    private boolean openaisHasInitScript = false;
    private boolean corosyncRunning = false;
    private boolean openaisRunning = false;
    private boolean corosyncOrOpenaisConfigExists = false;

    private boolean heartbeatInRc = false;
    private boolean heartbeatRunning = false;
    private boolean heartbeatConfigExists = false;
    private boolean heartbeatHasInitScript = false;

    private boolean pacemakerInRc = false;
    private boolean pacemakerRunning = false;
    private boolean pacemakerHasInitScript = false;
    /** Pacemaker service version. From version 1, use pacamker init script. */
    private int pcmkServiceVersion = -1;
    private String corosyncVersion = null;
    private String heartbeatVersion = null;
    private boolean crmStatusOk = false;
    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 CountDownLatch waitForServerStatusLatch = new CountDownLatch(1);
    private final Collection<JComponent> enableOnConnectElements = new ArrayList<JComponent>();
    private String pacemakerInstallMethodIndex;
    private String heartbeatPacemakerInstallMethodIndex;
    private String heartbeatLibPath = null;
    private String vmInfoFromServerMD5 = null;
    private int positionInTheCluster = 0;
    private volatile boolean lastConnectionCheckPositive = false;
    private Boolean corosyncOrHeartbeatRunning = null;
    private String libvirtVersion = null;
    private List<BlockDevice> physicalVolumes = new ArrayList<BlockDevice>();
    private Map<String, Long> volumeGroups = new LinkedHashMap<String, Long>();
    private Map<String, Set<String>> volumeGroupsWithLvs = new HashMap<String, Set<String>>();
    private boolean savable = true;
    /** Ping is set every 10s. */
    private volatile AtomicBoolean ping = new AtomicBoolean(true);
    private final Lock mDRBDStatusLock = new ReentrantLock();
    private final Lock mUpdateVMSlock = new ReentrantLock();
    private final Lock mInfoTimestampLock = new ReentrantLock();
    /** Time stamp hash. */
    private final Map<String, Double> infoTimestamp = new HashMap<String, Double>();
    private boolean inCluster = false;
    /** Whether dist info was already logged. */
    private boolean distInfoAlreadyLogged = false;

    private boolean drbdStatusOk = false;

    @Inject
    private DrbdHost drbdHost;
    @Inject
    private TerminalPanel terminalPanel;
    @Inject
    private GUIData guiData;
    @Inject
    private Ssh ssh;
    @Inject
    private HostBrowser hostBrowser;
    @Inject
    private Provider<DrbdXml> drbdXmlProvider;
    @Inject
    private Hosts allHosts;
    @Inject
    private Application application;
    @Inject
    private RoboTest roboTest;
    @Inject
    private ClusterEventBus eventBus;
    @Inject
    private BlockDeviceService blockDeviceService;
    @Inject
    private NetInterfaceService netInterfacesService;

    public void init() {
        if (allHosts.size() == 1) {
            enteredHostOrIp = Tools.getDefault("SSH.SecHost");
        }
        mountPoints.add("/mnt/");

        hostBrowser.init(this);
        terminalPanel.initWithHost(this);
        eventBus.register(this);
    }

    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 (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 void setCrmStatusOk(final boolean crmStatusOk) {
        this.crmStatusOk = crmStatusOk;
    }

    public boolean isCrmStatusOk() {
        return crmStatusOk && isConnected();
    }

    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 void removeFileSystems() {
        availableFileSystems.clear();
    }

    public String[] getAvailableFileSystems() {
        return availableFileSystems.toArray(new String[availableFileSystems.size()]);
    }

    public Set<String> getFileSystemsList() {
        return availableFileSystems;
    }

    public Set<String> getAvailableCryptoModules() {
        return availableCryptoModules;
    }

    public Set<Value> getAvailableQemuKeymaps() {
        return availableQemuKeymaps;
    }

    public Set<Value> getCPUMapModels() {
        return availableCpuMapModels;
    }

    public Set<Value> getCPUMapVendors() {
        return availableCpuMapVendors;
    }

    public Set<String> getMountPointsList() {
        return mountPoints;
    }

    public String[] getIps(final int hop) {
        return allIps.get(hop);
    }

    /**
     * Sets distribution info for this host from array of strings.
     * Array consists of: kernel name, kernel version, arch, os, version
     * and distribution.
     */
    @SuppressWarnings("fallthrough")
    void setDistInfo(final String[] info) {
        if (info == null) {
            LOG.debug("setDistInfo: " + getName() + " dist info is null");
            return;
        }
        if (!distInfoAlreadyLogged) {
            for (final String di : info) {
                LOG.debug1("setDistInfo: dist info: " + di);
            }
        }

        /* no breaks in the switch statement are intentional */
        String lsbVersion = null;
        String lsbDist = null;
        switch (info.length) {
            case 9:
                lsbVersion = info[8]; // TODO: not used
            case 8:
                lsbDist = info[7];
            case 7:
                lsbVersion = info[6]; // TODO: not used
            case 6:
                lsbDist = info[5];
            case 5:
                if (lsbDist == null || "linux".equals(lsbDist)) {
                    detectedDist = info[4];
                } else {
                    detectedDist = lsbDist;
                }
            case 4:
                if (lsbVersion == null) {
                    detectedDistVersion = info[3];
                } else {
                    detectedDistVersion = info[3] + '/' + lsbVersion;
                }
            case 3:
                detectedKernelVersion = info[2];
            case 2:
                detectedArch = info[1];
            case 1:
                detectedKernelName = info[0];
            case 0:
                break;
            default:
                LOG.appError("setDistInfo: list: ", Arrays.asList(info).toString());
                break;
        }
        distributionName = detectedDist;
        distributionVersion = detectedDistVersion;
        initDistInfo();
        if (!distInfoAlreadyLogged) {
            LOG.debug1("setDistInfo: kernel name: " + detectedKernelName);
            LOG.debug1("setDistInfo: kernel version: " + detectedKernelVersion);
            LOG.debug1("setDistInfo: arch: " + detectedArch);
            LOG.debug1("setDistInfo: dist version: " + detectedDistVersion);
            LOG.debug1("setDistInfo: dist: " + detectedDist);
        }
        distInfoAlreadyLogged = true;
    }

    /** Initializes dist info. Must be called after setDistInfo. */
    void initDistInfo() {
        if (!"Linux".equals(detectedKernelName)) {
            LOG.appWarning("initDistInfo: detected kernel not linux: " + detectedKernelName);
        }
        setKernelName("Linux");

        if (!distributionName.equals(detectedDist)) {
            LOG.appError("initDistInfo: dist: " + distributionName + " does not match " + detectedDist);
        }
        distributionVersionString = Tools.getDistVersionString(distributionName, distributionVersion);
        distributionVersion = Tools.getDistString("distributiondir", detectedDist, distributionVersionString, null);
        setKernelVersion(Tools.getKernelDownloadDir(detectedKernelVersion, getDistributionName(), distributionVersionString, null));
        String arch0 = Tools.getDistString("arch:" + detectedArch, getDistributionName(), distributionVersionString, null);
        if (arch0 == null) {
            arch0 = detectedArch;
        }
        setArch(arch0);
    }

    /** Returns the detected info to show. */
    public String getDetectedInfo() {
        return detectedDist + ' ' + detectedDistVersion;
    }

    /**
     * Gets distribution name from distribution version. E.g. suse from sles9.
     * This is used only when the distribution is selected in the pulldown menu,
     * not when it is detected.
     * The conversion rules for distributions are defined in DistResource.java,
     * with 'dist:' prefix.
     * TODO: remove it?
     */
    public String getDistFromDistVersion(final String dV) {
        /* remove numbers */
        if ("No Match".equals(dV)) {
            return null;
        }
        LOG.debug1("getDistFromDistVersion:" + dV.replaceFirst("\\d.*", ""));
        return Tools.getDistString("dist:" + dV.replaceFirst("\\d.*", ""), "", "", null);
    }

    void setDistributionName(final String dist) {
        this.distributionName = dist;
    }

    void setDistributionVersion(final String distVersion) {
        this.distributionVersion = distVersion;
        distributionVersionString = Tools.getDistVersionString(distributionName, distVersion);
        distributionName = getDistFromDistVersion(distVersion);
    }

    /** Sets arch, e.g. "i386". */
    public void setArch(final String arch) {
        this.arch = arch;
    }

    /** Sets kernel name, e.g. "linux". */
    void setKernelName(final String kernelName) {
        this.kernelName = kernelName;
    }

    void setKernelVersion(final String kernelVersion) {
        this.kernelVersion = kernelVersion;
    }

    /** Gets kernel name. Normaly "Linux" for this application. */
    public String getKernelName() {
        return kernelName;
    }

    /**
     * Gets kernel version. Usually some version,
     * like: "2.6.13.2ws-k7-up-lowmem".
     */
    public String getKernelVersion() {
        return kernelVersion;
    }

    public String getDetectedKernelVersion() {
        return detectedKernelVersion;
    }

    /** Gets architecture like i686. */
    public String getArch() {
        return arch;
    }

    public String getHeartbeatLibPath() {
        if (heartbeatLibPath != null) {
            return heartbeatLibPath;
        }
        if ("".equals(arch)) {
            LOG.appWarning("getHeartbeatLibPath: called to soon: unknown arch");
        } else if ("x86_64".equals(arch) || "amd64".equals(arch)) {
            return "/usr/lib64/heartbeat";
        }
        return "/usr/lib/heartbeat";
    }

    public String getLxcLibPath() {
        return getDistString("libvirt.lxc.libpath");
    }

    /** Returns xen lib path. */
    public String getXenLibPath() {
        return getDistString("libvirt.xen.libpath");
    }

    /** Gets distribution, e.g., debian. */
    public String getDistributionName() {
        return distributionName;
    }

    public String getDistributionVersion() {
        return distributionVersion;
    }

    public String getDistributionVersionString() {
        return distributionVersionString;
    }

    public void disconnect() {
        if (ssh.isConnected()) {
            ssh.forceDisconnect();
        }
        setVMInfoMD5(null);
    }

    /**
     * Converts command string to real command for a distribution, specifying
     * the convert command callback.
     */
    public String getDistCommand(final String commandString, final ConvertCmdCallback convertCmdCallback) {
        return Tools.getDistCommand(commandString,
                                    distributionName,
                                    distributionVersionString,
                                    arch,
                                    convertCmdCallback,
                                    false,  /* in bash */
                                    false); /* sudo */
    }

    /** Converts a string that is specific to the distribution distribution. */
    public String getDistString(final String commandString) {
        return Tools.getDistString(commandString, distributionName, distributionVersionString, arch);
    }

    /**
     *  Gets list of strings that are specific to the distribution
     *  distribution.
     */
    public List<String> getDistStrings(final String commandString) {
        return Tools.getDistStrings(commandString, distributionName, distributionVersionString, arch);
    }


    /**
     * Converts command string to real command for a distribution, specifying
     * what-with-what hash.
     */
    public String getDistCommand(final String commandString, final Map<String, String> replaceHash) {
        return Tools.getDistCommand(
                    commandString,
                    distributionName,
                    distributionVersionString,
                    arch,
                    new ConvertCmdCallback() {
                        @Override
                        public String convert(String command) {
                            for (final String tag : replaceHash.keySet()) {
                                if (tag != null && command.contains(tag)) {
                                    String s = replaceHash.get(tag);
                                    if (s == null) {
                                        s = "";
                                    }
                                    command = command.replaceAll(tag, s);
                                }
                            }
                            return command;
                        }
                    },
                    false,  /* in bash */
                    false); /* sudo */
    }

    /**
     * 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();
        guiData.startProgressIndicator(hostName, text);
        try {
            return ssh.captureCommand(execCommandConfig);
        } finally {
            guiData.stopProgressIndicator(hostName, text);
        }
    }

    public void execCommandProgressIndicator(final String text, final ExecCommandConfig execCommandConfig) {
        final String hostName = getName();
        guiData.startProgressIndicator(hostName, text);
        try {
            ssh.execCommand(execCommandConfig);
        } finally {
            guiData.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 server (hw) status background process. */
    public void stopServerStatus() {
        final ExecCommandThread sst = serverStatusThread;
        if (sst == null) {
            LOG.appWarning("trying to stop stopped server status");
            return;
        }
        sst.cancelTheSession();
        serverStatusThread = null;
    }

    /** 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(guiData.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);
        }
        application.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();
        application.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 progressIndicator, final int index) {
        if (!isConnected()) {
            final String hostName = getName();
            if (progressIndicator) {
                guiData.startProgressIndicator(hostName,
                                               Tools.getString("Dialog.Host.SSH.Connecting") + " (" + index + ')');
            }
            if (sshGui == null) {
                sshGui = new SSHGui(guiData.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();
                            getAllInfo();
                            if (progressIndicator) {
                                guiData.stopProgressIndicator(
                                  hostName,
                                  Tools.getString("Dialog.Host.SSH.Connecting") + " (" + index + ')');
                            }
                        }

                        @Override
                        public void doneError(final String errorText) {
                            setLoadingError();
                            setConnected();
                            if (progressIndicator) {
                                guiData.stopProgressIndicator(
                                  hostName,
                                  Tools.getString("Dialog.Host.SSH.Connecting") + " (" + index + ')');
                                guiData.progressIndicatorFailed(
                                  hostName,
                                  Tools.getString("Dialog.Host.SSH.Connecting") + " (" + index + ')');
                                guiData.stopProgressIndicator(
                                  hostName,
                                  Tools.getString("Dialog.Host.SSH.Connecting") + " (" + index + ')');
                            }
                        }
                    });
        }
    }

    /** Gets and stores info about the host. */
    void getAllInfo() {
        execCommand(new ExecCommandConfig().commandString("GetHostAllInfo")
                .execCallback(new ExecCallback() {
                    @Override
                    public void done(final String ans) {
                        parseHostInfo(ans);
                        setLoadingDone();
                    }

                    @Override
                    public void doneError(final String ans, final int exitCode) {
                        setLoadingError();
                    }
                })
                .sshCommandTimeout(HW_INFO_TIMEOUT)
                .silentCommand()
                .silentOutput()
                .silentCommand()).block();
    }

    /** Gets and stores hardware info about the host. */
    public void getHWInfo(final boolean updateLVM) {
        getHWInfo(new CategoryInfo[]{}, new ResourceGraph[]{}, updateLVM);
    }

    /** Gets and stores hardware info about the host. */
    public void getHWInfo(final CategoryInfo[] infosToUpdate, final ResourceGraph[] graphs, final boolean updateLVM) {
        final String cmd;
        if (updateLVM) {
            cmd = "GetHostHWInfoLVM";
        } else {
            cmd = "GetHostHWInfo";
        }
        execCommand(new ExecCommandConfig().commandString(cmd)
                         .execCallback(new ExecCallback() {
                             @Override
                             public void done(final String ans) {
                                 parseHostInfo(ans);
                                 for (final CategoryInfo ci : infosToUpdate) {
                                     ci.updateTable(CategoryInfo.MAIN_TABLE);
                                 }
                                 for (final ResourceGraph g : graphs) {
                                     if (g != null) {
                                         g.repaint();
                                     }
                                 }
                                 setLoadingDone();
                             }

                             @Override
                             public void doneError(final String ans, final int exitCode) {
                                 setLoadingError();
                                 getSSH().forceReconnect();
                             }
                         })
                         .sshCommandTimeout(HW_INFO_TIMEOUT)
                         .silentCommand()
                         .silentOutput()).block();
    }

    public String getOutput(final String type, final StringBuffer buffer) {
        final String infoStart = "--" + type + "-info-start--";
        final String infoEnd = "--" + type + "-info-end--";
        final int infoStartLength = infoStart.length();
        final int infoEndLength = infoEnd.length();
        final int s = buffer.indexOf(infoStart);
        final int s2 = buffer.indexOf("\r\n", s);
        final int e = buffer.indexOf(infoEnd, s);
        String out = null;
        if (s > -1 && s < s2 && s2 <= e) {
            Double timestamp = null;
            final String ts = buffer.substring(s + infoStartLength, s2);
            try {
                timestamp = Double.parseDouble(ts);
            catch (final NumberFormatException nfe) {
                LOG.debug("getOutput: could not parse: " + ts + ' ' + nfe);
            }
            mInfoTimestampLock.lock();
            if (timestamp != null
                && (!infoTimestamp.containsKey(type) || timestamp >= infoTimestamp.get(type))) {
                infoTimestamp.put(type, timestamp);
                mInfoTimestampLock.unlock();
                out = buffer.substring(s2 + 2, e);
            } else {
                mInfoTimestampLock.unlock();
            }
            buffer.delete(0, e + infoEndLength + 2);
        }
        return out;
    }

    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();
    }

    /** Gets and stores hardware info about the host. */
    public void startHWInfoDaemon(final CategoryInfo[] infosToUpdate, final ResourceGraph[] graphs) {
        final Host host = this;
        LOG.debug1("startHWInfoDaemon: " + getName());
        serverStatusThread = ssh.execCommand(new ExecCommandConfig()
                         .commandString("HostHWInfoDaemon")
                         .inBash(false)
                         .inSudo(false)
                         .execCallback(new ExecCallback() {
                             @Override
                             public void done(final String ans) {
                                 parseHostInfo(ans);
                                 for (final CategoryInfo ci : infosToUpdate) {
                                     ci.updateTable(CategoryInfo.MAIN_TABLE);
                                 }
                                 for (final ResourceGraph g : graphs) {
                                     if (g != null) {
                                         g.repaint();
                                     }
                                 }
                                 if (host.getWaitForServerStatusLatch()) {
                                     final ClusterBrowser cb = getBrowser().getClusterBrowser();
                                     cb.updateServerStatus(host);
                                 }
                                 setLoadingDone();
                             }

                             @Override
                             public void doneError(final String ans, final int exitCode) {
                                 if (host.getWaitForServerStatusLatch()) {
                                     final ClusterBrowser cb = getBrowser().getClusterBrowser();
                                     cb.updateServerStatus(host);
                                 }
                                 setLoadingError();
                             }
                         })
                         .newOutputCallback(new NewOutputCallback() {
                             private final StringBuffer outputBuffer = new StringBuffer(300);
                             @Override
                             public void output(final String output) {
                                 outputBuffer.append(output);
                                 final ClusterBrowser cb = getBrowser().getClusterBrowser();
                                 String hw, vm, drbdConfig;
                                 String hwUpdate = null;
                                 String vmUpdate = null;
                                 String drbdUpdate = null;
                                 do {
                                     hw = getOutput("hw", outputBuffer);
                                     if (hw != null) {
                                         hwUpdate = hw;
                                     }
                                     vm = getOutput("vm", outputBuffer);
                                     if (vmStatusTryLock()) {
                                         if (vm != null) {
                                             vmUpdate = vm;
                                         }
                                         vmStatusUnlock();
                                     }
                                     drbdStatusLock();
                                     drbdConfig = getOutput("drbd", outputBuffer);
                                     if (drbdConfig != null) {
                                         drbdUpdate = drbdConfig;
                                     }
                                     drbdStatusUnlock();
                                 } while (hw != null || vm != null || drbdConfig != null);

                                 Tools.chomp(outputBuffer);
                                 if (hwUpdate != null) {
                                     parseHostInfo(hwUpdate);
                                     for (final ResourceGraph g : graphs) {
                                         if (g != null) {
                                             g.repaint();
                                         }
                                     }
                                 }
                                 if (vmUpdate != null) {
                                     final VmsXml newVmsXml = new VmsXml(host);
                                     if (newVmsXml.update(vmUpdate)) {
                                         cb.vmsXmlPut(host, newVmsXml);
                                         cb.updateVms();
                                     }
                                 }
                                 if (drbdUpdate != null) {
                                     final DrbdXml dxml = drbdXmlProvider.get();
                                     dxml.init(cluster.getHostsArray(), cb.getHostDrbdParameters());
                                     dxml.update(drbdUpdate);
                                     cb.setDrbdXml(dxml);
                                     application.invokeLater(new Runnable() {
                                         @Override
                                         public void run() {
                                             hostBrowser.getClusterBrowser().getGlobalInfo().setParameters();
                                             cb.updateDrbdResources();
                                         }
                                     });
                                 }
                                 if (drbdUpdate != null
                                     || vmUpdate != null) {
                                     cb.updateHWInfo(host, !Host.UPDATE_LVM);
                                 }
                                 if (drbdUpdate != null) {
                                     cb.updateServerStatus(host);
                                 }
                                 if (getWaitForServerStatusLatch()) {
                                     cb.updateServerStatus(host);
                                 }
                                 setLoadingDone();
                             }
                         })
                         .silentCommand()
                         .silentOutput()
                         .sshCommandTimeout(HW_INFO_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();
    }

    /**
     * replaces variables in command. For output to the user set hidePassword
     * to true, so that variables like passwords are not shown to the user.
     * This functions should not be used to really sensitive passwords, since
     * it is not secure.
     *
     * following variables are defined:
     *
     * \@USER\@           user for download area
     * \@PASSWORD\@       password for download area
     * \@KERNELVERSION\@  version of the kernel
     * \@DRBDVERSION\@    version of drbd, that will be installed
     * \@DISTRIBUTION\@   distribution for which the drbd will be installed.
     *
     * @param command
     *          command in which the variables will be replaced
     *
     * @param hidePassword
     *          if set to true all vars will be replaced except of sensitive
     *          ones.
     *
     * @return command with replaced variables
     */
    public String replaceVars(String command, final boolean hidePassword) {
        if (command.contains("@USER@")) {
            command = command.replaceAll("@USER@", application.getDownloadUser());
        }
        if (command.indexOf("@PASSWORD@") > -1) {
            if (hidePassword) {
                command = command.replaceAll("@PASSWORD@", "*****");
            } else {
                command = command.replaceAll("@PASSWORD@", application.getDownloadPassword());
            }
        }
        String supportDir = "support";
        if (application.isStagingDrbd()) {
            supportDir = "support/staging";
        }
        if (kernelVersion != null
            && command.contains("@KERNELVERSIONDIR@")) {
            command = command.replaceAll("@KERNELVERSIONDIR@", kernelVersion);
        }
        if (distributionVersion != null
            && command.contains("@DISTRIBUTION@")) {
            command = command.replaceAll("@DISTRIBUTION@", distributionVersion);
        }
        if (arch != null
            && command.contains("@ARCH@")) {
            command = command.replaceAll("@ARCH@", arch);
        }
        if (command.contains("@SUPPORTDIR@")) {
            command = command.replaceAll("@SUPPORTDIR@", supportDir);
        }
        if (command.contains("@DRBDDIR@")) {
            final String drbdDir = "drbd";
            command = command.replaceAll("@DRBDDIR@", drbdDir);
        }
        if (command.contains("@GUI-HELPER@")) {
            final StringBuilder helperProg = new StringBuilder("/usr/local/bin/lcmc-gui-helper-");
            helperProg.append(Tools.getRelease());
            if (application.isCmdLog()) {
                helperProg.append(' ');
                helperProg.append(LOG_COMMANDS_ON_SERVER_OPTION);
            }
            command = command.replaceAll("@GUI-HELPER@", helperProg.toString());
        }
        if (command.contains("@GUI-HELPER-PROG@")) {
            command = command.replaceAll("@GUI-HELPER-PROG@", "/usr/local/bin/lcmc-gui-helper-" + Tools.getRelease());
        }
        return command;
    }

    /**
     * Replaces variables in command.
     *
     * Following variables are defined:
     *
     * \@USER\@           user for download area
     * \@PASSWORD\@       password for download area
     * \@KERNELVERSION\@  version of the kernel
     * \@DRBDVERSION\@    version of drbd, that will be installed
     * \@DISTRIBUTION\@   distribution for which the drbd will be installed.
     *
     * @param command
     *          command in which the variables will be replaced
     *
     * @return command with replaced variables
     */
    public String replaceVars(final String command) {
        return replaceVars(command, false);
    }

    public void parseHostInfo(final String ans) {
        LOG.debug1("parseHostInfo: updating host info: " + getName());
        final String[] lines = ans.split("\\r?\\n");
        final List<String> versionLines = new ArrayList<String>();
        final Map<String, BlockDevice> newBlockDevices = new LinkedHashMap<String, BlockDevice>();
        final Map<String, BlockDevice> newDrbdBlockDevices = new LinkedHashMap<String, BlockDevice>();
        final List<NetInterface> newNetInterfaces = new ArrayList<NetInterface>();
        final List<Value> newBridges = new ArrayList<Value>();
        final Map<String, Long> newVolumeGroups = new LinkedHashMap<String, Long>();
        final Map<String, Set<String>> newVolumeGroupsLVS = new HashMap<String, Set<String>>();
        final List<BlockDevice> newPhysicalVolumes = new ArrayList<BlockDevice>();
        final Set<String> newFileSystems = new TreeSet<String>();
        final Set<String> newCryptoModules = new TreeSet<String>();
        final Set<Value> newQemuKeymaps = new TreeSet<Value>();
        final Set<Value> newCpuMapModels = new TreeSet<Value>();
        final Set<Value> newCpuMapVendors = new TreeSet<Value>();
        final Set<String> newMountPoints = new TreeSet<String>();

        final Map<String, List<String>> newGuiOptions = new HashMap<String, List<String>>();
        final Set<String> newDrbdResProxy = new HashSet<String>();

        final Collection<String> changedTypes = new HashSet<String>();

        final Map<String, String> diskSpaces = new HashMap<String, String>();

        newMountPoints.add("/mnt/");
        String guiOptionName = null;

        String type = "";
        for (final String line : lines) {
            if (line.indexOf("ERROR:") == 0) {
                break;
            } else if (line.indexOf("WARNING:") == 0) {
                continue;
            }
            if (INFO_TYPES.contains(line)) {
                type = line;
                changedTypes.add(type);
                continue;
            }
            if ("net-info".equals(type)) {
                try {
                    final NetInterface netInterface = new NetInterface(line);
                    if (netInterface.getIp() != null && !"".equals(netInterface.getIp())) {
                        newNetInterfaces.add(netInterface);
                    }
                } catch (final UnknownHostException e) {
                    LOG.appWarning("parseHostInfo: cannot parse: net-info: " + line);
                }
            } else if (BRIDGE_INFO_DELIM.equals(type)) {
                newBridges.add(new StringValue(line));
            } else if ("disk-info".equals(type)) {
                final Optional<BlockDevice> blockDevice = createBlockDevice(line);
                if (!blockDevice.isPresent()) {
                    continue;
                }
                final String bdName = blockDevice.get().getName();
                if (bdName != null) {
                    final Matcher drbdM = DRBD_DEV_FILE_PATTERN.matcher(bdName);
                    if (drbdM.matches()) {
                        if (drbdBlockDevices.containsKey(bdName)) {
                            drbdBlockDevices.get(bdName).updateFrom(blockDevice.get());
                        } else {
                            newDrbdBlockDevices.put(bdName, blockDevice.get());
                        }
                    } else {
                        newBlockDevices.put(bdName, blockDevice.get());
                        if (blockDevice.get().getVolumeGroup() == null
                            && bdName.length() > 5 && bdName.indexOf('/', 5) < 0) {
                            final Matcher m = BLOCK_DEV_FILE_PATTERN.matcher(bdName);
                            if (m.matches()) {
                                newBlockDevices.remove(m.group(1));
                            }
                        }
                    }
                }
                final String vg = blockDevice.get().getVolumeGroup();
                if (vg != null) {
                    Set<String> logicalVolumes = newVolumeGroupsLVS.get(vg);
                    if (logicalVolumes == null) {
                        logicalVolumes = new HashSet<String>();
                        newVolumeGroupsLVS.put(vg, logicalVolumes);
                    }
                    final String lv = blockDevice.get().getLogicalVolume();
                    if (lv != null) {
                        logicalVolumes.add(lv);
                    }
                }
                if (blockDevice.get().isPhysicalVolume()) {
                    newPhysicalVolumes.add(blockDevice.get());
                }
            } else if (DISK_SPACE_DELIM.equals(type)) {
                final Matcher dsM = USED_DISK_SPACE_PATTERN.matcher(line);
                if (dsM.matches()) {
                    final String bdName = dsM.group(1);
                    final String used = dsM.group(2);
                    diskSpaces.put(bdName, used);
                }
            } else if ("vg-info".equals(type)) {
                final String[] vgi = line.split("\\s+");
                if (vgi.length == 2) {
                    newVolumeGroups.put(vgi[0], Long.parseLong(vgi[1]));
                } else {
                    LOG.appWarning("parseHostInfo: could not parse volume info: " + line);
                }
            } else if ("filesystems-info".equals(type)) {
                newFileSystems.add(line);
            } else if ("crypto-info".equals(type)) {
                newCryptoModules.add(line);
            } else if ("qemu-keymaps-info".equals(type)) {
                newQemuKeymaps.add(new StringValue(line));
            } else if ("cpu-map-model-info".equals(type)) {
                newCpuMapModels.add(new StringValue(line));
            } else if ("cpu-map-vendor-info".equals(type)) {
                newCpuMapVendors.add(new StringValue(line));
            } else if ("mount-points-info".equals(type)) {
                newMountPoints.add(line);
            } else if ("gui-info".equals(type)) {
                parseGuiInfo(line);
            } else if ("installation-info".equals(type)) {
                parseInstallationInfo(line);
            } else if ("gui-options-info".equals(type)) {
                guiOptionName = parseGuiOptionsInfo(line, guiOptionName, newGuiOptions);
            } else if ("version-info".equals(type)) {
                versionLines.add(line);
            } else if ("drbd-proxy-info".equals(type)) {
                /* res-other.host-this.host */
                final Cluster cl = getCluster();
                if (cl != null) {
                    String res = null;
                    if (line.startsWith("up:")) {
                        for (final Host otherHost : getCluster().getProxyHosts()) {
                            if (otherHost == this) {
                                continue;
                            }
                            final String hostsPart = '-' + otherHost.getName() + '-' + getName();
                            final int i = line.indexOf(hostsPart);
                            if (i > 0) {
                                res = line.substring(3, i);
                                break;
                            }
                        }
                    }
                    if (res == null) {
                        LOG.appWarning("parseHostInfo: could not parse proxy line: " + line);
                    } else {
                        newDrbdResProxy.add(res);
                    }
                }
            }
        }

        LOG.debug1("parseHostInfo: "
                   + getName()
                   + ", pacemaker: "   + pacemakerVersion
                   + ", corosync: "    + corosyncVersion
                   + ", heartbeat: "   + heartbeatVersion
                   + ", drbd: "        + drbdHost.getDrbdUtilVersion()
                   + ", drbd module: " + drbdHost.getDrbdModuleVersion());

        if (changedTypes.contains(NET_INFO_DELIM)) {
            eventBus.post(new NetInterfacesChangedEvent(this, newNetInterfaces));
        }

        if (changedTypes.contains(BRIDGE_INFO_DELIM)) {
            eventBus.post(new BridgesChangedEvent(this, newBridges));
        }

        if (changedTypes.contains(DISK_INFO_DELIM)) {
            eventBus.post(new BlockDevicesChangedEvent(this, newBlockDevices.values()));
            drbdBlockDevices = newDrbdBlockDevices;
            physicalVolumes = newPhysicalVolumes;
            volumeGroupsWithLvs = newVolumeGroupsLVS;
        }
        if (changedTypes.contains(DISK_SPACE_DELIM)) {
            eventBus.post(new BlockDevicesDiskSpaceEvent(this, diskSpaces));
        }

        if (changedTypes.contains(VG_INFO_DELIM)) {
            volumeGroups = newVolumeGroups;
        }

        if (changedTypes.contains(FILESYSTEMS_INFO_DELIM)) {
            availableFileSystems = newFileSystems;
        }

        if (changedTypes.contains(CRYPTO_INFO_DELIM)) {
            availableCryptoModules = newCryptoModules;
        }

        if (changedTypes.contains(QEMU_KEYMAPS_INFO_DELIM)) {
            availableQemuKeymaps = newQemuKeymaps;
        }

        if (changedTypes.contains(CPU_MAP_MODEL_INFO_DELIM)) {
            availableCpuMapModels = newCpuMapModels;
        }

        if (changedTypes.contains(CPU_MAP_VENDOR_INFO_DELIM)) {
            availableCpuMapVendors = newCpuMapVendors;
        }

        if (changedTypes.contains(MOUNT_POINTS_INFO_DELIM)) {
            mountPoints = newMountPoints;
        }

        if (changedTypes.contains(VERSION_INFO_DELIM)) {
            setDistInfo(versionLines.toArray(new String[versionLines.size()]));
        }

        if (changedTypes.contains(GUI_OPTIONS_INFO_DELIM)) {
            guiOptions = newGuiOptions;
        }

        if (changedTypes.contains(DRBD_PROXY_INFO_DELIM)) {
            drbdResourcesWithProxy = newDrbdResProxy;
        }

        getBrowser().updateHWResources(
                getAvailableFileSystems());
    }

    /** Parses the gui info, with drbd and heartbeat graph positions. */
    private void parseGuiInfo(final String line) {
        final String[] tokens = line.split(";");
        String id = null;
        String x = null;
        String y = null;
        for (final String token : tokens) {
            final String[] r = token.split("=");
            if (r.length == 2) {
                if (r[0].equals("hb") || r[0].equals("dr")) {
                    id = token;
                } else if (r[0].equals("x")) {
                    x = r[1];
                } else if (r[0].equals("y")) {
                    y = r[1];
                }
            }
        }
        if (id != null && x != null && y != null) {
            servicePositions.put(id, new Point2D.Double(new Double(x).doubleValue(), new Double(y).doubleValue()));
        }
    }
    /** Parses the gui options info. */
    public String parseGuiOptionsInfo(final String line,
                                      final String guiOptionName,
                                      final Map<String, List<String>> goptions) {
        if (line.length() > 2 && line.substring(0, 2).equals("o:")) {
            final String op = line.substring(2);
            goptions.put(op, new ArrayList<String>());
            return op;
        }
        if (guiOptionName != null) {
            final List<String> options = goptions.get(guiOptionName);
            if (options != null) {
                options.add(line);
            }
        }
        return guiOptionName;
    }

    public void parseInstallationInfo(final String line) {
        final String[] tokens = line.split(":|\\s+");
        if (tokens.length < 2) {
            return;
        }
        if ("pm".equals(tokens[0])) {
            if (tokens.length == 2) {
                pacemakerVersion = tokens[1].trim();
            } else {
                pacemakerVersion = null;
            }
        } else if ("cs".equals(tokens[0])) {
            if (tokens.length == 2) {
                corosyncVersion = tokens[1].trim();
            } else {
                corosyncVersion = null;
            }
        } else if ("ais".equals(tokens[0])) {
            if (tokens.length == 2) {
                openaisVersion = tokens[1].trim();
            } else {
                openaisVersion = null;
            }
        } else if ("ais-rc".equals(tokens[0])) {
            if (tokens.length == 2) {
                openaisInRc = "on".equals(tokens[1].trim());
            } else {
                openaisInRc = false;
            }
        } else if ("cs-rc".equals(tokens[0])) {
            if (tokens.length == 2) {
                corosyncInRc = "on".equals(tokens[1].trim());
            } else {
                corosyncInRc = false;
            }
        } else if ("cs-ais-conf".equals(tokens[0])) {
            if (tokens.length == 2) {
                corosyncOrOpenaisConfigExists = "on".equals(tokens[1].trim());
            } else {
                corosyncOrOpenaisConfigExists = false;
            }
        } else if ("cs-running".equals(tokens[0])) {
            if (tokens.length == 2) {
                corosyncRunning = "on".equals(tokens[1].trim());
            } else {
                corosyncRunning = false;
            }
        } else if ("ais-running".equals(tokens[0])) {
            if (tokens.length == 2) {
                openaisRunning = "on".equals(tokens[1].trim());
                commLayerStarting = false;
                pacemakerStarting = false;
            } else {
                openaisRunning = false;
            }
        } else if ("cs-init".equals(tokens[0])) {
            if (tokens.length == 2) {
                corosyncHasInitScript = "on".equals(tokens[1].trim());
            } else {
                corosyncHasInitScript = false;
            }
        } else if ("ais-init".equals(tokens[0])) {
            if (tokens.length == 2) {
                openaisHasInitScript = "on".equals(tokens[1].trim());
            } else {
                openaisHasInitScript = false;
            }
        } else if ("hb".equals(tokens[0])) {
            if (tokens.length == 2) {
                heartbeatVersion = tokens[1].trim();
            } else {
                heartbeatVersion = null;
            }
        } else if ("hb-init".equals(tokens[0])) {
            if (tokens.length == 2) {
                heartbeatHasInitScript = "on".equals(tokens[1].trim());
            } else {
                heartbeatHasInitScript = false;
            }
        } else if ("hb-rc".equals(tokens[0])) {
            if (tokens.length == 2) {
                heartbeatInRc = "on".equals(tokens[1].trim());
            } else {
                heartbeatInRc = false;
            }
        } else if ("hb-conf".equals(tokens[0])) {
            if (tokens.length == 2) {
                heartbeatConfigExists = "on".equals(tokens[1].trim());
            } else {
                heartbeatConfigExists = false;
            }
        } else if ("hb-running".equals(tokens[0])) {
            if (tokens.length == 2) {
                heartbeatRunning = "on".equals(tokens[1].trim());
            } else {
                heartbeatRunning = false;
            }
        } else if ("pcmk-rc".equals(tokens[0])) {
            if (tokens.length == 2) {
                pacemakerInRc = "on".equals(tokens[1].trim());
            } else {
                pacemakerInRc = false;
            }
        } else if ("pcmk-running".equals(tokens[0])) {
            if (tokens.length == 2) {
                pacemakerRunning = "on".equals(tokens[1].trim());
            } else {
                pacemakerRunning = false;
            }
        } else if ("drbdp-running".equals(tokens[0])) {
            if (tokens.length == 2) {
                drbdHost.setDrbdProxyRunning("on".equals(tokens[1].trim()));
            } else {
                drbdHost.setDrbdProxyRunning(false);
            }
        } else if ("pcmk-init".equals(tokens[0])) {
            if (tokens.length == 2) {
                pacemakerHasInitScript = "on".equals(tokens[1].trim());
            } else {
                pacemakerHasInitScript = false;
            }
        } else if ("pcmk-svc-ver".equals(tokens[0])) {
            if (tokens.length == 2) {
                try {
                    pcmkServiceVersion = Integer.parseInt(tokens[1].trim());
                } catch (final NumberFormatException e) {
                    pcmkServiceVersion = -1;
                }
            }
        } else if ("drbd-loaded".equals(tokens[0])) {
            if (tokens.length == 2) {
                drbdHost.setDrbdLoaded("on".equals(tokens[1].trim()));
            } else {
                drbdHost.setDrbdLoaded(false);
            }
        } else if ("hb-lib-path".equals(tokens[0])) {
            if (tokens.length == 2) {
                heartbeatLibPath = tokens[1].trim();
            } else {
                heartbeatLibPath = null;
            }
        } else if ("hn".equals(tokens[0])) {
            if (tokens.length == 2) {
                hostname = tokens[1].trim();
            } else {
                hostname = null;
            }
            setName(hostname);
        } else if ("drbd".equals(tokens[0])) {
            if (tokens.length == 2) {
                drbdHost.setDrbdUtilVersion(tokens[1].trim());
            } else {
                drbdHost.setDrbdUtilVersion(null);
            }
        } else if ("drbd-mod".equals(tokens[0])) {
            if (tokens.length == 2) {
                drbdHost.setDrbdModuleVersion(tokens[1].trim());
            } else {
                drbdHost.setDrbdModuleVersion(null);
            }
        }
        corosyncOrHeartbeatRunning = heartbeatRunning || corosyncRunning || openaisRunning;
        if (commLayerStarting && (corosyncRunning || openaisRunning || heartbeatRunning)) {
            commLayerStarting = false;
        }
        if (pacemakerStarting && pacemakerRunning) {
            pacemakerStarting = false;
        }
        if (drbdProxyStarting && drbdHost.isDrbdProxyRunning()) {
            drbdProxyStarting = false;
        }
        if (commLayerStopping && !corosyncRunning && !openaisRunning && !heartbeatRunning) {
            commLayerStopping = false;
        }
    }

    public Point2D getGraphPosition(final String id) {
        return servicePositions.get(id);
    }

    public void resetGraphPosition(final String id) {
        servicePositions.remove(id);
    }

    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);
    }

    public void setHeartbeatVersion(final String heartbeatVersion) {
        this.heartbeatVersion = heartbeatVersion;
    }

    public void setCorosyncVersion(final String corosyncVersion) {
        this.corosyncVersion = corosyncVersion;
    }

    public void setPacemakerVersion(final String pacemakerVersion) {
        this.pacemakerVersion = pacemakerVersion;
    }

    public void setOpenaisVersion(final String openaisVersion) {
        this.openaisVersion = openaisVersion;
    }

    public String getPacemakerVersion() {
        return pacemakerVersion;
    }

    public String getCorosyncVersion() {
        return corosyncVersion;
    }

    public boolean isCorosyncInstalled() {
        return corosyncVersion != null;
    }

    public boolean isOpenaisWrapper() {
        return "wrapper".equals(openaisVersion);
    }


    public String getOpenaisVersion() {
        return openaisVersion;
    }

    public String getHeartbeatVersion() {
        return heartbeatVersion;
    }

    /**
     * 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().
     */
    void setLoadingError() {
        isLoadingGate.countDown();
    }

    /** Waits for the server status latch. */
    public void waitForServerStatusLatch() {
        try {
            waitForServerStatusLatch.await();
        } catch (final InterruptedException ignored) {
            Thread.currentThread().interrupt();
        }
    }

    /** The latch is set when the server status is run for the first time. */
    public void serverStatusLatchDone() {
        waitForServerStatusLatch.countDown();
    }

    /** Returns true if latch is set. */
    public boolean getWaitForServerStatusLatch() {
        return waitForServerStatusLatch.getCount() == 1;
    }

    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;
    }

    public boolean isCorosyncInRc() {
       return corosyncInRc;
    }

    public boolean isOpenaisInRc() {
       return openaisInRc;
    }

    public boolean isPacemakerInRc() {
       return pacemakerInRc;
    }

    public boolean hasHeartbeatInitScript() {
       return heartbeatHasInitScript;
    }

    public boolean hasCorosyncInitScript() {
       return corosyncHasInitScript;
    }

    public boolean hasOpenaisInitScript() {
       return openaisHasInitScript;
    }

    public boolean hasPacemakerInitScript() {
       return pacemakerHasInitScript;
    }

    public boolean isCorosyncRunning() {
       return corosyncRunning;
    }

    public boolean isPacemakerRunning() {
       return pacemakerRunning;
    }

    public boolean isOpenaisRunning() {
       return openaisRunning;
    }

    public boolean corosyncOrOpenaisConfigExists() {
       return corosyncOrOpenaisConfigExists;
    }

    public boolean isHeartbeatInRc() {
       return heartbeatInRc;
    }

    public boolean isHeartbeatRunning() {
       return heartbeatRunning;
    }

    public boolean heartbeatConfigExists() {
       return heartbeatConfigExists;
    }

    /** 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(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 (!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 long getFreeInVolumeGroup(final String volumeGroup) {
        final Long f = volumeGroups.get(volumeGroup);
        if (f == null) {
            return 0;
        }
        return f;
    }

    public Set<String> getVolumeGroupNames() {
        return volumeGroups.keySet();
    }

    public Boolean getCorosyncOrHeartbeatRunning() {
        return corosyncOrHeartbeatRunning;
    }

    public void setCorosyncOrHeartbeatRunning(final Boolean corosyncOrHeartbeatRunning) {
        this.corosyncOrHeartbeatRunning = corosyncOrHeartbeatRunning;
    }

    public boolean isCommLayerStopping() {
        return commLayerStopping;
    }

    public void setCommLayerStopping(final boolean commLayerStopping) {
        this.commLayerStopping = commLayerStopping;
    }

    public boolean isCommLayerStarting() {
        return commLayerStarting;
    }

    public void setCommLayerStarting(final boolean commLayerStarting) {
        this.commLayerStarting = commLayerStarting;
    }

    public boolean isPacemakerStarting() {
        return pacemakerStarting;
    }

    public void setPacemakerStarting(final boolean pacemakerStarting) {
        this.pacemakerStarting = pacemakerStarting;
    }

    public boolean isDrbdProxyStarting() {
        return drbdProxyStarting;
    }

    public void setDrbdProxyStarting(final boolean drbdProxyStarting) {
        this.drbdProxyStarting = drbdProxyStarting;
    }

    public boolean isPcmkStartedByCorosync() {
        return pcmkServiceVersion == 0;
    }

    public void setLibvirtVersion(final String libvirtVersion) {
        this.libvirtVersion = libvirtVersion;
    }

    public String getLibvirtVersion() {
        return libvirtVersion;
    }

    public Set<String> getLogicalVolumesFromVolumeGroup(final String vg) {
        return volumeGroupsWithLvs.get(vg);
    }

    public Set<String> getAllLogicalVolumes() {
        final Set<String> allLVS = new LinkedHashSet<String>();
        for (final String vg : volumeGroups.keySet()) {
            final Set<String> lvs = volumeGroupsWithLvs.get(vg);
            if (lvs != null) {
                allLVS.addAll(lvs);
            }
        }
        return allLVS;
    }

    /** Returns whether DRBD has volume feature. */
    public boolean hasVolumes() {
        try {
            return Tools.compareVersions(drbdHost.getDrbdUtilVersion(), "8.4") >= 0;
        } catch (final Exceptions.IllegalVersionException e) {
            LOG.appWarning("hasVolumes: " + e.getMessage(), e);
        }
        return true;
    }

    public Iterable<BlockDevice> getPhysicalVolumes() {
        return physicalVolumes;
    }

    public void setSavable(final boolean savable) {
        this.savable = savable;
    }

    public boolean isSavable() {
        return savable;
    }

    public BlockDevice getDrbdBlockDevice(final String device) {
        return drbdBlockDevices.get(device);
    }

    public Iterable<BlockDevice> getDrbdBlockDevices() {
        return drbdBlockDevices.values();
    }

    public Iterable<BlockDevice> getPhysicalVolumes(final String vg) {
        final Collection<BlockDevice> bds = new ArrayList<BlockDevice>();
        if (vg == null) {
            return bds;
        }
        for (final BlockDevice b : physicalVolumes) {
            if (vg.equals(b.getVgOnPhysicalVolume())) {
                bds.add(b);
            }
        }
        return bds;
    }

    public boolean drbdStatusTryLock() {
        return mDRBDStatusLock.tryLock();
    }

    public void drbdStatusLock() {
        mDRBDStatusLock.lock();
    }

    public void drbdStatusUnlock() {
        mDRBDStatusLock.unlock();
    }

    public void vmStatusLock() {
        mUpdateVMSlock.lock();
    }

    public boolean vmStatusTryLock() {
        return mUpdateVMSlock.tryLock();
    }

    public void vmStatusUnlock() {
        mUpdateVMSlock.unlock();
    }

    public boolean isRoot() {
        return ROOT_USER.equals(username);
    }

    public Iterable<String> getGuiOptions(final String name) {
        final List<String> opts = guiOptions.get(name);
        if (opts == null) {
            return new ArrayList<String>();
        }
        return new ArrayList<String>(guiOptions.get(name));
    }

    public boolean isDrbdProxyUp(final String drbdResource) {
        return drbdResourcesWithProxy.contains(drbdResource);
    }

    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 void setDrbdStatusOk(final boolean drbdStatusOk) {
        this.drbdStatusOk = drbdStatusOk;
        eventBus.post(new DrbdStatusChangedEvent(this, drbdStatusOk));
    }

    public boolean isDrbdStatusOk() {
        return drbdStatusOk;
    }

    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);
    }

    private Optional<BlockDevice> createBlockDevice(final String line) {
        final Pattern p = Pattern.compile("([^:]+):(.*)");
        final String[] cols = line.split(" ");
        if (cols.length < 2) {
            LOG.appWarning("update: cannot parse block device line: " + line);
            return Optional.absent();
        } else {
            final Collection<String> diskIds = new HashSet<String>();
            final String device = cols[0];
            final Map<String, String> tokens = new HashMap<String, String>();
            for (int i = 1; i < cols.length; i++) {
                final Matcher m = p.matcher(cols[i]);
                if (m.matches()) {
                    if (TOKEN_DISK_ID.equals(m.group(1))) {
                        diskIds.add(m.group(2));
                    } else {
                        tokens.put(m.group(1), m.group(2));
                    }
                } else {
                    LOG.appWarning("update: could not parse: " + line);
                }
            }
            final BlockDevice blockDevice = new BlockDevice(this, device);
            blockDevice.setDiskUuid(tokens.get(TOKEN_UUID));
            blockDevice.setBlockSize(tokens.get(TOKEN_SIZE));
            blockDevice.setMountedOn(tokens.get(TOKEN_MP));
            blockDevice.setFsType(tokens.get(TOKEN_FS));
            blockDevice.setVolumeGroup(tokens.get(TOKEN_VG));
            blockDevice.setLogicalVolume(tokens.get(TOKEN_LV));
            blockDevice.setVgOnPhysicalVolume(tokens.get(TOKEN_PV));
            blockDevice.setDiskIds(diskIds);
            return Optional.of(blockDevice);
        }
    }
}
TOP

Related Classes of lcmc.host.domain.Host

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.