All Downloads are FREE. Search and download functionalities are using the official Maven repository.

oracle.kv.impl.diagnostic.DiagnosticVerifyCommand Maven / Gradle / Ivy

Go to download

NoSQL Database Server - supplies build and runtime support for the server (store) side of the Oracle NoSQL Database.

There is a newer version: 18.3.10
Show newest version
/*-
 * Copyright (C) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle NoSQL
 * Database made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/nosqldb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle NoSQL Database for a copy of the license and
 * additional information.
 */

package oracle.kv.impl.diagnostic;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.InetAddress;
import java.security.KeyStore;
import java.security.KeyStore.PasswordProtection;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

import oracle.kv.KVVersion;
import oracle.kv.impl.admin.param.BootstrapParams;
import oracle.kv.impl.admin.param.SecurityParams;
import oracle.kv.impl.diagnostic.ssh.SSHClient;
import oracle.kv.impl.diagnostic.util.TopologyDetector;
import oracle.kv.impl.param.LoadParameters;
import oracle.kv.impl.param.ParameterState;
import oracle.kv.impl.param.ParameterUtils;
import oracle.kv.impl.security.ssl.KeyStorePasswordSource;
import oracle.kv.impl.topo.StorageNode;
import oracle.kv.impl.topo.StorageNodeId;
import oracle.kv.impl.topo.Topology;
import oracle.kv.impl.util.ConfigUtils;
import oracle.kv.impl.util.FileNames;
import oracle.kv.util.shell.CommandWithSubs;
import oracle.kv.util.shell.Shell;
import oracle.kv.util.shell.ShellException;
import oracle.kv.util.shell.ShellUsageException;

/**
 * This command shell implements the diagnostics verify command. The syntax
 * for the command is: diagnostics verify {-checkLocal | -checkMulti}
 */
public class DiagnosticVerifyCommand extends CommandWithSubs {
    private static final String COMMAND_NAME = "verify";

    private static final String CHECK_LOCAL_SUBCOMMAND_NAME = "-checkLocal";
    private static final String CHECK_MULTI_SUBCOMMAND_NAME = "-checkMulti";

    private static final Set remoteFileSet =
            new CopyOnWriteArraySet();
    private static final Map> IPRemoteMap =
            new ConcurrentHashMap>();

    private static final List subs =
            Arrays.asList(new DiagnosticVerifyLocalSub(),
                          new DiagnosticVerifyMultiSub());

    private static String snHostname = null;
    private static int snPort = -1;
    private static String sshUser = null;
    private static String user = null;
    private static String securityFile = null;
    private static String configdir = null;

    public DiagnosticVerifyCommand() {
        super(subs, COMMAND_NAME, 4, 0);
    }

    @Override
    protected String getCommandOverview() {
        return "The verify command checks that each SN's configuration " +
            eol + "has valid values and is consistent with other nodes in " +
            eol + "the cluster. A sn-target-list file must exist and can be" +
            eol + "created with diagnostics setup or diagnostics verify " +
            eol + "{ -checkLocal | -checkMulti } -host  -port  " +
            eol + "command.";
    }

    /**
     * Set user name a specified SNA Info by history SNA Info
     * @param snaInfo the specified SNA Info which has no user name
     * @param list the list of history SNA Info
     */
    private static void setUserByHistory(SNAInfo snaInfo, List list) {
        for (SNAInfo si : list) {
            /* Assign the user when find the same IP in history list */
            if (si.getIP().equals(snaInfo.getIP())) {
                snaInfo.setSSHUser(si.getSSHUser());
            }
        }
    }

    /**
     * Connect to a kvstore component to find the latest topology and use it to
     * update the sn target file.
     * @throws Exception
     */
    private static void updateSNTargetFile(Shell shell) throws Exception {

        try {
            /*
             * Connect topology and fetch the host name, port and kvroot of
             * all SNAs, then write the info into configuration file.
             */
            TopologyDetector detector = new TopologyDetector(snHostname,
                                                             snPort,
                                                             user,
                                                             securityFile);
            Topology topo = detector.getTopology();
            List list = topo.getSortedStorageNodes();
            DiagnosticConfigFile configFile =
                    new DiagnosticConfigFile(configdir);


            /* Store the history SNA Info to set user for new SNA Info */
            List historyList = configFile.getAllSNAInfo();
            configFile.clear();
            for (StorageNode sn : list) {
                StorageNodeId snId = sn.getStorageNodeId();
                String rootDir = detector.getRootDirPath(sn);

                if (rootDir != null) {
                    SNAInfo snaInfo = new SNAInfo(topo.getKVStoreName(),
                                                  snId.getFullName(),
                                                  sn.getHostname(),
                                                  rootDir);

                    /* Set user for the new SNA info */
                    setUserByHistory(snaInfo, historyList);

                    /*
                     * Set user for the new SNA info by the specified
                     * parameter
                     */
                    if (sshUser != null) {
                        snaInfo.setSSHUser(sshUser);
                    }

                    /* Add new SNA info into configuration file */
                    configFile.add(snaInfo);
                } else {
                    shell.getOutput().println("Cannot get root directory " +
                                    "path for " + snId.getFullName() +
                                    " on host " + sn.getHostname());
                }
            }
        } catch (Exception ex) {
            throw ex;
        }
    }

