Package lcmc.cluster.ui.wizard

Source Code of lcmc.cluster.ui.wizard.HbConfig

/*
* 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.cluster.ui.wizard;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.inject.Inject;
import javax.inject.Named;
import javax.swing.BoxLayout;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SpringLayout;

import lcmc.Exceptions;
import lcmc.common.ui.GUIData;
import lcmc.common.domain.AccessMode;
import lcmc.common.domain.Application;
import lcmc.crm.domain.CastAddress;
import lcmc.cluster.domain.Cluster;
import lcmc.host.domain.Host;
import lcmc.common.domain.StringValue;
import lcmc.common.domain.Value;
import lcmc.drbd.domain.NetInterface;
import lcmc.crm.domain.UcastLink;
import lcmc.common.ui.SpringUtilities;
import lcmc.common.ui.WizardDialog;
import lcmc.cluster.ui.widget.Check;
import lcmc.cluster.ui.widget.Widget;
import lcmc.cluster.ui.widget.WidgetFactory;
import lcmc.common.domain.ExecCallback;
import lcmc.crm.service.Heartbeat;
import lcmc.host.service.NetInterfaceService;
import lcmc.logger.Logger;
import lcmc.logger.LoggerFactory;
import lcmc.common.ui.utils.MyButton;
import lcmc.cluster.service.ssh.ExecCommandConfig;
import lcmc.cluster.service.ssh.ExecCommandThread;
import lcmc.common.domain.util.Tools;
import lcmc.common.ui.utils.WidgetListener;

/**
* An implementation of a dialog where heartbeat is initialized on all hosts.
*/
@Named
final class HbConfig extends DialogCluster {
    private static final Logger LOG = LoggerFactory.getLogger(HbConfig.class);
    private static final String KEEPALIVE_OPTION = "keepalive";
    private static final String WARNTIME_OPTION = "warntime";
    private static final String DEADTIME_OPTION = "deadtime";
    private static final String INITDEAD_OPTION = "initdead";
    private static final String CRM_OPTION = "crm";
    private static final String COMPRESSION_OPTION = "compression";
    private static final String COMPRESSION_THRESHOLD_OPTION = "compression_threshold";
    private static final String TRADITIONAL_COMPRESSION_OPTION = "traditional_compression";
    private static final String LOGFACILITY_OPTION = "logfacility";
    private static final String USE_LOGD_OPTION = "use_logd";
    private static final String AUTOJOIN_OPTION = "autojoin";
    private static final String NODE_OPTION = "node";
    private static final String[] OPTIONS = {KEEPALIVE_OPTION,
                                             WARNTIME_OPTION,
                                             DEADTIME_OPTION,
                                             INITDEAD_OPTION,
                                             CRM_OPTION,
                                             COMPRESSION_OPTION,
                                             COMPRESSION_THRESHOLD_OPTION,
                                             TRADITIONAL_COMPRESSION_OPTION,
                                             LOGFACILITY_OPTION,
                                             USE_LOGD_OPTION,
                                             AUTOJOIN_OPTION,
                                             NODE_OPTION};
    private static final Map<String, Widget.Type> OPTION_WIDGET_TYPES = new HashMap<String, Widget.Type>();
    private static final Map<String, String> OPTION_REGEXPS = new HashMap<String, String>();
    private static final Map<String, Value> OPTION_DEFAULTS = new HashMap<String, Value>();
    private static final Map<String, Integer> OPTION_SIZES = new HashMap<String, Integer>();
    private static final Value MCAST_TYPE = new StringValue("mcast");
    private static final Value BCAST_TYPE = new StringValue("bcast");
    private static final Value UCAST_TYPE = new StringValue("ucast");
    private static final Value SERIAL_TYPE = new StringValue("serial");
    private static final int ADDR_COMBOBOX_WIDTH = 160;
    private static final int LINK_COMBOBOX_WIDTH = 130;
    private static final int TYPE_COMBOBOX_WIDTH = 80;
    private static final int INTERFACE_COMBOBOX_WIDTH = 80;
    private static final int REMOVE_BUTTON_WIDTH  = 100;
    private static final int REMOVE_BUTTON_HEIGHT = 14;
    private static final String EDIT_CONFIG_STRING = Tools.getString("Dialog.Cluster.HbConfig.Checkbox.EditConfig");
    private static final String SEE_EXISTING_STRING = Tools.getString("Dialog.Cluster.HbConfig.Checkbox.SeeExisting");
    private static final String HA_CF_ERROR_STRING = "error: read error";
    private static final String NEWLINE = "\\r?\\n";
    static {
        OPTION_REGEXPS.put(KEEPALIVE_OPTION, "\\d*");
        OPTION_REGEXPS.put(WARNTIME_OPTION, "\\d*");
        OPTION_REGEXPS.put(DEADTIME_OPTION, "\\d*");
        OPTION_REGEXPS.put(INITDEAD_OPTION, "\\d*");
        OPTION_REGEXPS.put(CRM_OPTION, "\\w*");

        OPTION_REGEXPS.put(COMPRESSION_OPTION, "\\w*");
        OPTION_REGEXPS.put(COMPRESSION_THRESHOLD_OPTION, "\\d*");
        OPTION_REGEXPS.put(TRADITIONAL_COMPRESSION_OPTION, "\\w*");
        OPTION_REGEXPS.put(LOGFACILITY_OPTION, "\\w*");
        OPTION_REGEXPS.put(USE_LOGD_OPTION, "\\w*");
        OPTION_REGEXPS.put(AUTOJOIN_OPTION, "\\w*");
        OPTION_REGEXPS.put(NODE_OPTION, ".*?");
        /* defaults */
        OPTION_DEFAULTS.put(KEEPALIVE_OPTION, new StringValue("2"));
        OPTION_DEFAULTS.put(WARNTIME_OPTION, new StringValue("20"));
        OPTION_DEFAULTS.put(DEADTIME_OPTION, new StringValue("30"));
        OPTION_DEFAULTS.put(INITDEAD_OPTION, new StringValue("30"));
        OPTION_DEFAULTS.put(CRM_OPTION, new StringValue("respawn"));
        OPTION_DEFAULTS.put(COMPRESSION_OPTION, new StringValue("bz2"));
        OPTION_DEFAULTS.put(COMPRESSION_THRESHOLD_OPTION, new StringValue("20"));
        OPTION_DEFAULTS.put(TRADITIONAL_COMPRESSION_OPTION, new StringValue("on"));
        /* sizes */
        OPTION_SIZES.put(CRM_OPTION, 100);
        OPTION_SIZES.put(COMPRESSION_OPTION, 80);
        OPTION_SIZES.put(LOGFACILITY_OPTION, 80);
        OPTION_SIZES.put(USE_LOGD_OPTION, 50);
        OPTION_SIZES.put(AUTOJOIN_OPTION, 80);
        OPTION_SIZES.put(NODE_OPTION, 300);
    }
    private final Map<String, Value> optionDefaults = new HashMap<String, Value>(OPTION_DEFAULTS);
    private final Map<String, Value[]> optionValues = new HashMap<String, Value[]>();
    private final Map<String, Widget> optionsWidgets = new HashMap<String, Widget>();

