Package lcmc.drbd.domain

Source Code of lcmc.drbd.domain.DrbdXml

/*
* 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.drbd.domain;

import java.math.BigInteger;
import java.util.ArrayList;
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.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import lcmc.Exceptions;
import lcmc.common.domain.AccessMode;
import lcmc.common.ui.GUIData;
import lcmc.drbd.ui.DrbdGraph;
import lcmc.drbd.ui.resource.BlockDevInfo;
import lcmc.drbd.ui.resource.ProxyNetInfo;
import lcmc.common.domain.Application;
import lcmc.host.domain.Host;
import lcmc.common.domain.StringValue;
import lcmc.common.domain.Value;
import lcmc.common.domain.XML;
import lcmc.common.domain.ConvertCmdCallback;
import lcmc.logger.Logger;
import lcmc.logger.LoggerFactory;
import lcmc.common.domain.util.Tools;
import lcmc.common.domain.Unit;
import lcmc.cluster.service.ssh.ExecCommandConfig;
import lcmc.cluster.service.ssh.SshOutput;

import org.apache.commons.collections15.keyvalue.MultiKey;
import org.apache.commons.collections15.map.LinkedMap;
import org.apache.commons.collections15.map.MultiKeyMap;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.inject.Inject;
import javax.inject.Named;

/**
* This class parses xml from drbdsetup and drbdadm, stores the
* information in the hashes and provides methods to get this
* information.
* The xml is obtained with drbdsetp xml command and drbdadm dump-xml.
*/
@Named
public class DrbdXml extends XML {
    private static final Logger LOG = LoggerFactory.getLogger(DrbdXml.class);
    public static final String[] EMPTY_STRING = new String[0];

    private static final Pattern UNIT_PATTERN = Pattern.compile("(\\d*)([kmgtsKMGTS]?)");
    public static final String GLOBAL_SECTION = "global";
    public static final Value PROTOCOL_A = new StringValue("A", "A / Asynchronous");
    public static final Value PROTOCOL_B = new StringValue("B", "B / Semi-Synchronous");
    public static final Value PROTOCOL_C = new StringValue("C", "C / Synchronous");
    public static final String PROTOCOL_PARAM = "protocol";
    public static final String PING_TIMEOUT_PARAM = "ping-timeout";

    private static final BigInteger KILO = new BigInteger("1024");
    static final Value[] PROTOCOLS = {PROTOCOL_A, PROTOCOL_B, PROTOCOL_C};
    static final Collection<String> NOT_ADVANCED_PARAMS = new ArrayList<String>();

    static final Collection<String> IGNORE_CONFIG_ERRORS = new HashSet<String>();
    static final Map<String, AccessMode.Type> PARAM_ACCESS_TYPE = new HashMap<String, AccessMode.Type>();

    private static final Map<String, Value> PREFERRED_VALUES_MAP = new HashMap<String, Value>();

    /** Yes / true drbd config value. */
    public static final Value CONFIG_YES = new StringValue("yes");
    /** No / false drbd config value. */
    public static final Value CONFIG_NO = new StringValue("no");
    /** Hardcoded defaults, for options that have it but we don't get
        it from the drbdsetup. */
    static final Map<String, Value> HARDCODED_DEFAULTS = new HashMap<String, Value>();

    @Inject
    private Application application;

    static {
        NOT_ADVANCED_PARAMS.add("rate");
        NOT_ADVANCED_PARAMS.add(PROTOCOL_PARAM);
        NOT_ADVANCED_PARAMS.add(PING_TIMEOUT_PARAM);
        NOT_ADVANCED_PARAMS.add("fence-peer");
        NOT_ADVANCED_PARAMS.add("wfc-timeout");
        NOT_ADVANCED_PARAMS.add("degr-wfc-timeout");
        NOT_ADVANCED_PARAMS.add("become-primary-on");
        NOT_ADVANCED_PARAMS.add("timeout");
        NOT_ADVANCED_PARAMS.add("allow-two-primaries");
        NOT_ADVANCED_PARAMS.add("fencing");
        NOT_ADVANCED_PARAMS.add("after"); /* before 8.4 */
        NOT_ADVANCED_PARAMS.add("resync-after");
        NOT_ADVANCED_PARAMS.add("usage-count"); /* global */
        NOT_ADVANCED_PARAMS.add("memlimit"); /* proxy */
        NOT_ADVANCED_PARAMS.add("plugin-zlib"); /* proxy */
        NOT_ADVANCED_PARAMS.add("plugin-lzma"); /* proxy */
    }
    static {
        IGNORE_CONFIG_ERRORS.add("no resources defined!");
        IGNORE_CONFIG_ERRORS.add(
            "Can not open '/etc/drbd.conf': No such file or directory");
    }
    static {
        PARAM_ACCESS_TYPE.put("rate", AccessMode.OP);
    }
    static {
        PREFERRED_VALUES_MAP.put(PROTOCOL_PARAM, PROTOCOL_C);
    }
    static {
        HARDCODED_DEFAULTS.put("usage-count", new StringValue());
        HARDCODED_DEFAULTS.put("disable-ip-verification", CONFIG_NO);
       
        HARDCODED_DEFAULTS.put(PROTOCOL_PARAM, PROTOCOL_C);
        HARDCODED_DEFAULTS.put("after-sb-0pri", new StringValue("disconnect"));
        HARDCODED_DEFAULTS.put("after-sb-1pri", new StringValue("disconnect"));
        HARDCODED_DEFAULTS.put("after-sb-2pri", new StringValue("disconnect"));
        HARDCODED_DEFAULTS.put("rr-conflict", new StringValue("disconnect"));
        HARDCODED_DEFAULTS.put("on-io-error", new StringValue("pass_on"));
        HARDCODED_DEFAULTS.put("fencing", new StringValue("dont-care"));
        HARDCODED_DEFAULTS.put("on-no-data-accessible", new StringValue("io-error"));
        HARDCODED_DEFAULTS.put("on-congestion", new StringValue("block"));
    }

    public static Unit getUnitBytes(final String unitPart) {
        return new Unit("", "", "Byte" + unitPart, "Bytes" + unitPart);
    }

    public static Unit getUnitKiBytes(final String unitPart) {
        return new Unit("K", "k", "KiByte" + unitPart, "KiBytes" + unitPart);
    }

    public static Unit getUnitMiBytes(final String unitPart) {
        return new Unit("M", "m", "MiByte" + unitPart, "MiBytes" + unitPart);
    }

    public static Unit getUnitGiBytes(final String unitPart) {
        return new Unit("G", "g", "GiByte" + unitPart, "GiBytes" + unitPart);
    }

    public static Unit getUnitTiBytes(final String unitPart) {
        return new Unit("T", "t", "TiByte" + unitPart, "TiBytes" + unitPart);
    }

    public static Unit getUnitPiBytes(final String unitPart) {
        return new Unit("P", "p", "PiByte" + unitPart, "PiBytes" + unitPart);
    }

    public static Unit getUnitSectors(final String unitPart) {
        return new Unit("s", "s", "Sector" + unitPart, "Sectors" + unitPart);
    }