    static class DiagnosticVerifyLocalSub extends SubCommand {
        DiagnosticVerifyLocalSub() {
            super(CHECK_LOCAL_SUBCOMMAND_NAME, 6);
        }

        @Override
        protected String getCommandSyntax() {
            return COMMAND_NAME + " " + CHECK_LOCAL_SUBCOMMAND_NAME + eolt +
                    "[" + DiagnosticConstants.HOST_FLAG +
                    " ]" + eolt +
                    "[" + DiagnosticConstants.PORT_FLAG +
                    " ]" + eolt +
                    "[" + DiagnosticConstants.SSH_USER_FLAG +
                    " ]" + eolt +
                    "[" + DiagnosticConstants.USER_FLAG +
                    " ]" + eolt +
                    "[" + DiagnosticConstants.SECURITY_FLAG +
                    " ]" + eolt +
                    "[" + DiagnosticConstants.CONFIG_DIRECTORY_FLAG
                    + " ]";
        }

        @Override
        protected String getCommandDescription() {
            return "Verify that each SN's configuration file is valid";
        }

        @Override
        public String execute(String[] args, Shell shell)
            throws ShellException {

            /* Initialize arguments */
            snHostname = null;
            snPort = -1;
            sshUser = null;
            user = null;
            securityFile = null;
            configdir = null;

            Shell.checkHelp(args, this);
            if (args.length > 1) {
                for (int i = 1; i < args.length; i++) {
                    if (DiagnosticConstants.HOST_FLAG.equals(args[i])) {
                        snHostname = Shell.nextArg(args, i++, this);
                    } else if (DiagnosticConstants.PORT_FLAG.equals(args[i])) {
                        String sPort = Shell.nextArg(args, i++, this);
                        snPort = parseInt(sPort);
                        if (snPort <= 0) {
                            String info = DiagnosticConstants.PORT_FLAG +
                                          " should be greater than 0";
                            throw new ShellUsageException(info, this);
                        }
                    } else if (DiagnosticConstants.SSH_USER_FLAG.
                            equals(args[i])) {
                        sshUser = Shell.nextArg(args, i++, this);
                    } else if (DiagnosticConstants.USER_FLAG.equals(args[i])) {
                        user = Shell.nextArg(args, i++, this);
                    } else if (DiagnosticConstants.SECURITY_FLAG.
                            equals(args[i])) {
                        securityFile = Shell.nextArg(args, i++, this);
                    } else if  (DiagnosticConstants.CONFIG_DIRECTORY_FLAG.
                            equals(args[i])) {
                        configdir = Shell.nextArg(args, i++, this);
                    } else {
                        shell.unknownArgument(args[i], this);
                    }
                }
            }

            if (snPort < 0) {
                if (snHostname != null && !snHostname.isEmpty()) {
                    shell.requiredArg(DiagnosticConstants.PORT_FLAG, this);
                }
            } else {
                if (snHostname == null || snHostname.isEmpty()) {
                    shell.requiredArg(DiagnosticConstants.HOST_FLAG, this);
                }
            }

            if (configdir == null || configdir.isEmpty()) {
                configdir = DiagnosticConstants.DEFAULT_WORK_DIR;
            }

            checkLocalConfig(shell);

            return null;
        }

        /**
         * Copy the config.xml files from each SN to this client, and check
         * the properties in each file.
         *
         * @param shell
         * @throws ShellUsageException
         */
        private void checkLocalConfig(Shell shell) throws ShellUsageException {
            try {
                if (snHostname != null) {
                    updateSNTargetFile(shell);
                }
            } catch (Exception ex) {
                /*
                 * Updating configuration file failed when encounter exception,
                 * and the old configuration file is used in the follow
                 * statements to collect log files.
                 */
                if (ex.getMessage() != null &&
                    ex.getMessage().contains("Problem parsing")) {
                    throw new ShellUsageException(ex.getMessage(), this);
                }
                shell.getOutput().println("Updating configuration file " +
                                    "failed: " + ex.getMessage());
            }

            /*
             * Check whether the SN target list in the diagnostic command's
             * configuration file is valid.
             */
            try {
                DiagnosticConfigFile configFile =
                        new DiagnosticConfigFile(configdir);
                configFile.verify();
            } catch (Exception ex) {
                throw new ShellUsageException(ex.getMessage(), this);
            }

            try {
                /* Clear result directory list */
                remoteFileSet.clear();
                IPRemoteMap.clear();

                DiagnosticTaskManager diagnosticTaskManager =
                        new DiagnosticTaskManager(shell);

                DiagnosticSSHTask SSHtask =
                        new DiagnosticSSHTask(configdir, sshUser) {
                    @Override
                    public DiagnosticSSHRunnable
                        getSSHRunnable(SNAInfo snaInfo, SSHClient client,
                                       List taskSNList) {
                        return new ConfigGather(snaInfo, this, client);
                    }
                };

                LocalConfigCheck localConfigCheck = new LocalConfigCheck();

                /* Add tasks into DiagnosticTaskManager */
                diagnosticTaskManager.addTask(SSHtask);
                diagnosticTaskManager.addTask(localConfigCheck);

                /* Execute all tasks in DiagnosticTaskManager one by one */
                diagnosticTaskManager.execute();

                deleteEntireDirectory(new File("tmp"));

            } catch (Exception ex) {
                throw new ShellUsageException(ex.toString(), this);
            }
        }