    private JCheckBox dopdWidget = null;
    private JCheckBox mgmtdWidget = null;
    private JPanel mcastPanel;
    /** Set of ucast, bcast, mcast etc. addresses. */
    private final Collection<CastAddress> castAddresses = new LinkedHashSet<CastAddress>();
    private final JLabel configStatus = new JLabel("");
    /** Connection type pulldown menu: ucast, bcast, mcast ... */
    private Widget typeWidget;
    private Widget ifaceWidget;
    private Widget serialWidget;
    private Widget ucastLink1Widget;
    private Widget ucastLink2Widget;
    private Widget addrWidget;
    private MyButton addAddressButton;
    /** Array with /etc/ha.d/ha.cf configs from all hosts. */
    private String[] configs;
    private JPanel statusPanel;
    /** Check box that allows to edit a new config are see the existing
     * configs. */
    private JCheckBox configCheckbox;
    private final JPanel configPanel = new JPanel();
    private boolean configChangedByUser = false;
    private volatile JScrollPane configScrollPane = null;
    private volatile boolean configAlreadyScrolled = false;
    private CountDownLatch fieldCheckLatch = new CountDownLatch(1);
    @Inject
    private GUIData guiData;
    @Inject
    private Application application;
    @Inject
    private WidgetFactory widgetFactory;
    private MyButton makeConfigButton;
    @Inject
    private InitCluster initCluster;
    @Inject
    private NetInterfaceService netInterfaceService;

    @Override
    public void init(final WizardDialog previousDialog, final Cluster cluster) {
        super.init(previousDialog, cluster);
        makeConfigButton = widgetFactory.createButton(Tools.getString("Dialog.Cluster.HbConfig.CreateHbConfig"));
        final Host[] hosts = getCluster().getHostsArray();
        final StringBuilder config = new StringBuilder();
        boolean first = true;
        for (final Host host : hosts) {
            if (!first) {
                config.append(' ');
            }
            first = false;
            config.append(host.getHostname());
        }
        /* choices */
        optionValues.put(NODE_OPTION, new Value[]{new StringValue(config.toString()), new StringValue()});
        optionDefaults.put(NODE_OPTION, new StringValue(config.toString()));
        optionValues.put(CRM_OPTION, new Value[]{new StringValue("respawn"),
                                                 new StringValue("on"),
                                                 new StringValue("off")});
        optionValues.put(COMPRESSION_OPTION, new Value[]{new StringValue(),
                                                         new StringValue("zlib"),
                                                         new StringValue("bz2")});
        optionValues.put(TRADITIONAL_COMPRESSION_OPTION,
                         new Value[]{new StringValue(), new StringValue("on"), new StringValue("off")});
        optionValues.put(LOGFACILITY_OPTION, new Value[]{new StringValue("local0"),
                                                         new StringValue("local1"),
                                                         new StringValue("local2"),
                                                         new StringValue("local3"),
                                                         new StringValue("local4"),
                                                         new StringValue("local5"),
                                                         new StringValue("local6"),
                                                         new StringValue("local7"),
                                                         new StringValue("none")});
        optionValues.put(USE_LOGD_OPTION, new Value[]{new StringValue(),
                                                      new StringValue("on"),
                                                      new StringValue("off")});
        optionValues.put(AUTOJOIN_OPTION, new Value[]{new StringValue(),
                                                      new StringValue("any"),
                                                      new StringValue("other"),
                                                      new StringValue("none")});
        configs = new String[hosts.length];
        makeConfigButton.setBackgroundColor(Tools.getDefaultColor("ConfigDialog.Button"));
        makeConfigButton.setEnabled(false);
        makeConfigButton.addActionListener(
            new ActionListener() {
                @Override
                public void actionPerformed(final ActionEvent e) {
                    final Thread thread = new Thread(
                        new Runnable() {
                            @Override
                            public void run() {
                                fieldCheckLatch = new CountDownLatch(1);
                                application.invokeLater(new Runnable() {
                                    @Override
                                    public void run() {
                                        makeConfigButton.setEnabled(false);
                                    }
                                });
                                disableComponents();
                                final StringBuilder config = hbConfigHead(false);
                                config.append(hbConfigOptions());
                                config.append('\n');
                                config.append(hbConfigAddr());
                                config.append(hbConfigDopd(dopdWidget.isSelected()));
                                config.append(hbConfigMgmtd(mgmtdWidget.isSelected()));

                                Heartbeat.createHBConfig(hosts, config);
                                final boolean configOk = updateOldHbConfig();
                                if (dopdWidget.isSelected()) {
                                    for (final Host h : hosts) {
                                        final String hbV = h.getHeartbeatVersion();
                                        boolean wa = false;
                                        try {
                                            if (hbV != null && Tools.compareVersions(hbV, "3.0.2") <= 0) {
                                                wa = true;
                                            }
                                        } catch (
                                         final Exceptions.IllegalVersionException e) {
                                            LOG.appWarning("run: " + e.getMessage(), e);
                                        }
                                        Heartbeat.enableDopd(h, wa);
                                    }
                                }
                                Heartbeat.reloadHeartbeats(hosts);
                                enableComponents();
                                final List<String> incorrect = new ArrayList<String>();
                                final List<String> changed = new ArrayList<String>();
                                if (configOk) {
                                    hideRetryButton();
                                } else {
                                    incorrect.add("config failed");
                                }
                                nextButtonSetEnabled(new Check(incorrect, changed));

                                if (configOk) {
                                    if (!application.getAutoClusters().isEmpty()) {
                                        Tools.sleep(1000);
                                        pressNextButton();
                                    }
                                }
                            }
                        }
                    );
                    thread.start();
                }
            });
    }