    /** Returns units. */
    public Unit[] getUnits(final String param, final String unitPart) {
        final List<Unit> units = new ArrayList<Unit>();
        if ("k".equalsIgnoreCase(paramDefaultUnitMap.get(param))) {
            units.add(getUnitKi(unitPart));
            units.add(getUnitMi(unitPart));
            units.add(getUnitGi(unitPart));
        } else if ("m".equalsIgnoreCase(paramDefaultUnitMap.get(param))) {
            units.add(getUnitMi(unitPart));
            units.add(getUnitGi(unitPart));
        } else if ("g".equalsIgnoreCase(paramDefaultUnitMap.get(param))) {
            units.add(getUnitGi(unitPart));
        } else {
            units.add(getUnitDefault(unitPart));
            units.add(getUnitKi(unitPart));
            units.add(getUnitMi(unitPart));
            units.add(getUnitGi(unitPart));
        }
        return units.toArray(new Unit[units.size()]);
    }

    public Unit[] getByteUnits(final String param, final String unitPart) {
        final List<Unit> units = new ArrayList<Unit>();
        if ("1".equalsIgnoreCase(paramDefaultUnitMap.get(param))) {
            units.add(getUnitBytes(unitPart));
            units.add(getUnitKiBytes(unitPart));
            units.add(getUnitMiBytes(unitPart));
            units.add(getUnitGiBytes(unitPart));
            units.add(getUnitSectors(unitPart));
        } else if ("s".equalsIgnoreCase(paramDefaultUnitMap.get(param))) {
            units.add(getUnitSectors(unitPart));
            units.add(getUnitBytes(unitPart));
            units.add(getUnitKiBytes(unitPart));
            units.add(getUnitMiBytes(unitPart));
            units.add(getUnitGiBytes(unitPart));
        } else if ("k".equalsIgnoreCase(paramDefaultUnitMap.get(param))) {
            units.add(getUnitKiBytes(unitPart));
            units.add(getUnitMiBytes(unitPart));
            units.add(getUnitGiBytes(unitPart));
            units.add(getUnitSectors(unitPart));
        } else if ("m".equalsIgnoreCase(paramDefaultUnitMap.get(param))) {
            units.add(getUnitMiBytes(unitPart));
            units.add(getUnitGiBytes(unitPart));
            units.add(getUnitSectors(unitPart));
        } else if ("g".equalsIgnoreCase(paramDefaultUnitMap.get(param))) {
            units.add(getUnitGiBytes(unitPart));
            units.add(getUnitSectors(unitPart));
        }
        return units.toArray(new Unit[units.size()]);
    }

    public static Unit getUnitDefault(final String unitPart) {
        return new Unit("", "", "", "");
    }

    public static Unit getUnitKi(final String unitPart) {
        return new Unit("k", "K", "k", "k");
    }
                   
    public static Unit getUnitMi(final String unitPart) {
        return new Unit("m", "M", "m", "m");
    }

    public static Unit getUnitGi(final String unitPart) {
        return new Unit("g", "G", "g", "g");
    }

    public static Unit getUnitTi(final String unitPart) {
        return new Unit("t", "T", "t", "t");
    }

    public static Unit getUnitPi(final String unitPart) {
        return new Unit("p", "P", "p", "p");
    }

    /** Return part after '/' from the unit long description. */
    public static String getUnitPart(final String unitLong) {
        if (unitLong == null) {
            return "";
        }

        final int index = unitLong.indexOf('/');
        String unitPart = "";
        if (index > -1) {
            unitPart = unitLong.substring(index);
        }
        return unitPart;
    }
    // TODO: should that not be per host?
    /** Map from parameter name to the default value. */
    private final Map<String, Value> paramDefaultMap = new HashMap<String, Value>();
    /** Map from parameter name to its type. */
    private final Map<String, String> paramTypeMap = new HashMap<String, String>();
    /** Map from parameter name to its section. */
    private final Map<String, String> paramSectionMap = new LinkedHashMap<String, String>();
    /** Map from section to this section's parameters. */
    private final Map<String, List<String>> sectionParamsMap = new LinkedHashMap<String, List<String>>();
    /** Map from parameter name to its unit name (long). */
    private final Map<String, String> paramUnitLongMap = new LinkedHashMap<String, String>();

    /** Map from parameter name to its unit name. */
    private final Map<String, String> paramDefaultUnitMap = new LinkedHashMap<String, String>();

    /** Map from parameter name to its long description. */
    private final Map<String, String> paramLongDescMap = new HashMap<String, String>();

    /** Map from parameter name to its minimum value. */
    private final Map<String, BigInteger> paramMinMap = new LinkedHashMap<String, BigInteger>();

    /** Map from parameter name to its maximum value. */
    private final Map<String, BigInteger> paramMaxMap = new LinkedHashMap<String, BigInteger>();
    /** Map from parameter name to its correct value. */
    private final Map<String, Boolean> paramCorrectValueMap = new HashMap<String, Boolean>();

    /** Map from parameter name to its items if there is a choice list. */
    private final Map<String, List<Value>> paramItemsMap = new LinkedHashMap<String, List<Value>>();
    /** List of all parameters. */
    private final Collection<String> parametersList = new ArrayList<String>();
    /** List of all global parameters. */
    private final Collection<String> globalParametersList = new ArrayList<String>();
    /** List of all required parameters. */
    private final Collection<String> requiredParametersList = new ArrayList<String>();
    /** Map from resource option to the value. */
    private final Map<String, Map<String, Value>> optionsMap = new HashMap<String, Map<String, Value>>();

    /** List with drbd resources. */
    private final List<String> resourceList = new ArrayList<String>();
    /** Map from drbd resource name to the drbd device. */
    private final MultiKeyMap<String, String> resourceDeviceMap =
                                                    MultiKeyMap.decorate(new LinkedMap<MultiKey<String>, String>());
    /** Map from drbd device to the drbd resource name. */
    private final Map<String, String> deviceResourceMap = new HashMap<String, String>();
    /** Map from drbd device to the drbd volume. */
    private final Map<String, String> deviceVolumeMap = new HashMap<String, String>();

    /** Map from drbd resource name and the host to the block device. */
    private final MultiKeyMap<String, Map<String, String>> resourceHostDiskMap =
                                                                    new MultiKeyMap<String, Map<String, String>>();
    /** Map from drbd resource name and the host to the ip. */
    private final Map<String, Map<String, String>> resourceHostIpMap = new HashMap<String, Map<String, String>>();
    /** Map from drbd resource name and the host to the port. */
    private final Map<String, Map<String, String>> resourceHostPortMap = new HashMap<String, Map<String, String>>();
    /** Map from drbd resource name and the host to the family. */
    private final Map<String, Map<String, String>> resourceHostFamilyMap = new HashMap<String, Map<String, String>>();
    /** Map from drbd resource name and the host to the meta disk. */
    private final MultiKeyMap<String, Map<String, String>> resourceHostMetaDiskMap =
                                                                    new MultiKeyMap<String, Map<String, String>>();
    /** Map from drbd resource name and the host to the meta disk index. */
    private final MultiKeyMap<String, Map<String, String>> resourceHostMetaDiskIndexMap =
                                                                    new MultiKeyMap<String, Map<String, String>>();
    /** Map from resource and host to the proxy information. */
    private final MultiKeyMap<String, HostProxy> resourceHostProxyMap = new MultiKeyMap<String, HostProxy>();
    /** Set of all proxy hosts. */
    private final Collection<String> proxyHostNames = new LinkedHashSet<String>();
    /** Map from host to the boolean value if drbd is loaded on this host. */
    private final Map<String, Boolean> hostDrbdLoadedMap = new HashMap<String, Boolean>();
    private boolean unknownSections = false;
    private String oldConfig = null;
    @Inject
    private GUIData guiData;