        /**
         * A subclass of DiagnosticSSHThread. It is to copy xml configuration
         * files from SNAs.
         */
        private class ConfigGather extends DiagnosticSSHRunnable {
            final String HOST = "host";
            final String ROOT_DIR_FLAG = "root directory";
            final String DONT_EXIST = " does not exist";
            final String BLANKSPACE_SIGN = " ";

            public ConfigGather(SNAInfo snaInfo,
                                DiagnosticTask threadOwner,
                                SSHClient client) {
                super(snaInfo, threadOwner, client);
            }

            @Override
            public String doWork() throws Exception {
                String message = DiagnosticConstants.EMPTY_STRING;

                /* Check host is reachable or not */
                String retMsg =
                        ParametersValidator.checkHostname(HOST,
                                                          snaInfo.getHost());

                if (retMsg != null) {
                    message += (DiagnosticConstants.NEW_LINE_TAB + retMsg);
                    return message;
                }

                if (client == null) {
                    message += "Cannot connect " + snaInfo.getHost();
                    return message.trim();
                }

                /* Check root directory exists or not */
                if (!client.checkFile(snaInfo.getRootdir())) {
                    message += (DiagnosticConstants.NEW_LINE_TAB +
                            ROOT_DIR_FLAG + BLANKSPACE_SIGN
                            + snaInfo.getRootdir() + DONT_EXIST);
                }

                if (!message.equals(DiagnosticConstants.EMPTY_STRING)) {
                    return message.trim();
                }

                /* Copy xml configuration file from remote machine */
                File saveFolder = new File("tmp", snaInfo.getStoreName() + "_"
                        + snaInfo.getStorageNodeName() + "_"
                        + snaInfo.getHost());

                List fileList = client.getConfigFile(snaInfo, saveFolder);
                for (File f : fileList) {
                    remoteFileSet.add(new RemoteFile(f, snaInfo));
                }

                message = DiagnosticConstants.NEW_LINE_TAB +
                        "Fetched configuration file from [" +
                        snaInfo.getSNAInfo() + "]";
                return message.trim();
            }
        }

        /**
         * Check whether the properties in each SN's config.xml are consistent.
         * Current checks are:
         *  - confirm that all ports used by the SN are available.
         *  - check that there are a sufficient number of ports in the
         *    HA range
         */
        private class LocalConfigCheck extends DiagnosticTask {
            final String PREFIX_MESSAGE = "\nSN Local Configuration check\n\t";

            @Override
            public void doWork() throws Exception {
                String retMsg = null;
                String message = DiagnosticConstants.EMPTY_STRING;

                if (remoteFileSet.size() == 0) {
                    message = "No configuration file found";
                    /* Notify complete a sub task */
                    notifyCompleteSubTask(PREFIX_MESSAGE + message.trim());
                    return;
                }

                for (RemoteFile rFile : remoteFileSet) {
                    String allMessage = null;
                    BootstrapParams bp =
                            ConfigUtils.
                                getBootstrapParams(rFile.getLocalFile());

                    PortConflictValidator validator =
                            new PortConflictValidator();

                    String title = "Host: " + rFile.getSNAInfo().getHost()
                            + ", config: " + rFile.getSNAInfo().getRootdir()
                            + "/" + rFile.getLocalFile().getName();

                    allMessage = aggregateMessage(allMessage, title, 0);

                    /* Check registry port is already assigned or not */
                    retMsg =
                        validator.check(ParameterState.COMMON_REGISTRY_PORT,
                                        bp.getRegistryPort());
                    allMessage = aggregateMessage(allMessage, retMsg, 1);

                    /* Check HA range ports are already assigned or not */
                    retMsg = validator.check(ParameterState.COMMON_PORTRANGE,
                                             bp.getHAPortRange());
                    allMessage = aggregateMessage(allMessage, retMsg, 1);

                    if (bp.isHostingAdmin()) {
                        /* Check the number of ports in a range is enough */
                        retMsg = ParametersValidator.checkRangePortsNumber
                            (ParameterState.COMMON_PORTRANGE,
                             bp.getHAPortRange(),
                             bp.getCapacity() + 1);
                        allMessage = aggregateMessage(allMessage,
                                                      retMsg, 1);
                    } else {
                        /* Check the number of ports in a range is enough */
                        retMsg = ParametersValidator.checkRangePortsNumber
                            (ParameterState.COMMON_PORTRANGE,
                             bp.getHAPortRange(),
                             bp.getCapacity());
                        allMessage = aggregateMessage(allMessage,
                                                      retMsg, 1);
                    }

                    if (bp.getServicePortRange() != null
                            && !bp.getServicePortRange().isEmpty()) {
                        /*
                         * Check service range ports are already assigned or
                         * not
                         */
                        retMsg = validator.check(
                                    ParameterState.COMMON_SERVICE_PORTRANGE,
                                    bp.getServicePortRange());
                        allMessage = aggregateMessage(allMessage, retMsg, 1);
                    }

                    /* Check trap port is already assigned or not */
                    retMsg = validator.check(
                                ParameterState.COMMON_MGMT_TRAP_PORT,
                                bp.getMgmtTrapPort());
                    allMessage = aggregateMessage(allMessage, retMsg, 1);

                    /* Check poll port is already assigned or not */
                    retMsg = validator.check(
                                ParameterState.COMMON_MGMT_POLL_PORT,
                                bp.getMgmtPollingPort());
                    allMessage = aggregateMessage(allMessage, retMsg, 1);

                    if (allMessage.equals(title)) {
                        allMessage = aggregateMessage(allMessage,
                                                      "SN configuration " +
                                                      "is valid.",
                                                      1);

                    }
                    message += (DiagnosticConstants.NEW_LINE_TAB + allMessage);
                }

                /* Notify complete a sub task */
                notifyCompleteSubTask(PREFIX_MESSAGE + message.trim());
            }