    @Override
    public WizardDialog nextDialog() {
        final DialogCluster nextDialog = initCluster;
        nextDialog.init(this, getCluster());
        return nextDialog;
    }

    @Override
    protected String getClusterDialogTitle() {
        return Tools.getString("Dialog.Cluster.HbConfig.Title");
    }

    @Override
    protected String getDescription() {
        return Tools.getString("Dialog.Cluster.HbConfig.Description");
    }

    @Override
    public String nextButton() {
        return Tools.getString("Dialog.Cluster.HbConfig.NextButton");
    }

    @Override
    protected void initDialogBeforeVisible() {
        super.initDialogBeforeVisible();
        configPanel.setLayout(new BoxLayout(configPanel, BoxLayout.PAGE_AXIS));
        configPanel.setBackground(Tools.getDefaultColor("ConfigDialog.Background"));
        enableComponentsLater(new JComponent[]{buttonClass(nextButton())});
    }

    @Override
    protected void initDialogAfterVisible() {
        final Thread thread = new Thread(
            new Runnable() {
                @Override
                public void run() {
                    final boolean configOk = updateOldHbConfig();
                    application.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            makeConfigButton.setEnabled(false);
                        }
                    });
                    enableComponents();
                    final List<String> incorrect = new ArrayList<String>();
                    final List<String> changed = new ArrayList<String>();
                    if (!configOk) {
                        incorrect.add("config failed");
                    }
                    nextButtonSetEnabled(new Check(incorrect, changed));
                    if (configOk) {
                        if (!application.getAutoClusters().isEmpty()) {
                            Tools.sleep(1000);
                            pressNextButton();
                        }
                    }
                }
            });
        thread.start();
    }

    /**
     * Parses the old config and sets the new one with old and new information.
     */
    private void setNewConfig(final String oldConfig) {
        final String[] config = oldConfig.split(NEWLINE);
        final Pattern bcastP = Pattern.compile("(bcast) (\\w+)");
        final Pattern mcastP = Pattern.compile("(mcast) (\\w+) (.*)");
        final Pattern serialP = Pattern.compile("(serial) (.*)");
        final Pattern ucastP = Pattern.compile("(ucast) (\\w+) (.*)");
        final Map<String, Pattern> optionPatterns = new HashMap<String, Pattern>();
        for (final String option : OPTIONS) {
            optionPatterns.put(option,
                               Pattern.compile("^\\s*" + option + "\\s+(" + OPTION_REGEXPS.get(option) + ")\\s*$"));
        }
        final Pattern dopdP = Pattern.compile("^\\s*respawn hacluster .*/dopd$");
        final Pattern mgmtdP = Pattern.compile("^\\s*respawn root .*/mgmtd -v$");
        castAddresses.clear();
        final Map<String, String> opValues = new HashMap<String, String>();
        for (final String line : config) {
            final Matcher bcastM  = bcastP.matcher(line);
            final Matcher mcastM  = mcastP.matcher(line);
            final Matcher ucastM  = ucastP.matcher(line);
            final Matcher serialM = serialP.matcher(line);
            final Matcher dopdM = dopdP.matcher(line);
            final Matcher mgmtdM = mgmtdP.matcher(line);
            final String type;
            String iface      = "";
            String addr       = "";
            String serial     = "";
            if (bcastM.matches()) {
                type  = bcastM.group(1);
                iface = bcastM.group(2);
            } else if (mcastM.matches()) {
                type  = mcastM.group(1);
                iface = mcastM.group(2);
                addr  = mcastM.group(3);
            } else if (serialM.matches()) {
                type   = serialM.group(1);
                serial = serialM.group(2);
            } else if (ucastM.matches()) {
                type  = ucastM.group(1);
                iface = ucastM.group(2);
                addr  = ucastM.group(3);
            } else if (dopdM.matches()) {
                dopdWidget.setSelected(true);
                continue;
            } else if (mgmtdM.matches()) {
                mgmtdWidget.setSelected(true);
                continue;
            } else {
                for (final String option : OPTIONS) {
                    final Matcher m = optionPatterns.get(option).matcher(line);
                    if (m.matches()) {
                        opValues.put(option, m.group(1).trim());
                    }
                }
                continue;
            }
            if (type != null && !type.isEmpty()) {
                castAddresses.add(new CastAddress(type, iface, addr, serial));
            }
        }
        for (final String option : OPTIONS) {
            if (opValues.containsKey(option)) {
                optionsWidgets.get(option).setValue(new StringValue(opValues.get(option)));
            } else {
                optionsWidgets.get(option).setValue(new StringValue());
            }
        }
    }

    /**
     * Checks whether the old config is the same on all hosts, if it exists at
     * all and enable the components accordingly.
     * Returns whether the configs are ok and the same on all hosts.
     */
    private boolean updateOldHbConfig() { /* is run in a thread */
        final Host[] hosts = getCluster().getHostsArray();
        final ExecCommandThread[] ts = new ExecCommandThread[hosts.length];
        configStatus.setText(Tools.getString("Dialog.Cluster.HbConfig.Loading"));
        int i = 0;

        for (final Host h : hosts) {
            final int index = i;
            ts[i] = h.execCommand(new ExecCommandConfig().commandString("Heartbeat.getHbConfig")
                             .execCallback(new ExecCallback() {
                                 @Override
                                 public void done(final String answer) {
                                     configs[index] = answer;
                                 }
                                 @Override
                                 public void doneError(final String answer, final int errorCode) {
                                     configs[index] = HA_CF_ERROR_STRING;
                                 }
                             })
                             .silentCommand()
                             .silentOutput());
            i++;
        }
        for (final ExecCommandThread t : ts) {
            /* wait for all of them */
            try {
                t.join();
            } catch (final InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }

        boolean configOk = false;
        boolean noConfigs = true;
        if (configs[0].equals(HA_CF_ERROR_STRING)) {
            application.invokeLater(new Runnable() {
                @Override
                public void run() {
                    configStatus.setText(hosts[0] + ": " + Tools.getString("Dialog.Cluster.HbConfig.NoConfigFound"));
                }
            });
            retry();
            if (!application.getAutoClusters().isEmpty()) {
                Tools.sleep(1000);
                addAddressButton.pressButton();
            }
        } else {
            noConfigs = false;
            int j;
            for (j = 1; j < configs.length; j++) {
                final Host host = hosts[j];
                if (configs[j].equals(HA_CF_ERROR_STRING)) {
                    application.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            configStatus.setText(host
                                                 + ": "
                                                 + Tools.getString("Dialog.Cluster.HbConfig.NoConfigFound"));
                        }
                    });
                    break;
                } else if (!configs[0].equals(configs[j])) {
                    application.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            configStatus.setText(Tools.getString("Dialog.Cluster.HbConfig.ConfigsNotTheSame"));
                        }
                    });
                    break;
                }
            }
            if (j < configs.length) {
                retry();
            } else {
                boolean generated = false;
                final Pattern p = Pattern.compile("## generated by (drbd-gui|LCMC).*", Pattern.DOTALL);
                final Matcher m = p.matcher(configs[0]);
                if (m.matches()) {
                    generated = true;
                }
                final boolean editableConfig = generated;
                application.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        configStatus.setText(Tools.getString("Dialog.Cluster.HbConfig.ha.cf.ok"));
                        configCheckbox.setSelected(false);
                        if (editableConfig) {
                            configCheckbox.setText(SEE_EXISTING_STRING);
                        } else {
                            configCheckbox.setText(EDIT_CONFIG_STRING);
                        }
                        statusPanel.setMaximumSize(statusPanel.getPreferredSize());
                    }
                });
                setNewConfig(configs[0]);
                if (editableConfig) {
                    updateConfigPanelEditable(false);
                } else {
                    updateConfigPanelExisting();
                }
                hideRetryButton();
                configOk = true;
            }
        }
        if (!configOk) {
            final boolean noConfigsF = noConfigs;
            application.invokeLater(new Runnable() {
                @Override
                public void run() {
                    if (noConfigsF) {
                        configCheckbox.setText(SEE_EXISTING_STRING);
                        configCheckbox.setSelected(false);
                        statusPanel.setMaximumSize(statusPanel.getPreferredSize());
                    } else {
                        configCheckbox.setText(EDIT_CONFIG_STRING);
                        configCheckbox.setSelected(false);
                        statusPanel.setMaximumSize(statusPanel.getPreferredSize());
                    }
                }
            });
            if (noConfigs) {
                updateConfigPanelEditable(false);
            } else {
                updateConfigPanelExisting();
            }
        }
        application.invokeLater(new Runnable() {
            @Override
            public void run() {
                fieldCheckLatch.countDown();
            }
        });
        return configOk;
    }

    /** Shows all ha.cf config files. */
    private void updateConfigPanelExisting() {
        final Host[] hosts = getCluster().getHostsArray();
        application.invokeLater(new Runnable() {
            @Override
            public void run() {
                makeConfigButton.setEnabled(false);
                configPanel.removeAll();
                final JPanel insideConfigPanel = new JPanel(new SpringLayout());
                int cols = 0;
                for (int i = 0; i < hosts.length; i++) {
                    if (HA_CF_ERROR_STRING.equals(configs[i])) {
                        configs[i] = Tools.getString("Dialog.Cluster.HbConfig.NoConfigFound");
                    }
                    final JLabel l = new JLabel(hosts[i].getName() + ':');
                    l.setBackground(Color.WHITE);
                    final JPanel labelP = new JPanel();
                    labelP.setBackground(Tools.getDefaultColor("ConfigDialog.Background"));
                    labelP.setLayout(new BoxLayout(labelP, BoxLayout.PAGE_AXIS));
                    labelP.setAlignmentX(java.awt.Component.TOP_ALIGNMENT);
                    labelP.add(l);
                    insideConfigPanel.add(labelP);
                    final JTextArea ta = new JTextArea(configs[i]);
                    ta.setEditable(false);
                    insideConfigPanel.add(ta);
                    cols += 2;
                }
                if (cols > 0) {
                    SpringUtilities.makeCompactGrid(insideConfigPanel,
                                                    1, cols,
                                                    1, 1,
                                                    1, 1);
                    configPanel.add(insideConfigPanel);
                }
                configPanel.revalidate();
                configPanel.repaint();
            }
        });
    }

    /** Updates the config panel. */
    private void updateConfigPanelEditable(final boolean configChanged) {
        if (configChanged && fieldCheckLatch.getCount() > 0) {
            return;
        }
        this.configChangedByUser = configChanged;
        application.invokeLater(new Runnable() {
            @Override
            public void run() {
                if (!configChanged) {
                    makeConfigButton.setEnabled(false);
                }
                configPanel.removeAll();
                /* head */
                final String[] head = hbConfigHead(true).toString().split(NEWLINE);
                for (final String line : head) {
                    configPanel.add(new JLabel(line));
                }
                /* timeouts */
                for (final String option : OPTIONS) {
                    configPanel.add(getComponentPanel(option, optionsWidgets.get(option).getComponent()));
                }
                configPanel.add(new JLabel(" "));
                if (castAddresses.size() < 2) {
                    final JLabel l;
                    if (castAddresses.size() < 1) {
                        l = new JLabel(Tools.getString("Dialog.Cluster.HbConfig.WarningAtLeastTwoInt"));
                    } else {
                        l = new JLabel(Tools.getString("Dialog.Cluster.HbConfig.WarningAtLeastTwoInt.OneMore"));
                    }
                    l.setForeground(Color.RED);
                    configPanel.add(l);
                    l.addComponentListener(new ComponentListener() {
                        @Override
                        public void componentHidden(final ComponentEvent e) {
                            /* do nothing */
                        }

                        @Override
                        public void componentMoved(final ComponentEvent e) {
                            if (configAlreadyScrolled) {
                                return;
                            }
                            configAlreadyScrolled = true;
                            configScrollPane.getViewport().setViewPosition(l.getBounds().getLocation());
                        }

                        @Override
                        public void componentResized(final ComponentEvent e) {
                            /* do nothing */
                        }

                        @Override
                        public void componentShown(final ComponentEvent e) {
                            /* do nothing */
                        }
                    });
                }
                /* addresses */
                for (final CastAddress c : castAddresses) {
                    configPanel.add(getComponentPanel(c.getConfigString(), getRemoveButton(c)));
                }
                configPanel.add(new JLabel(" "));
                /* mcast etc combo boxes */
                configPanel.add(mcastPanel);
                /* dopd */
                final String[] dopdLines = hbConfigDopd(dopdWidget.isSelected()).toString().split(NEWLINE);
                boolean checkboxDone = false;
                for (final String line : dopdLines) {
                    if (checkboxDone) {
                        configPanel.add(new JLabel(line));
                    } else {
                        configPanel.add(getComponentPanel(line, dopdWidget));
                        checkboxDone = true;
                    }
                }

                /* mgmtd */
                final String[] mgmtdLines = hbConfigMgmtd(mgmtdWidget.isSelected()).toString().split(NEWLINE);
                checkboxDone = false;
                for (final String line : mgmtdLines) {
                    if (checkboxDone) {
                        configPanel.add(new JLabel(line));
                    } else {
                        configPanel.add(getComponentPanel(line, mgmtdWidget));
                        checkboxDone = true;
                    }
                }
                configPanel.revalidate();
                configPanel.repaint();
                if (configChanged) {
                    if (castAddresses.isEmpty()) {
                        makeConfigButton.setEnabled(false);
                    } else {
                        guiData.setAccessible(makeConfigButton, AccessMode.ADMIN);
                    }
                    if (!application.getAutoClusters().isEmpty() && !castAddresses.isEmpty()) {
                        Tools.sleep(1000);
                        makeConfigButton.pressButton();
                    }
                }
            }
        });
    }

    private MyButton getRemoveButton(final CastAddress c) {
        final MyButton removeButton = widgetFactory.createButton(Tools.getString("Dialog.Cluster.HbConfig.RemoveIntButton"));
        removeButton.setBackgroundColor(Tools.getDefaultColor("ConfigDialog.Button"));
        removeButton.setMaximumSize(new Dimension(REMOVE_BUTTON_WIDTH, REMOVE_BUTTON_HEIGHT));
        removeButton.setPreferredSize(new Dimension(REMOVE_BUTTON_WIDTH, REMOVE_BUTTON_HEIGHT));
        removeButton.addActionListener(
            new ActionListener() {
                @Override
                public void actionPerformed(final ActionEvent e) {
                    final Thread t = new Thread(new Runnable() {
                        @Override
                        public void run() {
                            castAddresses.remove(c);
                            updateConfigPanelEditable(true);
                            checkInterface();
                        }
                    });
                    t.start();
                }
            });
        return removeButton;
    }

    /**
     * Checks interface if it already exists and enables/disables the 'add
     * button' accordingly.
     */
    private void checkInterface() {
        final Value type = typeWidget.getValue();
        String addr       = "";
        String iface      = "";
        String serial     = "";

        if (BCAST_TYPE.equals(type)) {
            iface = ifaceWidget.getStringValue();
        } else if (MCAST_TYPE.equals(type)) {
            iface = ifaceWidget.getStringValue();
            addr = addrWidget.getStringValue();
        } else if (SERIAL_TYPE.equals(type)) {
            serial = serialWidget.getStringValue();
        } else if (UCAST_TYPE.equals(type)) {
            final UcastLink ucastLink1 = (UcastLink) ucastLink1Widget.getValue();
            final UcastLink ucastLink2 = (UcastLink) ucastLink2Widget.getValue();
            if (ucastLink1 == null || ucastLink2 == null || ucastLink1.getHost() == ucastLink2.getHost()) {
                application.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        addAddressButton.setEnabled(false);
                    }
                });
                return;
            }
            iface = ucastLink1.getInterface();
            addr = ucastLink2.getIp();
        }

        for (final CastAddress c : castAddresses) {
            if (c.equals(type.getValueForConfig(), iface, addr, serial)) {
                application.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        addAddressButton.setEnabled(false);
                    }
                });
                return;
            }
        }
        application.invokeLater(new Runnable() {
            @Override
            public void run() {
                addAddressButton.setEnabled(true);
            }
        });
    }

    private StringBuilder hbConfigHead(final boolean fake) {
        final StringBuilder config = new StringBuilder(130);
        if (fake) {
            config.append("## to be generated by LCMC ");
        } else {
            config.append("## generated by LCMC ");
        }
        return config;
    }

    /** Returns timeouts. */
    private CharSequence hbConfigOptions() {
        final StringBuilder config = new StringBuilder(130);
        config.append(Tools.getRelease());
        config.append("\n\n");
        for (final String option : OPTIONS) {
            final String value = optionsWidgets.get(option).getStringValue();
            if (value != null && !value.isEmpty()) {
                config.append(option);
                config.append(' ');
                config.append(optionsWidgets.get(option).getStringValue());
                config.append('\n');
            }
        }
        return config;
    }

    private CharSequence hbConfigAddr() {
        final StringBuilder config = new StringBuilder(80);
        for (final CastAddress ca : castAddresses) {
            config.append(ca.getConfigString());
            config.append('\n');
        }
        return config;
    }

    /**
     * Returns the part of the config that turns on dopd. To turn it off, the
     * dopd config is commented out.
     */
    private CharSequence hbConfigDopd(final boolean useDopd) {
        final StringBuilder config = new StringBuilder(120);
        if (!useDopd) {
            config.append("# ");
        }
        config.append("respawn hacluster ");
        final Host[] hosts = getCluster().getHostsArray();
        config.append(hosts[0].getHeartbeatLibPath());
        config.append("/dopd\n");
        if (!useDopd) {
            config.append("# ");
        }
        config.append("apiauth dopd gid=haclient uid=hacluster\n");
        return config;
    }

    /**
     * Returns the part of the config that turns on mgmt. To turn it off, the
     * mgmt config is commented out.
     */
    private CharSequence hbConfigMgmtd(final boolean useMgmt) {
        final StringBuilder config = new StringBuilder(120);
        if (!useMgmt) {
            config.append("# ");
        }
        config.append("respawn root ");
        final Host[] hosts = getCluster().getHostsArray();
        config.append(hosts[0].getHeartbeatLibPath());
        config.append("/mgmtd -v\n");
        if (!useMgmt) {
            config.append("# ");
        }
        config.append("apiauth mgmtd uid=root\n");
        return config;
    }

    /** Adds interface to the config panel. It must be called from a thread. */
    private void addInterface(final Value type) {
        String iface      = "";
        String addr       = "";
        String serial     = "";
        if (MCAST_TYPE.equals(type)) {
            iface  = ifaceWidget.getStringValue();
            addr = addrWidget.getStringValue();
        } else if (BCAST_TYPE.equals(type)) {
            iface  = ifaceWidget.getStringValue();
        } else if (UCAST_TYPE.equals(type)) {
            iface = ((UcastLink) ucastLink1Widget.getValue()).getInterface();
            addr = ((UcastLink) ucastLink2Widget.getValue()).getIp();
        } else if (SERIAL_TYPE.equals(type)) {
            serial = serialWidget.getStringValue();
        }
        castAddresses.add(new CastAddress(type.getValueForConfig(), iface, addr, serial));
        updateConfigPanelEditable(true);
        checkInterface();
    }

    @Override
    protected JComponent getInputPane() {
        optionsWidgets.clear();
        final JPanel pane = new JPanel();
        pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS));
        final Host[] hosts = getCluster().getHostsArray();
        final Value[] types = {MCAST_TYPE, BCAST_TYPE, UCAST_TYPE, SERIAL_TYPE};

        typeWidget = widgetFactory.createInstance(
                                      Widget.GUESS_TYPE,
                                      MCAST_TYPE,
                                      types,
                                      Widget.NO_REGEXP,
                                      TYPE_COMBOBOX_WIDTH,
                                      Widget.NO_ABBRV,
                                      new AccessMode(AccessMode.RO, AccessMode.NORMAL),
                                      Widget.NO_BUTTON);

        final NetInterface[] ni = netInterfaceService.getNetInterfacesWithBridges(hosts[0]);
        NetInterface defaultNi = null;
        for (final NetInterface n : ni) {
            /* skip lo */
            if (!n.isLocalHost()) {
                defaultNi = n;
                break;
            }
        }
        if (defaultNi == null) {
            LOG.appError("getInputPane: " + hosts[0].getName() + ": missing network interfaces");
        }
        ifaceWidget = widgetFactory.createInstance(Widget.Type.COMBOBOX,
                                                   defaultNi,
                                                   ni,
                                                   Widget.NO_REGEXP,
                                                   INTERFACE_COMBOBOX_WIDTH,
                                                   Widget.NO_ABBRV,
                                                   new AccessMode(AccessMode.RO, AccessMode.NORMAL),
                                                   Widget.NO_BUTTON);

        /* ucast links */
        final List<UcastLink> ulList = new ArrayList<UcastLink>();
        for (final Host host : hosts) {
            final NetInterface[] netInterfaces = netInterfaceService.getNetInterfacesWithBridges(host);
            for (final NetInterface n : netInterfaces) {
                ulList.add(new UcastLink(host, n));
            }
        }
        final UcastLink[] ucastLinks = ulList.toArray(new UcastLink[ulList.size()]);

        ucastLink1Widget = widgetFactory.createInstance(
                                             Widget.GUESS_TYPE,
                                             Widget.NO_DEFAULT,
                                             ucastLinks,
                                             Widget.NO_REGEXP,
                                             LINK_COMBOBOX_WIDTH,
                                             Widget.NO_ABBRV,
                                             new AccessMode(AccessMode.RO, AccessMode.NORMAL),
                                             Widget.NO_BUTTON);
        ucastLink2Widget = widgetFactory.createInstance(
                                             Widget.GUESS_TYPE,
                                             Widget.NO_DEFAULT,
                                             ucastLinks,
                                             Widget.NO_REGEXP,
                                             LINK_COMBOBOX_WIDTH,
                                             Widget.NO_ABBRV,
                                             new AccessMode(AccessMode.RO, AccessMode.NORMAL),
                                             Widget.NO_BUTTON);

        /* serial links */
        final Value[] serialDevs = {new StringValue("/dev/ttyS0"),
                                    new StringValue("/dev/ttyS1"),
                                    new StringValue("/dev/ttyS2"),
                                    new StringValue("/dev/ttyS3")};

        serialWidget = widgetFactory.createInstance(
                                             Widget.GUESS_TYPE,
                                             Widget.NO_DEFAULT,
                                             serialDevs,
                                             Widget.NO_REGEXP,
                                             LINK_COMBOBOX_WIDTH,
                                             Widget.NO_ABBRV,
                                             new AccessMode(AccessMode.RO, AccessMode.NORMAL),
                                             Widget.NO_BUTTON);

        /* this matches something like this: 225.0.0.43 694 1 0
         * if you think that the regexp is too complicated for that, consider,
         * that it must match also during the thing is written.
         * TODO: it does not work very good anyway
         */
        final String regexp = "^\\d{1,3}(\\.\\d{0,3}(\\d\\.\\d{0,3}"
                              + "(\\d\\.\\d{0,3})( \\d{0,3}(\\d \\d{0,3}"
                              + "(\\d \\d{0,3})?)?)?)?)?$";
        addrWidget = widgetFactory.createInstance(
                                     Widget.GUESS_TYPE,
                                     new StringValue("239.192.0.0 694 1 0"),
                                     Widget.NO_ITEMS,
                                     regexp,
                                     ADDR_COMBOBOX_WIDTH,
                                     Widget.NO_ABBRV,
                                     new AccessMode(AccessMode.RO, AccessMode.NORMAL),
                                     Widget.NO_BUTTON);

        typeWidget.addListeners(
                new WidgetListener() {
                    @Override
                    public void check(final Value value) {
                        final Value type = typeWidget.getValue();
                        if (type != null) {
                            if (MCAST_TYPE.equals(type) || BCAST_TYPE.equals(type)) {
                                ifaceWidget.setVisible(true);
                            } else {
                                ifaceWidget.setVisible(false);
                            }

                            if (MCAST_TYPE.equals(type)) {
                                addrWidget.setVisible(true);
                            } else {
                                addrWidget.setVisible(false);
                            }
                            if (SERIAL_TYPE.equals(type)) {
                                serialWidget.setVisible(true);
                            } else {
                                serialWidget.setVisible(false);
                            }
                            if (UCAST_TYPE.equals(type)) {
                                ucastLink1Widget.setVisible(true);
                                ucastLink2Widget.setVisible(true);
                            } else {
                                ucastLink1Widget.setVisible(false);
                                ucastLink2Widget.setVisible(false);
                            }
                            application.invokeLater(new Runnable() {
                                @Override
                                public void run() {
                                    mcastPanel.setMaximumSize(mcastPanel.getPreferredSize());
                                }
                            });
                            checkInterface();
                        }
                    }
                });

        ifaceWidget.addListeners(new WidgetListener() {
            @Override
            public void check(final Value value) {
                checkInterface();
            }
        });

        serialWidget.setVisible(false);

        serialWidget.addListeners(new WidgetListener() {
            @Override
            public void check(final Value value) {
                checkInterface();
            }
        });

        ucastLink1Widget.setVisible(false);
        ucastLink2Widget.setVisible(false);

        ucastLink1Widget.addListeners(new WidgetListener() {
            @Override
            public void check(final Value value) {
                checkInterface();
            }
        });
        ucastLink2Widget.addListeners(new WidgetListener() {
            @Override
            public void check(final Value value) {
                checkInterface();
            }
        });
        addrWidget.addListeners(new WidgetListener() {
            @Override
            public void check(final Value value) {
                checkInterface();
            }
        });

        addAddressButton = widgetFactory.createButton(Tools.getString("Dialog.Cluster.HbConfig.AddIntButton"));
        addAddressButton.setBackgroundColor(Tools.getDefaultColor("ConfigDialog.Button"));
        addAddressButton.addActionListener(
                new ActionListener() {
                    @Override
                    public void actionPerformed(final ActionEvent e) {
                        final Value type = typeWidget.getValue();
                        final Thread thread = new Thread(new Runnable() {
                            @Override
                            public void run() {
                                addInterface(type);
                            }
                        });
                        thread.start();
                    }
                });

        configScrollPane = new JScrollPane(configPanel,
                                           JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                                           JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        configScrollPane.setPreferredSize(new Dimension(Short.MAX_VALUE, 150));
        statusPanel = new JPanel();
        statusPanel.add(configStatus);
        configCheckbox = new JCheckBox("-----", true);
        configCheckbox.setBackground(Tools.getDefaultColor("ConfigDialog.Background.Light"));
        guiData.setAccessible(configCheckbox, AccessMode.ADMIN);
        configCheckbox.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(final ItemEvent e) {
                final String text = configCheckbox.getText();
                if (e.getStateChange() == ItemEvent.SELECTED) {
                    final Thread thread = new Thread(new Runnable() {
                        @Override
                        public void run() {
                            if (EDIT_CONFIG_STRING.equals(text)) {
                                updateConfigPanelEditable(configChangedByUser);
                                application.invokeLater(new Runnable() {
                                    @Override
                                    public void run() {
                                        configCheckbox.setText(SEE_EXISTING_STRING);
                                        configCheckbox.setSelected(false);
                                        statusPanel.setMaximumSize(statusPanel.getPreferredSize());
                                    }
                                });
                            } else if (SEE_EXISTING_STRING.equals(text)) {
                                updateConfigPanelExisting();
                                application.invokeLater(new Runnable() {
                                    @Override
                                    public void run() {
                                        configCheckbox.setText(EDIT_CONFIG_STRING);
                                        configCheckbox.setSelected(false);
                                        statusPanel.setMaximumSize(statusPanel.getPreferredSize());
                                    }
                                });
                            }
                        }
                    });
                    thread.start();
                }
            }
        });
        statusPanel.add(configCheckbox);
        statusPanel.setAlignmentX(java.awt.Component.LEFT_ALIGNMENT);
        pane.add(statusPanel);
        pane.add(configScrollPane);
        configScrollPane.setAlignmentX(java.awt.Component.LEFT_ALIGNMENT);
        mcastPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
        mcastPanel.setBackground(Tools.getDefaultColor("ConfigDialog.Background"));
        mcastPanel.add(new JLabel("# "));
        mcastPanel.add(typeWidget.getComponent());
        mcastPanel.add(ifaceWidget.getComponent());
        mcastPanel.add(addrWidget.getComponent());
        mcastPanel.add(serialWidget.getComponent());
        mcastPanel.add(ucastLink1Widget.getComponent());
        mcastPanel.add(ucastLink2Widget.getComponent());
        mcastPanel.add(addAddressButton);
        mcastPanel.setMaximumSize(mcastPanel.getPreferredSize());
        mcastPanel.setAlignmentX(java.awt.Component.LEFT_ALIGNMENT);
        for (final String option : OPTIONS) {
            final int size;
            if (OPTION_SIZES.containsKey(option)) {
                size = OPTION_SIZES.get(option);
            } else {
                size = 40;
            }
            final Widget w = widgetFactory.createInstance(
                    OPTION_WIDGET_TYPES.get(option),
                    optionDefaults.get(option),
                    optionValues.get(option),
                    '^' + OPTION_REGEXPS.get(option) + "\\s*$",
                    size,
                    Widget.NO_ABBRV,
                    new AccessMode(AccessMode.ADMIN, AccessMode.NORMAL),
                    Widget.NO_BUTTON);
            optionsWidgets.put(option, w);
            w.setAlwaysEditable(true);
            w.addListeners(getOptionListener());
        }

        /* dopd */
        dopdWidget = new JCheckBox(Tools.getString("Dialog.Cluster.HbConfig.UseDopdCheckBox"), null, false);
        dopdWidget.setBackground(Tools.getDefaultColor("ConfigDialog.Background"));
        dopdWidget.setToolTipText(Tools.getString("Dialog.Cluster.HbConfig.UseDopdCheckBox.ToolTip"));
        dopdWidget.addItemListener(
                new ItemListener() {
                    @Override
                    public void itemStateChanged(final ItemEvent e) {
                        final Thread thread = new Thread(new Runnable() {
                            @Override
                            public void run() {
                                updateConfigPanelEditable(true);
                            }
                        });
                        thread.start();
                    }
                });

        /* mgmtd */
        mgmtdWidget = new JCheckBox(Tools.getString("Dialog.Cluster.HbConfig.UseMgmtdCheckBox"), null, false);
        mgmtdWidget.setBackground(Tools.getDefaultColor("ConfigDialog.Background"));
        mgmtdWidget.setToolTipText(Tools.getString("Dialog.Cluster.HbConfig.UseMgmtdCheckBox.ToolTip"));
        mgmtdWidget.addItemListener(
                new ItemListener() {
                    @Override
                    public void itemStateChanged(final ItemEvent e) {
                        final Thread thread = new Thread(new Runnable() {
                            @Override
                            public void run() {
                                updateConfigPanelEditable(true);
                            }
                        });
                        thread.start();
                    }
                });
        makeConfigButton.setAlignmentX(java.awt.Component.LEFT_ALIGNMENT);
        pane.add(makeConfigButton);
        return pane;
    }

    @Override
    protected boolean skipButtonEnabled() {
        return true;
    }

    private WidgetListener getOptionListener() {
        return new WidgetListener() {
            @Override
            public void check(final Value value) {
                if (fieldCheckLatch.getCount() > 0) {
                    return;
                }
                for (final String option : OPTIONS) {
                    final Widget w = optionsWidgets.get(option);
                    if (w != null) {
                        if (checkRegexp(w.getRegexp(), w.getStringValue())) {
                            w.setBackground(null, null, true);
                        } else {
                            w.wrongValue();
                        }
                    }
                }
                guiData.setAccessible(makeConfigButton, AccessMode.ADMIN);
            }
        };
    }

    /** Checks regexp. */
    private boolean checkRegexp(final String regexp, final CharSequence value) {
        if (regexp != null) {
            final Pattern p = Pattern.compile(regexp);
            final Matcher m = p.matcher(value);
            return m.matches();
        }
        return true;
    }
}
TOP

Related Classes of lcmc.cluster.ui.wizard.HbConfig

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.