    public void init(final Host[] hosts, final Map<Host, String> drbdParameters) {
        addSpecialParameter("resource", "name", true);
        for (final Host host : hosts) {
            final String output;
            if (drbdParameters.get(host) == null) {
                output = updateDrbdParameters(host);
                drbdParameters.put(host, output);
                if (output == null) {
                    return;
                }
            } else {
                output = drbdParameters.get(host);
            }
            /* TODO: move this part somewhere else, it should be called
               once per invocation or interactively. (drbd-get-xml) */
            parseDrbdParameters(host, output, hosts);
        }
    }

    public final String updateDrbdParameters(final Host host) {
        final String command = host.getDistCommand("Drbd.getParameters", (ConvertCmdCallback) null);
        final SshOutput ret = host.captureCommand(new ExecCommandConfig().command(command)
                                                                         .silentCommand()
                                                                         .silentOutput());
        if (ret.getExitCode() != 0) {
            return null;
        }
        return ret.getOutput();
    }

    public final void parseDrbdParameters(final Host host, final String output, final Host[] hosts) {
        final String[] lines = output.split("\\r?\\n");
        final Pattern bp = Pattern.compile("^<command name=\"(.*?)\".*");
        final Pattern ep = Pattern.compile("^</command>$");
        final StringBuilder xml = new StringBuilder();
        String section = null;

        for (final String line : lines) {
            final Matcher m = bp.matcher(line);
            if (m.matches()) {
                section = m.group(1);
            }
            if (section != null) {
                xml.append(line);
                xml.append('\n');
                final Matcher m2 = ep.matcher(line);
                if (m2.matches()) {
                    parseSection(section, xml.toString(), host, hosts);
                    section = null;
                    xml.delete(0, xml.length());
                }
            }
        }
        if (!parametersList.contains(PROTOCOL_PARAM)) {
            /* prior 8.4 */
            addParameter("resource", PROTOCOL_PARAM, new StringValue(), PROTOCOLS, true);
        }
        if (paramItemsMap.containsKey("after-sb-0pri")) {
            for (final Host h : hosts) {
                paramItemsMap.get("after-sb-0pri").add(new StringValue("discard-node-" + h.getName()));
            }
        }
    }

    /** Returns config from server. */
    public String getConfig(final Host host) {
        if (!host.isConnected()) {
            return null;
        }
        final String command2 = host.getDistCommand("Drbd.getConfig", (ConvertCmdCallback) null);
        final SshOutput ret = host.captureCommand(new ExecCommandConfig().command(command2)
                                                                         .silentCommand()
                                                                         .silentOutput());
        if (ret.getExitCode() == 0) {
            final StringBuffer confSB = new StringBuffer(ret.getOutput());
            return host.getOutput("drbd", confSB);
        }
        return null;
    }

    /**
     * Retrieves and updates all the data. This should be called if there is
     * a drbd event.
     */
    public void update(final String configString) {
        if (configString != null && !configString.isEmpty()) {
            oldConfig = configString;
            parseConfig(configString);
        }
    }

    /** Returns all drbd parameters. */
    public Iterable<String> getParameters() {
        return parametersList;
    }

    public String getParamShortDesc(final String param) {
        final StringBuilder name = new StringBuilder(param.replaceAll("\\-", " "));
        name.replace(0, 1, name.substring(0, 1).toUpperCase());
        if (paramUnitLongMap.containsKey(param)) {
            name.append(" (").append(paramUnitLongMap.get(param)).append(')');
        }
        return name.toString();
    }

    public String getParamLongDesc(final String param) {
        return paramLongDescMap.get(param);
    }

    public String getUnitLong(final String param) {
        return paramUnitLongMap.get(param);
    }

    public String getDefaultUnit(final String param) {
        return paramDefaultUnitMap.get(param);
    }

    public boolean hasUnitPrefix(final String param) {
        final String unit = paramUnitLongMap.get(param);
        return paramDefaultUnitMap.containsKey(param)
               && (unit == null || "bytes".equals(unit) || "bytes/second".equals(unit));
    }

    /**
     * Returns parameter type, that is one of the following:
     *  numeric
     *  string
     *  boolean
     *  handle
     * .
     */
    public String getParamType(final String param) {
        return paramTypeMap.get(param);
    }

    public Value getParamDefault(final String param) {
        return paramDefaultMap.get(param);
    }

    public Value getParamPreferred(final String param) {
        return PREFERRED_VALUES_MAP.get(param);
    }

    public String getSection(final String param) {
        return paramSectionMap.get(param);
    }

    /**
     * Checks parameter according to its type. Returns false if value is wrong.
     */
    public boolean checkParam(final String param, final Value rawValue) {
        final String type = paramTypeMap.get(param);
        boolean correctValue = true;

        if (rawValue == null || rawValue.isNothingSelected()) {
            correctValue = !isRequired(param);
        } else if ("boolean".equals(type)) {
            if (!rawValue.equals(CONFIG_YES)
                && !rawValue.equals(CONFIG_NO)) {
                correctValue = false;
            }
        } else if ("numeric".equals(type)) {
            final Pattern p = Pattern.compile("(-?\\d+)|\\d*");
            final Matcher m = p.matcher(rawValue.getValueForConfig());
            if (!m.matches()) {
                correctValue = false;
            } else if (rawValue.getUnit() == null || !"s".equalsIgnoreCase(rawValue.getUnit().getShortName())) {
                //TODO: skipping check for sectors, the size of sector
                // must be determined first.
                final BigInteger value = convertToUnit(param, rawValue);

                final BigInteger min = paramMinMap.get(param);
                final BigInteger max = paramMaxMap.get(param);

                if (min != null && value.compareTo(min) < 0) {
                    correctValue = false;
                }

                if (max != null && value.compareTo(max) > 0) {
                    correctValue = false;
                }
            }
        } else {
            correctValue = true;
        }
        paramCorrectValueMap.put(param, correctValue);
        return correctValue;
    }

    public boolean isInteger(final String param) {
        final String type = paramTypeMap.get(param);
        return "numeric".equals(type);
    }

    public boolean isLabel(final String param) {
        return false;
    }

    public boolean isStringType(final String param) {
        final String type = paramTypeMap.get(param);
        return "string".equals(type);
    }

    /**
     * Adds parameter to the specified section. This parameter will be not
     * used in the generated config.
     */
    private void addSpecialParameter(final String section, final String param, final boolean required) {
        if (!parametersList.contains(param)) {
            parametersList.add(param);
            if (required) {
                requiredParametersList.add(param);
            }

            paramTypeMap.put(param, "string");
            paramSectionMap.put(param, section);
        }
    }