            protected String aggregateMessage(String allMessage,
                                              String addMessage, int level) {
                String TAB = "\t";
                if (addMessage != null && !addMessage.isEmpty()) {
                    if (allMessage == null || allMessage.isEmpty()) {
                        allMessage = addMessage;
                    } else {
                        allMessage += (DiagnosticConstants.NEW_LINE_TAB);
                        /* Different level has different indent */
                        for (int i = 0; i < level; i++) {
                            allMessage += TAB;
                        }
                        allMessage += addMessage;
                    }
                }
                return allMessage;
            }
        }
    }

    /**
     * This class is used to check whether each SN's configuration is
     * consistent with other nodes in the cluster.
     */
    static class DiagnosticVerifyMultiSub extends SubCommand {
        /*
         * Store the generate result directory which save log files from remote
         * machines
         */
        private Map resultMap =
                new ConcurrentHashMap();

        DiagnosticVerifyMultiSub() {
            super(CHECK_MULTI_SUBCOMMAND_NAME, 6);
        }

        @Override
        protected String getCommandSyntax() {
            return COMMAND_NAME + " " + CHECK_MULTI_SUBCOMMAND_NAME + eolt +
                    "[" + DiagnosticConstants.HOST_FLAG +
                    " ]" + eolt +
                    "[" + DiagnosticConstants.PORT_FLAG +
                    " ]" + eolt +
                    "[" + DiagnosticConstants.SSH_USER_FLAG +
                    " ]" + eolt +
                    "[" + DiagnosticConstants.USER_FLAG +
                    " ]" + eolt +
                    "[" + DiagnosticConstants.SECURITY_FLAG +
                    " ]" + eolt +
                    "[" + DiagnosticConstants.CONFIG_DIRECTORY_FLAG
                    + " ]";
        }

        @Override
        protected String getCommandDescription() {
            return "Determine whether each SN's configuration is consistent " +
                "with other members of the cluster";
        }

        @Override
        public String execute(String[] args, Shell shell)
            throws ShellException {

            /* Initialize arguments */
            snHostname = null;
            snPort = -1;
            sshUser = null;
            user = null;
            securityFile = null;
            configdir = null;

            Shell.checkHelp(args, this);
            if (args.length > 1) {
                for (int i = 1; i < args.length; i++) {
                    if (DiagnosticConstants.HOST_FLAG.equals(args[i])) {
                        snHostname = Shell.nextArg(args, i++, this);
                    } else if (DiagnosticConstants.PORT_FLAG.equals(args[i])) {
                        String sPort = Shell.nextArg(args, i++, this);
                        try {
                            snPort = Integer.valueOf(sPort);
                        } catch (NumberFormatException nfe) {
                            invalidArgument(DiagnosticConstants.PORT_FLAG);
                        }

                        if (snPort <= 0) {
                            String info = DiagnosticConstants.PORT_FLAG +
                                          " should be greater than 0";
                            throw new ShellUsageException(info, this);
                        }
                    } else if (DiagnosticConstants.SSH_USER_FLAG.
                            equals(args[i])) {
                        sshUser = Shell.nextArg(args, i++, this);
                    } else if (DiagnosticConstants.USER_FLAG.equals(args[i])) {
                        user = Shell.nextArg(args, i++, this);
                    } else if (DiagnosticConstants.SECURITY_FLAG.
                            equals(args[i])) {
                        securityFile = Shell.nextArg(args, i++, this);
                    } else if (DiagnosticConstants.CONFIG_DIRECTORY_FLAG.
                            equals(args[i])) {
                        configdir = Shell.nextArg(args, i++, this);
                    } else {
                        shell.unknownArgument(args[i], this);
                    }
                }
            }

            if (snPort < 0) {
                if (snHostname != null && !snHostname.isEmpty()) {
                    shell.requiredArg(DiagnosticConstants.PORT_FLAG, this);
                }
            } else {
                if (snHostname == null || snHostname.isEmpty()) {
                    shell.requiredArg(DiagnosticConstants.HOST_FLAG, this);
                }
            }

            if (configdir == null || configdir.isEmpty()) {
                configdir = DiagnosticConstants.DEFAULT_WORK_DIR;
            }

            checkMulti(shell);

            return null;
        }

        /**
         * check whether the environment parameters among all machines which
         * host the SN
         *
         * @param shell
         * @throws ShellUsageException
         */
        private void checkMulti(Shell shell) throws ShellUsageException {
            try {
                if (snHostname != null) {
                    updateSNTargetFile(shell);
                }
            } catch (Exception ex) {
                /*
                 * Updating configuration file failed when encounter exception,
                 * and the old configuration file is used in the follow
                 * statements to collect log files.
                 */
                if (ex.getMessage() != null &&
                    ex.getMessage().contains("Problem parsing")) {
                    throw new ShellUsageException(ex.getMessage(), this);
                }
                shell.getOutput().println("Updating configuration file " +
                                    "failed: " + ex.getMessage());
            }

            /*
             * Check whether the SN target list in the diagnostic command's
             * configuration file is valid.
             */
            try {
                DiagnosticConfigFile configFile =
                        new DiagnosticConfigFile(configdir);
                configFile.verify();
            } catch (Exception ex) {
                throw new ShellUsageException(ex.getMessage(), this);
            }

            try {
                /* Clear result map */
                resultMap.clear();
                remoteFileSet.clear();
                IPRemoteMap.clear();

                DiagnosticTaskManager diagnosticTaskManager =
                        new DiagnosticTaskManager(shell);

                DiagnosticSSHTask SSHtask =
                        new DiagnosticSSHTask(configdir, sshUser) {
                    @Override
                    public DiagnosticSSHRunnable
                        getSSHRunnable(SNAInfo snaInfo, SSHClient client,
                                       List taskSNList) {
                        return new ConfigGather(snaInfo, this, client,
                                                taskSNList);
                    }
                };
                EnvParamsCheck check = new EnvParamsCheck();

                /* Add tasks into DiagnosticTaskManager */
                diagnosticTaskManager.addTask(SSHtask);
                diagnosticTaskManager.addTask(check);

                /* Execute all tasks in DiagnosticTaskManager one by one */
                diagnosticTaskManager.execute();

            } catch (Exception ex) {
                throw new ShellUsageException(ex.toString(), this);
            }
        }

        /**
         * A subclass of DiagnosticSSHThread. It is to copy xml configuration
         * files from SNAs.
         */
        private class ConfigGather extends DiagnosticSSHRunnable {
            final String HOST = "host";
            private List list;

            public ConfigGather(SNAInfo snaInfo,
                                DiagnosticTask threadOwner,
                                SSHClient client,
                                List list) {
                super(snaInfo, threadOwner, client);
                this.list = list;
            }

            @Override
            public String doWork() throws Exception {
                String message = DiagnosticConstants.EMPTY_STRING;

                /* Check host is reachable or not */
                String retMsg = ParametersValidator.
                        checkHostname(HOST, snaInfo.getHost());

                if (retMsg != null) {
                    message += (DiagnosticConstants.NEW_LINE_TAB + retMsg);
                    return message;
                }

                if (client == null) {
                    message += "Cannot connect " + snaInfo.getHost();
                    return message.trim();
                }

                /*
                 * Fetch latency, Java version and the network status of the
                 * machines
                 */
                long latency = client.getTimeLatency();
                JavaVersionVerifier javaVersion = client.getJavaVersion();
                Map map = client.getNetWorkStatus(list);

                File saveFolder = new File("tmp", snaInfo.getStoreName() + "_"
                        + snaInfo.getStorageNodeName() + "_"
                        + snaInfo.getHost());

                resultMap.put(snaInfo.getIP(), new EnvParams(latency,
                                                             javaVersion, map,
                                                             saveFolder));

                /* Get configuration files from SNA hosts */
                List fileList = client.getConfigFile(snaInfo, saveFolder);
                List rFilelist = IPRemoteMap.get(snaInfo.getIP());
                if (rFilelist == null) {
                    rFilelist = new ArrayList();
                    IPRemoteMap.put(snaInfo.getIP(), rFilelist);
                }
                for (File f : fileList) {
                    RemoteFile rf = new RemoteFile(f, snaInfo);
                    remoteFileSet.add(rf);
                    rFilelist.add(rf);
                }

                message = DiagnosticConstants.NEW_LINE_TAB +
                        "Fetched configuration file from [" +
                        snaInfo.getSNAInfo() + "]";
                return message.trim();
            }
        }
        /**
         * This class is used to check the environment parameters of SNAs and
         * the consistency of security policy and KVStore version.
         * The algorithm of computing the clock slew is as follows:
         * 1. Get the latency from client to all SNA hosts.
         * 2. Get the minimum latency and determine the fastest host.
         * 3. Compare the latency of fastest host with other SNA hosts and get
         * the clock slew.
         *
         * The algorithm of compare security policy is as follows:
         * 1. Get the security policy of all SNAs from configuration.
         * 2. Determine whether the SNAs apply security functions.
         * 3. Check whether the secured SNAs uses the same security policy.
         */
        private class EnvParamsCheck extends DiagnosticTask {
            final String PREFIX_MESSAGE = "\nMulti-SNs compatibility check";