    /** Add parameter with choice combo box. */
    private void addParameter(final String section,
                              final String param,
                              final Value defaultValue,
                              final Value[] items,
                              final boolean required) {
        addParameter(section, param, defaultValue, required);
        final List<Value> l = new ArrayList<Value>();
        for (final Value item : items) {
            if (!l.contains(item)) {
                l.add(item);
            }
        }
        paramItemsMap.put(param, l);
        paramTypeMap.put(param, "handler");
    }

    private void addParameter(final String section, final String param, final boolean required) {
        addSpecialParameter(section, param, required);
        sectionParamsMap.put(section, new ArrayList<String>());
        sectionParamsMap.get(section).add(param);
    }

    private void addParameter(final String section,
                              final String param,
                              final Value defaultValue,
                              final boolean required) {
        addParameter(section, param, required);
        paramDefaultMap.put(param, defaultValue);
    }

    public String[] getSections() {
        return sectionParamsMap.keySet().toArray(new String[sectionParamsMap.size()]);
    }

    public String[] getSectionParams(final String section) {
        final List<String> params = sectionParamsMap.get(section);
        if (params == null) {
            return EMPTY_STRING;
        }
        return params.toArray(new String[params.size()]);
    }

    public Iterable<String> getGlobalParams() {
        return globalParametersList;
    }

    public Value[] getPossibleChoices(final String param) {
        final List<Value> items = paramItemsMap.get(param);
        if (items == null) {
            return null;
        } else {
            return items.toArray(new Value[items.size()]);
        }
    }

    public boolean isRequired(final String param) {
        return requiredParametersList.contains(param);
    }

    public boolean isAdvanced(final String param) {
        return !isRequired(param) && !NOT_ADVANCED_PARAMS.contains(param);
    }

    public AccessMode.Type getAccessType(final String param) {
        final AccessMode.Type at = PARAM_ACCESS_TYPE.get(param);
        if (at == null) {
          return AccessMode.Type.ADMIN;
        }
        return at;
    }

    private void parseConfigGlobalNode(final Node globalNode, final Map<String, Value> nameValueMap) {
        final NodeList options = globalNode.getChildNodes();
        for (int i = 0; i < options.getLength(); i++) {
            final Node option = options.item(i);
            final String nodeName = option.getNodeName();
            if ("dialog-refresh".equals(nodeName)) {
                final String dialogRefresh = getAttribute(option, "refresh");
                nameValueMap.put(nodeName, new StringValue(dialogRefresh));
            } else if ("minor-count".equals(nodeName)) {
                final String minorCount = getAttribute(option, "count");
                nameValueMap.put(nodeName, new StringValue(minorCount));
            } else if ("disable-ip-verification".equals(nodeName)) {
                nameValueMap.put(nodeName, CONFIG_YES);
            } else if ("usage-count".equals(nodeName)) {
                /* does not come from drbdadm */
                /* TODO: "count" is guessed. */
                final String usageCount = getAttribute(option, "count");
                if (usageCount != null) {
                    nameValueMap.put(nodeName, new StringValue(usageCount));
                }
            }
        }
    }

    /** Parses command xml for parameters and fills up the hashes. */
    private void parseSection(final String section, final String xml, final Host host, final Host[] hosts) {
        final Document document = getXMLDocument(xml);

        /* get root <command> */
        final Node commandNode = getChildNode(document, "command");
        if (commandNode == null) {
            return;
        }
        final NodeList options = commandNode.getChildNodes();
        for (int i = 0; i < options.getLength(); i++) {
            final Node optionNode = options.item(i);

            /* <option> */
            if ("option".equals(optionNode.getNodeName())) {
                final String name = getAttribute(optionNode, "name");
                final String type = getAttribute(optionNode, "type");
                if ("flag".equals(type)) {
                    /* ignore flags */
                    continue;
                }
                if ("handler".equals(type)) {
                    final List<Value> items = new ArrayList<Value>();
                    items.add(new StringValue());
                    paramItemsMap.put(name, items);
                    paramDefaultMap.put(name, HARDCODED_DEFAULTS.get(name));
                } else if ("boolean".equals(type)) {
                    final List<Value> l = new ArrayList<Value>();
                    l.add(CONFIG_YES);
                    l.add(CONFIG_NO);
                    paramItemsMap.put(name, l);
                    paramDefaultMap.put(name, CONFIG_NO);
                }
                if ("fence-peer".equals(name)) {
                    final List<Value> l = new ArrayList<Value>();
                    l.add(new StringValue());
                    if (host.getArch() != null && !host.getArch().isEmpty()) {
                        l.add(new StringValue(host.getHeartbeatLibPath() + "/drbd-peer-outdater -t 5"));
                    }
                    l.add(new StringValue("/usr/lib/drbd/crm-fence-peer.sh"));
                    paramItemsMap.put(name, l);
                } else if ("after-resync-target".equals(name)) {
                    final List<Value> l = new ArrayList<Value>();
                    l.add(new StringValue());
                    l.add(new StringValue("/usr/lib/drbd/crm-unfence-peer.sh"));
                    paramItemsMap.put(name, l);
                } else if ("split-brain".equals(name)) {
                    final List<Value> l = new ArrayList<Value>();
                    l.add(new StringValue());
                    l.add(new StringValue("/usr/lib/drbd/notify-split-brain.sh root"));
                    paramItemsMap.put(name, l);
                } else if ("become-primary-on".equals(name)) {
                    final List<Value> l = new ArrayList<Value>();
                    l.add(new StringValue());
                    l.add(new StringValue("both"));
                    for (final Host h : hosts) {
                        l.add(new StringValue(h.getName()));
                    }
                    paramItemsMap.put(name, l);
                } else if ("verify-alg".equals(name)
                           || "csums-alg".equals(name)
                           || "data-integrity-alg".equals(name)
                           || "cram-hmac-alg".equals(name)) {
                    final List<Value> l = new ArrayList<Value>();
                    l.add(new StringValue());
                    for (final String cr : host.getAvailableCryptoModules()) {
                        l.add(new StringValue(cr));
                    }
                    paramItemsMap.put(name, l);
                }
                final NodeList optionInfos = optionNode.getChildNodes();
                for (int j = 0; j < optionInfos.getLength(); j++) {
                    final Node optionInfo = optionInfos.item(j);
                    final String tag = optionInfo.getNodeName();
                    /* <min>, <max>, <handler>, <default> */
                    if ("min".equals(tag)) {
                        final Value minValue = new StringValue(getText(optionInfo),
                                                               parseUnit(name, paramDefaultUnitMap.get(name)));
                        paramMinMap.put(name, convertToUnit(name, minValue));
                    } else if ("max".equals(tag)) {
                        final Value maxValue = new StringValue(getText(optionInfo),
                                                               parseUnit(name, paramDefaultUnitMap.get(name)));
                        paramMaxMap.put(name, convertToUnit(name, maxValue));
                    } else if ("handler".equals(tag)) {
                        paramItemsMap.get(name).add(new StringValue(getText(optionInfo)));
                    } else if ("default".equals(tag)) {
                        paramDefaultMap.put(name, new StringValue(getText(optionInfo),
                                                                  parseUnit(name, paramDefaultUnitMap.get(name))));
                    } else if ("unit".equals(tag)) {
                        paramUnitLongMap.put(name, getText(optionInfo));
                    } else if ("unit_prefix".equals(tag)) {
                        if (!"after".equals(name)
                            && !"resync-after".equals(name)) {
                            String option = getText(optionInfo);
                            if (!"s".equals(option)) {
                                /* "s" is an exception */
                                option = option.toUpperCase(Locale.US);
                            }
                            if ("1".equals(option)) {
                                option = "";
                            }
                            paramDefaultUnitMap.put(name, option);
                        }
                    } else if ("desc".equals(tag)) {
                        paramLongDescMap.put(name, getText(optionInfo));
                    }
                }
                paramTypeMap.put(name, type);
                if (!GLOBAL_SECTION.equals(section)
                    && !parametersList.contains(name)) {
                    parametersList.add(name);
                }
                if (!"resource".equals(section)
                    && !globalParametersList.contains(name)
                    && !("syncer".equals(section) && "after".equals(name))
                    && !"resync-after".equals(name)) {
                    globalParametersList.add(name);
                }

                paramSectionMap.put(name, section);
                if (!sectionParamsMap.containsKey(section)) {
                    sectionParamsMap.put(section, new ArrayList<String>());
                }
                if (!sectionParamsMap.get(section).contains(name)) {
                    sectionParamsMap.get(section).add(name);
                }
            }
        }
    }