            @Override
            public void doWork() throws Exception {
                String message = DiagnosticConstants.EMPTY_STRING;
                String fastestHost = DiagnosticConstants.EMPTY_STRING;
                long minLatency = Long.MAX_VALUE;

                if (remoteFileSet.size() == 0) {
                    message = "No configuration file found";
                    notifyCompleteSubTask(PREFIX_MESSAGE +
                                          DiagnosticConstants.NEW_LINE_TAB +
                                          message);
                    return;
                }

                /* Get the fastest host and minimum latency */
                for (Map.Entry entry :
                    resultMap.entrySet()) {
                    if (entry.getValue().getLatency() < minLatency) {
                        minLatency = entry.getValue().getLatency();
                        fastestHost = entry.getKey().getHostName();
                    }
                }

                /*
                 * Check clock skew, java version and network status among the
                 * machines which host SNAs.
                 */
                String clockSkewMsg = "Clock skew: ";
                String jdkversionMsg = "Java version: ";
                String networkMsg = "Network connection status: ";

                for (Map.Entry entry :
                    resultMap.entrySet()) {
                    if (minLatency != Long.MIN_VALUE) {
                        clockSkewMsg += (DiagnosticConstants.NEW_LINE_TAB + "\t"
                                + (entry.getValue().getLatency() - minLatency)
                                + "ms (" + entry.getKey().getHostName() + " to "
                                + fastestHost + ")");
                    } else {
                        clockSkewMsg += (DiagnosticConstants.NEW_LINE_TAB +
                                "\tCannot get clock skew for (" +
                                entry.getKey().getHostName() + " to "
                                + fastestHost + ")");
                    }

                    jdkversionMsg += (DiagnosticConstants.NEW_LINE_TAB + "\t"
                            + entry.getValue().getJavaVersion() + " ("
                            + entry.getKey().getHostName() + ")");

                    Map map = entry.getValue()
                                                     .getNetworkConnectionMap();
                    for (Map.Entry e : map.entrySet()) {
                        if (e.getValue()) {
                            networkMsg += (DiagnosticConstants.NEW_LINE_TAB
                                    + "\t" + entry.getKey().getHostName()
                                    + " to " + e.getKey().getHost()
                                    + ": connected");
                        } else {
                            networkMsg += (DiagnosticConstants.NEW_LINE_TAB
                                    + "\t" + entry.getKey().getHostName()
                                    + " to " + e.getKey().getHost()
                                    + ": disconnected");
                        }
                    }

                }
                List nonSecuredList = new ArrayList();
                Map> keyMap =
                        new HashMap>();

                Map versionsBeforePreReq =
                        new HashMap();

                Map versionsAfterPreReq =
                        new HashMap();

                /*
                 * Check if the SNs have consistent security policies, and
                 * if their KVstore versions are compatible. For SNs hosted
                 * on the same machine, check that their port ranges do not
                 * overlap.
                 */
                String securityMsg = "Security Policy: ";
                String kvversionMsg = "KVStore version: ";

                String overLapMsg = "Port Ranges: ";
                for (RemoteFile rFile : remoteFileSet) {
                    BootstrapParams bp =
                        ConfigUtils.getBootstrapParams(rFile.getLocalFile());
                    /* Group the security policy of all SNAs */
                    if (bp.getSecurityDir() != null
                            && !bp.getSecurityDir().isEmpty()) {
                        File securityDir =
                                new File(rFile.getLocalFile().getParentFile() +
                                         File.separator + bp.getSecurityDir());
                        File securityConfigPath =
                                new File(securityDir,
                                         FileNames.SECURITY_CONFIG_FILE);
                        LoadParameters lp =
                                LoadParameters.getParameters(securityConfigPath,
                                                             null);
                        SecurityParams sp =
                                new SecurityParams(lp, securityConfigPath);

                        /*
                         * Get private key and based on the private key to
                         * group the security policy
                         */
                        final KeyStorePasswordSource pwdSrc =
                                KeyStorePasswordSource.create(sp);
                        final String keyStoreName =
                                sp.getConfigDir() + File.separator +
                                sp.getKeystoreFile();

                        char[] ksPwd = null;
                        ksPwd = pwdSrc.getPassword();
                        KeyStore keyStore = loadStore(keyStoreName, ksPwd,
                                                      "keystore",
                                                      sp.getKeystoreType());

                        PasswordProtection pwdParam =
                                new PasswordProtection(ksPwd);
                        PrivateKeyEntry pkEntry =
                                (PrivateKeyEntry) keyStore.getEntry("shared",
                                                                    pwdParam);
                        PrivateKey privateKey = pkEntry.getPrivateKey();

                        List list = keyMap.get(privateKey);
                        if (list == null) {
                            list = new ArrayList();
                            keyMap.put(privateKey, list);
                        }
                        list.add(rFile);
                    } else {
                        nonSecuredList.add(rFile);
                    }

                    /* Compare the version of KVStore */
                    if (bp.getSoftwareVersion().
                            compareTo(KVVersion.PREREQUISITE_VERSION) < 0) {
                        versionsBeforePreReq.put(rFile,
                                                 bp.getSoftwareVersion());
                    } else {
                        versionsAfterPreReq.put(rFile,
                                                bp.getSoftwareVersion());
                    }
                }

                /* Build check results of security policy and KVStore version */
                int securityPolicyCount = 0;
                if (!nonSecuredList.isEmpty()) {
                    securityPolicyCount = 1;
                }
                securityPolicyCount += keyMap.size();
                if (securityPolicyCount > 1) {
                    securityMsg += DiagnosticConstants.NEW_LINE_TAB + "\t" +
                            securityPolicyCount +
                            " different security policies are found\n";
                } else if(securityPolicyCount == 1) {
                    securityMsg += DiagnosticConstants.NEW_LINE_TAB + "\t" +
                                "Security policies are consistent\n";
                } else {
                    securityMsg += DiagnosticConstants.NEW_LINE_TAB + "\t" +
                                "No security policy found\n";
                }
                int securityPolicyNO = 1;
                for (Map.Entry> entry :
                    keyMap.entrySet()) {
                    for (RemoteFile rf : entry.getValue()) {
                        securityMsg += DiagnosticConstants.NEW_LINE_TAB + "\t" +
                                rf + " secured " + "(security policy " +
                                securityPolicyNO + ")";
                    }
                    securityPolicyNO++;
                    securityMsg += "\n";
                }

                for (RemoteFile rf : nonSecuredList) {
                    securityMsg += DiagnosticConstants.NEW_LINE_TAB + "\t" +
                            rf + " non-secured";
                }

                if (versionsBeforePreReq.size() > 0 &&
                        versionsAfterPreReq.size() > 0) {
                    kvversionMsg += DiagnosticConstants.NEW_LINE_TAB + "\t" +
                        "There are version incompatibilities between " +
                        "nodes in the cluster. Nodes" +
                        DiagnosticConstants.NEW_LINE_TAB +
                        "\twith versions earlier than the required " +
                        "minimum of " +
                         KVVersion.PREREQUISITE_VERSION.
                         getNumericVersionString() + ":\n";

                    for (Map.Entry entry :
                        versionsBeforePreReq.entrySet()) {
                        kvversionMsg += DiagnosticConstants.NEW_LINE_TAB  +
                                "\t" + entry.getKey() +
                                " " + entry.getValue().getNumericVersionString();
                    }

                    if (versionsBeforePreReq.size() > 0 &&
                            versionsAfterPreReq.size() > 0) {
                        kvversionMsg += "\n";
                        kvversionMsg += DiagnosticConstants.NEW_LINE_TAB +
                                "\tNodes with compatible versions:\n";
                    }

                    for (Map.Entry entry :
                        versionsAfterPreReq.entrySet()) {
                        kvversionMsg += DiagnosticConstants.NEW_LINE_TAB  +
                                "\t" + entry.getKey() +
                                " " + entry.getValue().getNumericVersionString();
                    }
                } else if (versionsBeforePreReq.size() > 0 ||
                        versionsAfterPreReq.size() > 0) {
                    kvversionMsg += DiagnosticConstants.NEW_LINE_TAB +
                            "\tAll nodes have compatible versions:\n";

                    for (Map.Entry entry :
                        versionsBeforePreReq.entrySet()) {
                        kvversionMsg += DiagnosticConstants.NEW_LINE_TAB  +
                                "\t" + entry.getKey() + " " +
                                entry.getValue().getNumericVersionString();
                    }

                    for (Map.Entry entry :
                        versionsAfterPreReq.entrySet()) {
                        kvversionMsg += DiagnosticConstants.NEW_LINE_TAB  +
                                "\t" + entry.getKey() + " " +
                                entry.getValue().getNumericVersionString();
                    }
                }

                /*
                 * Check the overlap ports of two or more SNAs which are
                 * hosted in a same machine
                 */
                Map>> portMap =
                    new HashMap>>();

                /* Get all ports and their associated remote files */
                for (Map.Entry> entry :
                                IPRemoteMap.entrySet()) {
                    portMap.put(entry.getKey(),
                                getAllUsedPorts(entry.getValue()));
                }

                /* Build the result of overlap ports check. */
                boolean isOverlap = false;
                String overLapDetail = new String();
                for (Map.Entry>> entry :
                            portMap.entrySet()) {
                    for (Map.Entry> subEntry :
                        entry.getValue().entrySet()) {
                        if (subEntry.getValue().size() > 1) {
                            isOverlap = true;
                            overLapDetail += DiagnosticConstants.NEW_LINE_TAB +
                                    "\t" + "Host: " +
                                    entry.getKey().getHostName() + ", port: " +
                                    subEntry.getKey() + " is used by";
                            for (RemoteFile rFile : subEntry.getValue()) {
                                overLapDetail +=
                                        DiagnosticConstants.NEW_LINE_TAB
                                        + "\t\t" + rFile.toString() + " ";
                            }
                            overLapDetail = overLapDetail.trim() + "\n";
                        }
                    }
                }

                if (isOverlap) {
                    overLapMsg += DiagnosticConstants.NEW_LINE_TAB +
                            "\tConflicts in port allocation:\n" +
                            DiagnosticConstants.NEW_LINE_TAB +
                            "\t" + overLapDetail;
                } else {
                    overLapMsg += DiagnosticConstants.NEW_LINE_TAB + "\t" +
                        "No conflicts in port ranges for SNs hosted on " +
                        "the same node.";
                }

                /* Delete temporary folder which save the configuration files */
                deleteEntireDirectory(new File("tmp"));

                message += DiagnosticConstants.NEW_LINE_TAB +
                        clockSkewMsg.trim() + "\n" +
                        DiagnosticConstants.NEW_LINE_TAB +
                        jdkversionMsg.trim() +  "\n" +
                        DiagnosticConstants.NEW_LINE_TAB +
                        networkMsg.trim() +  "\n" +
                        DiagnosticConstants.NEW_LINE_TAB +
                        securityMsg.trim() +  "\n" +
                        DiagnosticConstants.NEW_LINE_TAB +
                        kvversionMsg.trim() + "\n" +
                        DiagnosticConstants.NEW_LINE_TAB +
                        overLapMsg;

                message = PREFIX_MESSAGE + DiagnosticConstants.NEW_LINE_TAB +
                        message.trim();
                /* Notify complete a sub task */
                notifyCompleteSubTask(message);
            }

            /**
             * Get all used ports and associated configuration files which is
             * declared to use the ports.
             * @param rFileList
             * @return a map contains port and the remote files used the port.
             */
            private Map>
                                getAllUsedPorts(List rFileList) {
                Map> map =
                        new HashMap>();

                /* Iterate all configuration files and extract all ports */
                for (RemoteFile rFile : rFileList) {
                    BootstrapParams bp =
                        ConfigUtils.getBootstrapParams(rFile.getLocalFile());
                    int registryPort = bp.getRegistryPort();
                    String HAPortRange = bp.getHAPortRange();
                    String servicePortRange = bp.getServicePortRange();
                    int trapPort = bp.getMgmtTrapPort();
                    int pollPort = bp.getMgmtPollingPort();

                    /* Add the port and associated configuration into map */
                    addPort(registryPort, rFile, map);
                    addPort(HAPortRange, rFile, map);

                    if (servicePortRange != null &&
                            !servicePortRange.isEmpty()) {
                        addPort(servicePortRange, rFile, map);
                    }

                    if (trapPort != 0) {
                        addPort(trapPort, rFile, map);
                    }

                    if (pollPort != 0) {
                        addPort(pollPort, rFile, map);
                    }
                }

                return map;
            }

            /**
             * Add a port and the associated configuration file which is
             * declared to use the port into a map.
             * @param port
             * @param rFile
             * @param map
             */
            private void addPort(int port,
                                  RemoteFile rFile,
                                  Map> map) {
                Set rSet = map.get(port);
                if (rSet == null) {
                    rSet = new HashSet();
                    map.put(port, rSet);
                }
                rSet.add(rFile);
            }