    /** Parses section node and creates map with option name value pairs. */
    private void parseConfigSectionNode(final Node sectionNode, final Map<String, Value> nameValueMap) {
        final NodeList options = sectionNode.getChildNodes();
        for (int i = 0; i < options.getLength(); i++) {
            final Node option = options.item(i);
            if ("option".equals(option.getNodeName())) {
                final String name = getAttribute(option, "name");
                final String valueS = getAttribute(option, "value");

                final Value value;
                if (valueS == null) { /* boolean option */
                    value = CONFIG_YES;
                } else if (hasUnitPrefix(name)) {
                    value = parseValue(name, valueS);
                } else {
                    value = new StringValue(valueS);
                }
                nameValueMap.put(name, value);
            } else if ("section".equals(option.getNodeName())) {
                final String name = getAttribute(option, "name");
                if ("plugin".equals(name)) {
                    parseProxyPluginNode(option.getChildNodes(), nameValueMap);
                }
            }
        }
    }

    /** Parse proxy XML plugin section. */
    private void parseProxyPluginNode(final NodeList options, final Map<String, Value> nameValueMap) {
        for (int i = 0; i < options.getLength(); i++) {
            final Node option = options.item(i);
            if ("option".equals(option.getNodeName())) {
                final String nameValues = getAttribute(option, "name");
                final int spacePos = nameValues.indexOf(' ');
                final String name;
                final String value;
                if (spacePos > 0) {
                    name = DrbdProxy.PLUGIN_PARAM_PREFIX + nameValues.substring(0, spacePos);
                    value = nameValues.substring(spacePos + 1, nameValues.length());
                } else {
                    /* boolean */
                    name = DrbdProxy.PLUGIN_PARAM_PREFIX + nameValues;
                    value = CONFIG_YES.getValueForConfig();
                }
                nameValueMap.put(name, new StringValue(value));
            }
        }
    }

    /** Parses host node in the drbd config. */
    private void parseHostConfig(final String resName, final Node hostNode) {
        final String hostName = getAttribute(hostNode, "name");
        parseVolumeConfig(hostName, resName, hostNode); /* before 8.4 */
        final NodeList options = hostNode.getChildNodes();
        for (int i = 0; i < options.getLength(); i++) {
            final Node option = options.item(i);
            if ("volume".equals(option.getNodeName())) {
                parseVolumeConfig(hostName, resName, option);
            } else if ("address".equals(option.getNodeName())) {
                final String ip = getText(option);
                final String port = getAttribute(option, "port");
                final String family = getAttribute(option, "family");
                /* ip */
                Map<String, String> hostIpMap = resourceHostIpMap.get(resName);
                if (hostIpMap == null) {
                    hostIpMap = new HashMap<String, String>();
                    resourceHostIpMap.put(resName, hostIpMap);
                }
                hostIpMap.put(hostName, ip);
                /* port */
                Map<String, String> hostPortMap = resourceHostPortMap.get(resName);
                if (hostPortMap == null) {
                    hostPortMap = new HashMap<String, String>();
                    resourceHostPortMap.put(resName, hostPortMap);
                }
                hostPortMap.put(hostName, port);
                /* family */
                Map<String, String> hostFamilyMap = resourceHostFamilyMap.get(resName);
                if (hostFamilyMap == null) {
                    hostFamilyMap = new HashMap<String, String>();
                    resourceHostFamilyMap.put(resName, hostFamilyMap);
                }
                hostFamilyMap.put(hostName, family);
            } else if ("proxy".equals(option.getNodeName())) {
                parseProxyHostConfig(hostName, resName, option);
            }
        }
    }

    /** Parses host node in the drbd config. */
    private void parseVolumeConfig(final String hostName, final String resName, final Node volumeNode) {
        String volumeNr = getAttribute(volumeNode, "vnr");
        if (volumeNr == null) {
            volumeNr = "0";
        }
        final NodeList options = volumeNode.getChildNodes();
        for (int i = 0; i < options.getLength(); i++) {
            final Node option = options.item(i);
            if ("device".equals(option.getNodeName())) {
                String device = getText(option);
                if (device != null && device.isEmpty()) {
                    final String minor = getAttribute(option, "minor");
                    device = "/dev/drbd" + minor;
                }
                resourceDeviceMap.put(resName, volumeNr, device);
                deviceResourceMap.put(device, resName);
                deviceVolumeMap.put(device, volumeNr);
            } else if ("disk".equals(option.getNodeName())) {
                final String disk = getText(option);
                Map<String, String> hostDiskMap = resourceHostDiskMap.get(resName, volumeNr);
                if (hostDiskMap == null) {
                    hostDiskMap = new HashMap<String, String>();
                    resourceHostDiskMap.put(resName, volumeNr, hostDiskMap);
                }
                hostDiskMap.put(hostName, disk);
            } else if ("meta-disk".equals(option.getNodeName())
                       || "flexible-meta-disk".equals(option.getNodeName())) {
                final boolean flexible = "flexible-meta-disk".equals(option.getNodeName());
                final String metaDisk = getText(option);
                String metaDiskIndex = null;

                if (!flexible) {
                    metaDiskIndex = getAttribute(option, "index");
                }

                if (metaDiskIndex == null) {
                    metaDiskIndex = "Flexible";
                }
                /* meta-disk */
                Map<String, String> hostMetaDiskMap = resourceHostMetaDiskMap.get(resName, volumeNr);
                if (hostMetaDiskMap == null) {
                    hostMetaDiskMap = new HashMap<String, String>();
                    resourceHostMetaDiskMap.put(resName, volumeNr, hostMetaDiskMap);
                }
                hostMetaDiskMap.put(hostName, metaDisk);

                /* meta-disk index */
                Map<String, String> hostMetaDiskIndexMap = resourceHostMetaDiskIndexMap.get(resName, volumeNr);
                if (hostMetaDiskIndexMap == null) {
                    hostMetaDiskIndexMap = new HashMap<String, String>();
                    resourceHostMetaDiskIndexMap.put(resName, volumeNr, hostMetaDiskIndexMap);
                }
                hostMetaDiskIndexMap.put(hostName, metaDiskIndex);
            } else if ("address".equals(option.getNodeName())) {
                /* since 8.4, it's outside of volume */
                final String ip = getText(option);
                final String port = getAttribute(option, "port");
                final String family = getAttribute(option, "family");
                /* ip */
                Map<String, String> hostIpMap = resourceHostIpMap.get(resName);
                if (hostIpMap == null) {
                    hostIpMap = new HashMap<String, String>();
                    resourceHostIpMap.put(resName, hostIpMap);
                }
                hostIpMap.put(hostName, ip);
                /* port */
                Map<String, String> hostPortMap = resourceHostPortMap.get(resName);
                if (hostPortMap == null) {
                    hostPortMap = new HashMap<String, String>();
                    resourceHostPortMap.put(resName, hostPortMap);
                }
                hostPortMap.put(hostName, port);
                /* family */
                Map<String, String> hostFamilyMap = resourceHostFamilyMap.get(resName);
                if (hostFamilyMap == null) {
                    hostFamilyMap = new HashMap<String, String>();
                    resourceHostFamilyMap.put(resName, hostFamilyMap);
                }
                hostFamilyMap.put(hostName, family);
            } else if ("proxy".equals(option.getNodeName())) {
                parseProxyHostConfig(hostName, resName, option);
            }
        }
    }

    private void parseProxyHostConfig(final String hostName, final String resName, final Node proxyNode) {
        final String proxyHostName = getAttribute(proxyNode, "hostname");
        final NodeList options = proxyNode.getChildNodes();
        String insideIp = null;
        String insidePort = null;
        String outsideIp = null;
        String outsidePort = null;
        for (int i = 0; i < options.getLength(); i++) {
            final Node option = options.item(i);
            if ("inside".equals(option.getNodeName())) {
                insideIp = getText(option);
                insidePort = getAttribute(option, "port");
            } else if ("outside".equals(option.getNodeName())) {
                outsideIp = getText(option);
                outsidePort = getAttribute(option, "port");
            }
        }
        resourceHostProxyMap.put(resName, hostName, new HostProxy(proxyHostName,
                                                                  new StringValue(insideIp),
                                                                  new StringValue(insidePort),
                                                                  new StringValue(outsideIp),
                                                                  new StringValue(outsidePort)));
        proxyHostNames.add(proxyHostName);
    }

    public Map<String, String> getHostDiskMap(final String resName, final String volumeNr) {
        return resourceHostDiskMap.get(resName, volumeNr);
    }

    public String getVirtualInterfacePort(final String hostName, final String resName) {
        if (resourceHostPortMap.containsKey(resName)) {
            return resourceHostPortMap.get(resName).get(hostName);
        }
        return null;
    }

    public String getVirtualInterfaceFamily(final String hostName, final String resName) {
        if (resourceHostFamilyMap.containsKey(resName)) {
            return resourceHostFamilyMap.get(resName).get(hostName);
        }
        return null;
    }

    public String getVirtualInterface(final String hostName, final String resName) {
        if (resourceHostIpMap.containsKey(resName)) {
            final String ip = resourceHostIpMap.get(resName).get(hostName);
            final HostProxy hostProxy = getHostProxy(hostName, resName);
            if (hostProxy == null) {
                return ip;
            }
            final String proxyHostName = hostProxy.getProxyHostName();
            return ProxyNetInfo.displayString(ip, hostName, proxyHostName);
        }
        return null;
    }

    public HostProxy getHostProxy(final String hostName, final String resName) {
        return resourceHostProxyMap.get(resName, hostName);
    }

    public Iterable<String> getProxyHostNames() {
        return proxyHostNames;
    }

    public String getMetaDisk(final String hostName, final String resName, final String volumeNr) {
        final Map<String, String> hostMetaDiskMap = resourceHostMetaDiskMap.get(resName, volumeNr);
        if (hostMetaDiskMap != null) {
            return hostMetaDiskMap.get(hostName);
        }
        return null;
    }

    /**
     * Gets meta-disk index for a host and a resource. Index can
     * be keyword 'flexible'. In this case 'flexible-meta-disk option
     * will be generated in the config.
     */
    public String getMetaDiskIndex(final String hostName, final String resName, final String volumeNr) {
        final Map<String, String> hostMetaDiskIndexMap = resourceHostMetaDiskIndexMap.get(resName, volumeNr);
        if (hostMetaDiskIndexMap != null) {
            return hostMetaDiskIndexMap.get(hostName);
        }
        return null;
    }

    /** Parses resource xml. */
    private void parseConfigResourceNode(final Node resourceNode, final String resName) {
        final String resProtocol = getAttribute(resourceNode, PROTOCOL_PARAM);
        if (resProtocol != null) {
            Map<String, Value> nameValueMap = optionsMap.get(resName + '.' + "resource");
            if (nameValueMap == null) {
                nameValueMap = new HashMap<String, Value>();
            } else {
                optionsMap.remove(resName + '.' + "resource");
            }

            nameValueMap.put(PROTOCOL_PARAM, new StringValue(resProtocol));
            optionsMap.put(resName + '.' + "resource", nameValueMap);
        }
        final NodeList c = resourceNode.getChildNodes();
        for (int i = 0; i < c.getLength(); i++) {
            final Node n = c.item(i);
            if ("host".equals(n.getNodeName())) {
                /* <host> */
                parseHostConfig(resName, n);
            } else if ("section".equals(n.getNodeName())
                       || ("#text".equals(n.getNodeName()) && !"".equals(n.getNodeValue().trim()))) {
                final String secName;
                if ("#text".equals(n.getNodeName())) {
                    secName = "proxy";
                    /* workaround for broken proxy xml in common section
                       at least till drbd 8.4.2 */
                    Map<String, Value> nameValueMap = optionsMap.get(resName + '.' + secName);
                    if (nameValueMap == null) {
                        nameValueMap = new HashMap<String, Value>();
                    } else {
                        optionsMap.remove(resName + '.' + secName);
                    }
                    try {
                        final boolean isProxy = DrbdProxy.parse(this, n.getNodeValue(), nameValueMap);
                        if (!isProxy) {
                            continue;
                        }
                    } catch (final Exceptions.DrbdConfigException e) {
                        LOG.appWarning("parseConfigResourceNode: " + e.getMessage());
                        LOG.appWarning("parseConfigResourceNode: " + n.getNodeValue());
                        continue;
                    }
                    optionsMap.put(resName + '.' + secName, nameValueMap);
                } else {
                    /* <resource> */
                    secName = getAttribute(n, "name");

                    Map<String, Value> nameValueMap = optionsMap.get(resName + '.' + secName);
                    if (nameValueMap == null) {
                        nameValueMap = new HashMap<String, Value>();
                    } else {
                        optionsMap.remove(resName + '.' + secName);
                    }

                    parseConfigSectionNode(n, nameValueMap);
                    optionsMap.put(resName + '.' + secName, nameValueMap);
                }
                if (!sectionParamsMap.containsKey(secName)
                    && !sectionParamsMap.containsKey(secName + "-options")) {
                    LOG.appWarning("parseConfigResourceNode: unknown section: " + secName);
                    if (!unknownSections) {
                        /* unknown section, so it's not removed. */
                        guiData.progressIndicatorFailed("DRBD: unknown section: " + secName);
                        unknownSections = true;
                    }
                }
            }
        }
    }

    /** Parses config xml from drbdadm dump-xml. */
    private void parseConfig(final String configXML) {
        final int start = configXML.indexOf("<config");
        if (start < 0) {
            final String c = configXML.trim();
            if (!c.isEmpty() && !IGNORE_CONFIG_ERRORS.contains(c)) {
                LOG.error("parseConfig: error: " + c);
            }
            return;
        }
        final Document document = getXMLDocument(configXML.substring(start));
        if (document == null) {
            return;
        }

        /* get root <config> */
        final Node configNode = getChildNode(document, "config");
        if (configNode == null) {
            return;
        }
        /* config file=".." TODO: */

        final NodeList resources = configNode.getChildNodes();
        Map<String, Value> globalNameValueMap = optionsMap.get(GLOBAL_SECTION);
        if (globalNameValueMap == null) {
            globalNameValueMap = new HashMap<String, Value>();
            optionsMap.put(GLOBAL_SECTION, globalNameValueMap);
        }
        globalNameValueMap.put("usage-count", CONFIG_YES);
        globalNameValueMap.put("disable-ip-verification", CONFIG_NO);

        for (int i = 0; i < resources.getLength(); i++) {
            final Node resourceNode = resources.item(i);
            /* <global> */
            if (resourceNode.getNodeName().equals(GLOBAL_SECTION)) {
                parseConfigGlobalNode(resourceNode, globalNameValueMap);
            }
            /* <common> */
            if ("common".equals(resourceNode.getNodeName())) {
                parseConfigResourceNode(resourceNode, "Section.Common");
            }
            /* <resource> */
            if ("resource".equals(resourceNode.getNodeName())) {
                final String resName = getAttribute(resourceNode, "name");
                if (!resourceList.contains(resName)) {
                    resourceList.add(resName);
                }
                parseConfigResourceNode(resourceNode, resName);
            }
        }
    }

    /** Returns value from drbd global config identified by option name. */
    public Value getGlobalConfigValue(final String optionName) {
        final Map<String, Value> option = optionsMap.get(GLOBAL_SECTION);
        if (option != null) {
            return option.get(optionName);
        }
        return null;
    }

    /**
     * Returns value from drbd config identified by resource, section and
     * option name.
     */
    public Value getConfigValue(final String res, final String section, final String optionName) {
        final Map<String, Value> option = optionsMap.get(res + '.' + section);

        Value value = null;
        if (option != null) {
            value = option.get(optionName);
        }

        return value;
    }

    public Value getCommonConfigValue(final String section, final String optionName) {
        Value value = null;
        final Map<String, Value> option = optionsMap.get("Section.Common." + section);
        if (option != null) {
            value = option.get(optionName);
        }
        return value;
    }

    public String[] getResources() {
        return resourceList.toArray(new String[resourceList.size()]);
    }

    /**
     * Returns drbd device of the resource. Although there can be different
     * drbd devices on different hosts. We do not allow that.
     */
    public String getDrbdDevice(final String res, final String volumeNr) {
        return resourceDeviceMap.get(res, volumeNr);
    }

    /** Returns map from res and volume to drbd device. */
    public MultiKeyMap<String, String> getResourceDeviceMap() {
        return resourceDeviceMap;
    }

    /** Gets block device object from device number. Can return null. */
    private BlockDevInfo getBlockDevInfo(final String devNr, final String hostName, final DrbdGraph drbdGraph) {
        final String disk = getBackingDisk(devNr, hostName);
        if (disk != null) {
            return drbdGraph.findBlockDevInfo(hostName, disk);
        }
        return null;
    }

    /** Return backing disk from device number. Can return null. */
    private String getBackingDisk(final String devNr, final String hostName) {
        final String device = "/dev/drbd" + devNr;
        final String resName = deviceResourceMap.get(device);
        String volumeNr = deviceVolumeMap.get(device);
        if (volumeNr == null) {
            volumeNr = "0";
        }
        if (resName != null) {
            final Map<String, String> hostDiskMap = resourceHostDiskMap.get(resName, volumeNr);
            if (hostDiskMap != null) {
                return hostDiskMap.get(hostName);
            }
        }
        return null;
    }

    /**
     * Parses events from drbd kernel module obtained via drbdsetup .. events
     * command and stores the values in the BlockDevice object.
     */
    public boolean parseDrbdEvent(final String hostName, final DrbdGraph drbdGraph, final String rawOutput) {
        if (rawOutput == null || hostName == null) {
            return false;
        }

        final String output = rawOutput.trim();
        if (output != null && output.isEmpty()) {
            return false;
        }
        if ("No response from the DRBD driver! Is the module loaded?".equals(
                output)) {
            if (hostDrbdLoadedMap.get(hostName)) {
                hostDrbdLoadedMap.put(hostName, false);
                return true;
            } else {
                return false;
            }
        } else {
            hostDrbdLoadedMap.put(hostName, true);
        }
        /* since drbd 8.3 there is ro: instead of st: */
        /* since drbd 8.4 there is ro: instead of st: */
        Pattern p = Pattern.compile("^(\\d+)\\s+ST\\s+(\\S+)\\s+\\{\\s+cs:(\\S+)\\s+"
                                    + "(?:st|ro):(\\S+)/(\\S+)\\s+ds:(\\S+)/(\\S+)\\s+(\\S+).*?");
        Matcher m = p.matcher(output);
        final Pattern pDev = Pattern.compile("^(\\d+),(\\S+)\\[(\\d+)\\]$");
        if (m.matches()) {
            /* String counter      = m.group(1); // not used */
            final String devNrString  = m.group(2);
            final String cs           = m.group(3);
            final String ro1          = m.group(4);
            final String ro2          = m.group(5);
            final String ds1          = m.group(6);
            final String ds2          = m.group(7);
            final String flags        = m.group(8);

            final Matcher mDev = pDev.matcher(devNrString);
            String devNr = devNrString;
            if (mDev.matches()) { /* since 8.4 */
                devNr = mDev.group(1);
            }
            /* get blockdevice object from device */
            final String disk = getBackingDisk(devNr, hostName);
            if (disk != null) {
                final BlockDevInfo bdi = drbdGraph.findBlockDevInfo(hostName, disk);
                if (bdi != null) {
                    if (bdi.getBlockDevice().isDifferent(cs, ro1, ds1, flags)) {
                        bdi.getBlockDevice().setDrbdBackingDisk(disk);
                        bdi.getBlockDevice().setConnectionState(cs);
                        bdi.getBlockDevice().setNodeState(ro1);
                        bdi.getBlockDevice().setDiskState(ds1);
                        bdi.getBlockDevice().setNodeStateOther(ro2);
                        bdi.getBlockDevice().setDiskStateOther(ds2);
                        bdi.getBlockDevice().setDrbdFlags(flags);
                        bdi.updateInfo();
                        return true;
                    } else {
                        return false;
                    }
                }
            }
            return false;
        }
        /* 19 SP 0 16.9 */
        p = Pattern.compile("^(\\d+)\\s+SP\\s+(\\S+)\\s(\\d+\\.\\d+).*");
        m = p.matcher(output);
        if (m.matches()) {
            /* String counter      = m.group(1); // not used */
            final String devNrString = m.group(2);
            final String synced = m.group(3);

            final Matcher mDev = pDev.matcher(devNrString);
            String devNr = devNrString;
            if (mDev.matches()) { /* since 8.4 */
                devNr = mDev.group(1);
            }
            final BlockDevInfo bdi = getBlockDevInfo(devNr, hostName, drbdGraph);
            if (bdi != null && bdi.getBlockDevice().isDrbd()) {
                if (Tools.areEqual(bdi.getBlockDevice().getSyncedProgress(), synced)) {
                    return false;
                } else {
                    bdi.getBlockDevice().setSyncedProgressInPercents(synced);
                    bdi.updateInfo();
                    return true;
                }
            }
            return false;
        }
        /* 19 UH 1 split-brain */
        p = Pattern.compile("^(\\d+)\\s+UH\\s+(\\S+)\\s([a-z-]+).*");
        m = p.matcher(output);
        if (m.matches()) {
            /* String counter      = m.group(1); // not used */
            final String devNrString = m.group(2);
            final String what = m.group(3);
            final Matcher mDev = pDev.matcher(devNrString);
            String devNr = devNrString;
            if (mDev.matches()) { /* since 8.4 */
                devNr = mDev.group(1);
            }
            LOG.debug("parseDrbdEvent: event: " + devNr + " - " + what);
            if ("split-brain".equals(what)) {
                final BlockDevInfo bdi = getBlockDevInfo(devNr, hostName, drbdGraph);

                if (bdi != null && bdi.getBlockDevice().isDrbd()) {
                    if (bdi.getBlockDevice().isSplitBrain()) {
                        return false;
                    } else {
                        bdi.getBlockDevice().setSplitBrain(true);
                        bdi.updateInfo();
                        return true;
                    }
                }
            }
            return false;
        }
        return false;
    }

    /** Removes the resource from resources, so that it does not reappear. */
    public void removeResource(final String res) {
        resourceList.remove(res);
    }

    /** Removes the volume from hashes, so that it does not reappear. */
    public void removeVolume(final String res, final String dev, final String volumeNr) {
        resourceDeviceMap.remove(res, volumeNr);
        deviceResourceMap.remove(dev);
        deviceVolumeMap.remove(dev);
    }

    /**
     * Whether drbd is disabled. If there are unknown sections, that we don't
     * want to overwrite.
     */
    public boolean isDrbdDisabled() {
        return unknownSections && !application.isAdvancedMode();
    }

    public String getOldConfig() {
        return oldConfig;
    }

    public Value parseValue(final String param, final String v) {
        if (v == null) {
            return null;
        }
        final Matcher m = UNIT_PATTERN.matcher(v);
        if (m.matches()) {
            final String value = m.group(1);
            final String u = m.group(2);

            final Unit unit = parseUnit(param, u);
            return new StringValue(value, unit);
        }
        return new StringValue(v);
    }

    private Unit parseUnit(final String param, final String u) {
        if (!hasUnitPrefix(param)) {
            return null;
        }
        final String unitType = getUnitLong(param);
        if (unitType == null || unitType.isEmpty()) {
            return parseSizeUnit(param, u);
        } else {
            return parseByteUnit(param, u);
        }
    }

    private Unit parseSizeUnit(final String param, final String u) {
        final String unitPart = getUnitPart(getUnitLong(param));
        if (u == null || u.isEmpty()) {
            return getUnitDefault(unitPart);
        } else if ("k".equalsIgnoreCase(u)) {
            return getUnitKi(unitPart);
        } else if ("m".equalsIgnoreCase(u)) {
            return getUnitMi(unitPart);
        } else if ("g".equalsIgnoreCase(u)) {
            return getUnitGi(unitPart);
        } else if ("t".equalsIgnoreCase(u)) {
            return getUnitTi(unitPart);
        } else {
            LOG.appError("can't parse unit: " + u + " param: " + param);
        }
        return null;
    }

    private Unit parseByteUnit(final String param, final String u) {
        final String unitPart = getUnitPart(getUnitLong(param));
        if (u == null || u.isEmpty()) {
            return getUnitBytes(unitPart);
        } else if ("k".equalsIgnoreCase(u)) {
            return getUnitKiBytes(unitPart);
        } else if ("m".equalsIgnoreCase(u)) {
            return getUnitMiBytes(unitPart);
        } else if ("g".equalsIgnoreCase(u)) {
            return getUnitGiBytes(unitPart);
        } else if ("t".equalsIgnoreCase(u)) {
            return getUnitTiBytes(unitPart);
        } else if ("s".equalsIgnoreCase(u)) {
            return getUnitSectors(unitPart);
        } else {
            LOG.appError("can't parse unit: " + u + " param: " + param);
        }
        return null;
    }

    public class HostProxy {
        private final String proxyHostName;
        private final Value insideIp;
        private final Value insidePort;
        private final Value outsideIp;
        private final Value outsidePort;

        public HostProxy(final String proxyHostName,
                         final Value insideIp,
                         final Value insidePort,
                         final Value outsideIp,
                         final Value outsidePort) {
            this.proxyHostName = proxyHostName;
            this.insideIp = insideIp;
            this.insidePort = insidePort;
            this.outsideIp = outsideIp;
            this.outsidePort = outsidePort;
        }

        public String getProxyHostName() {
            return proxyHostName;
        }

        public Value getInsideIp() {
            return insideIp;
        }

        public Value getInsidePort() {
            return insidePort;
        }

        public Value getOutsideIp() {
            return outsideIp;
        }

        public Value getOutsidePort() {
            return outsidePort;
        }
    }

    /** Converts value to unit e.g. bytes. */
    public BigInteger convertToUnit(final String param, final Value value) {
        final String unitPart = getUnitPart(getUnitLong(param));
        final Unit unit = value.getUnit();
        BigInteger num = new BigInteger(value.getValueForConfig());
        if (getUnitPiBytes(unitPart).equals(unit)
            || getUnitPi(unitPart).equals(unit)) {
            num = num.multiply(KILO)
                     .multiply(KILO)
                     .multiply(KILO)
                     .multiply(KILO)
                     .multiply(KILO);
        } else if (getUnitTiBytes(unitPart).equals(unit)
                   || getUnitTi(unitPart).equals(unit)) {
            num = num.multiply(KILO)
                     .multiply(KILO)
                     .multiply(KILO)
                     .multiply(KILO);
        } else if (getUnitGiBytes(unitPart).equals(unit)
                   || getUnitGi(unitPart).equals(unit)) {
            num = num.multiply(KILO)
                     .multiply(KILO)
                     .multiply(KILO);
        } else if (getUnitMiBytes(unitPart).equals(unit)
                   || getUnitMi(unitPart).equals(unit)) {
            num = num.multiply(KILO)
                     .multiply(KILO);
        } else if (getUnitKiBytes(unitPart).equals(unit)
                   || getUnitKi(unitPart).equals(unit)) {
            num = num.multiply(KILO);
        } else {
            LOG.appWarning("unknown unit: " + unit);
        }
        return num;
    }
}
TOP

Related Classes of lcmc.drbd.domain.DrbdXml

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.