            /**
             * Add a port range and the associated configuration file which is
             * declared to use the port into a map.
             * @param port
             * @param rFile
             * @param map
             */
            private void addPort(String rangePorts,
                                  RemoteFile rFile,
                                  Map> map) {
                StringTokenizer tokenizer = new StringTokenizer(rangePorts,
                    ParameterUtils.HELPER_HOST_SEPARATOR);
                int firstHAPort = Integer.parseInt(tokenizer.nextToken());
                int secondHAPort = Integer.parseInt(tokenizer.nextToken());
                for (int i=firstHAPort; i<=secondHAPort; i++) {
                    addPort(i, rFile, map);
                }
            }

            /**
             * Get key store of SN
             * @param storeName
             * @param storePassword
             * @param storeFlavor
             * @param storeType
             * @return key store
             * @throws IllegalArgumentException
             */
            private KeyStore loadStore(String storeName,
                                       char[] storePassword,
                                       String storeFlavor,
                                       String storeType)
                throws IllegalArgumentException {

                if (storeType == null || storeType.isEmpty()) {
                    storeType = KeyStore.getDefaultType();
                }

                final KeyStore ks;
                try {
                    ks = KeyStore.getInstance(storeType);
                } catch (KeyStoreException kse) {
                    throw new IllegalArgumentException(
                        "Unable to find a " + storeFlavor +
                        " instance of type " + storeType, kse);
                }

                final FileInputStream fis;
                try {
                    fis = new FileInputStream(storeName);
                } catch (FileNotFoundException fnfe) {
                    throw new IllegalArgumentException(
                        "Unable to locate specified " + storeFlavor + " " +
                        storeName,
                        fnfe);
                }

                try {
                    ks.load(fis, storePassword);
                } catch (IOException ioe) {
                    throw new IllegalArgumentException(
                        "Error reading from " + storeFlavor + " file " +
                        storeName,
                        ioe);
                } catch (NoSuchAlgorithmException nsae) {
                    throw new IllegalArgumentException(
                        "Unable to check " + storeFlavor + " integrity: " +
                        storeName,
                        nsae);
                } catch (CertificateException ce) {
                    throw new IllegalArgumentException(
                        "Not all certificates could be loaded: " + storeName,
                        ce);
                } finally {
                    try {
                        fis.close();
                    } catch (IOException ioe) {
                        /* ignored */
                    }
                }
                return ks;
            }
        }
    }

    /**
     * Delete a whole specified file
     *
     * @param toDeletedFile specified file
     */
    public static void deleteEntireDirectory(File toDeletedFile) {
        /* return when the folder does not exist */
        if (!toDeletedFile.exists()) {
            return;
        }

        /* Delete the specified file when the file is a file */
        if (toDeletedFile.isFile()) {
            toDeletedFile.delete();
            return;
        }

        /* Iterate all files under the specified directory */
        File[] files = toDeletedFile.listFiles();
        for (File f : files) {
            /* Delete it when f is a file */
            if (f.isFile()) {
                f.delete();
            } else if (f.isDirectory()) {
                /* Recurse the f when it is a directory */
                deleteEntireDirectory(f);
            }
        }
        /* Delete empty directory */
        toDeletedFile.delete();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy