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

org.voltdb.sysprocs.CheckUpgradePlanNT Maven / Gradle / Ivy

There is a newer version: 10.1.1
Show newest version
/* This file is part of VoltDB.
 * Copyright (C) 2008-2020 VoltDB Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with VoltDB.  If not, see .
 */

package org.voltdb.sysprocs;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import org.voltdb.CatalogContext;
import org.voltdb.VoltDB;
import org.voltdb.VoltNTSystemProcedure;
import org.voltdb.VoltTable;
import org.voltdb.VoltTable.ColumnInfo;
import org.voltdb.VoltType;
import org.voltdb.catalog.Table;
import org.voltdb.client.ClientResponse;
import org.voltdb.compiler.deploymentfile.DrRoleType;
import org.voltdb.licensetool.LicenseApi;
import org.voltdb.utils.CatalogUtil;

/*
 * Some simple file system path checks, the goal is using NT procedure to do the check on
 * every node before doing the online upgrade.
 */
public class CheckUpgradePlanNT extends VoltNTSystemProcedure {
    private final static String SUCCESS = "Success";
    private final static int MINIMUM_MAJOR_VERSION = 7;
    private final static int MINIMUM_MINOR_VERSION = 2;

    public static class PrerequisitesCheckNT extends VoltNTSystemProcedure {

        public VoltTable run(String newKitPath, String newRootPath) throws InterruptedException, ExecutionException {
            String ret = checkVoltDBKitExistence(newKitPath);
            String ret2 = checkVoltDBRootExistence(newRootPath);
            String ret3 = validateXDCRRequirement();
            String warning = checkWarnings();
            VoltTable vt = new VoltTable(
                    new ColumnInfo[] { new ColumnInfo("KIT_CHECK_RESULT", VoltType.STRING),
                                       new ColumnInfo("ROOT_CHECK_RESULT", VoltType.STRING),
                                       new ColumnInfo("XDCR_CHECK_RESULT", VoltType.STRING),
                                       new ColumnInfo("WARNINGS", VoltType.STRING)});
            vt.addRow(ret, ret2, ret3, warning);
            return vt;
        }

        private static String checkVoltDBKitExistence(String newKitPath) {
            Path newKit = Paths.get(newKitPath);
            if (!Files.exists(newKit)) {
                return newKitPath + " doesn't exist.";
            } else if (!Files.isDirectory(newKit)) {
                return newKitPath + " is not a directory.";
            }
            // Check the new VoltDB kit version
            int[] newKitVersion = new int[2];
            try {
                String version = new String(Files.readAllBytes(Paths.get(newKitPath, "version.txt")));
                String cause = checkVersionString(version, newKitPath, newKitVersion);
                if (cause != null) {
                    return cause;
                }
            } catch (IOException | NumberFormatException e) {
                return "Failed to parse version string in the new VoltDB kit";
            }

            // Check version of current VoltDB instance
            int[] currentVersion = new int[2];
            try {
                String cause = checkVersionString(VoltDB.instance().getVersionString(), null, currentVersion);
                if (cause != null) {
                    return cause;
                }
            } catch (NumberFormatException e) {
                return "Failed to parse version of target VoltDB cluster";
            }

            // Check whether upgrade/downgrade across two major versions
            if (Math.abs(newKitVersion[0] - currentVersion[0]) >= 2) {
                return String.format("Online upgrade/downgrade across two major versions (%d.%d -> %d.%d) is not supported.",
                        currentVersion[0], currentVersion[1], newKitVersion[0], newKitVersion[1]);
            }

            return SUCCESS;
        }

        private static String checkVoltDBRootExistence(String newRootPath) {
            Path newRoot = Paths.get(newRootPath);
            if (!Files.exists(newRoot)) {
                return newRoot + " doesn't exist.";
            } else if (!Files.isDirectory(newRoot)) {
                return newRoot + " is not a directory.";
            }
            return SUCCESS;
        }

        private static String validateXDCRRequirement() {
            LicenseApi licenseApi = VoltDB.instance().getLicenseApi();
            if (!licenseApi.isDrActiveActiveAllowed()) {
                return "Target VoltDB cluster doesn't have a valid XDCR license.";
            }

            CatalogContext context = VoltDB.instance().getCatalogContext();
            if (context.getDeployment().getDr() == null || context.getDeployment().getDr().getRole() != DrRoleType.XDCR) {
                return "Target VoltDB cluster must have XDCR enabled (set role=\"xdcr\" under DR tag of the deployment file).";
            }

            return SUCCESS;
        }

        /*
         * Create warnings if
         * 1) not all the user tables are DR table, or
         * 2) cluster runs on XDCR mode but is not listening on the DR port.
         */
        private static String checkWarnings() {
            StringBuilder warning = new StringBuilder();
            CatalogContext context = VoltDB.instance().getCatalogContext();
            if (context.getDeployment().getDr() != null && context.getDeployment().getDr().isListen() == false) {
                warning.append("Target VoltDB cluster is not listening on DR port.(set listen=\"true\" under DR tag of the deployment file)\n");
            }

            for (Table tb : context.database.getTables()) {
                if (!tb.getTypeName().equals(CatalogUtil.DR_CONFLICTS_PARTITIONED_EXPORT_TABLE)         /* skip conflict export table */
                        && !tb.getTypeName().equals(CatalogUtil.DR_CONFLICTS_REPLICATED_EXPORT_TABLE)
                        && tb.getMaterializer() == null                                                 /* skip view table */
                        && !tb.getIsdred()) {
                    warning.append(tb.getTypeName()).append(" is not a DR table.").append("\n");
                }
            }

            if (warning.length() != 0) {
                return warning.substring(0, warning.length() - 1); // get rid of the last '\n'
            }
            return null;
        }

        private static String checkVersionString(String ver, String newKitPath, int[] versionNumber) throws NumberFormatException {
            String[] versions = ver.split("\\.");
            if (newKitPath != null && versionNumber.length < 2) {
                return "Illegal version string format found in " + newKitPath + ": " + ver;
            }
            int majorVersion = Integer.parseInt(versions[0].trim());
            int minorVersion = Integer.parseInt(versions[1].trim());
            if ( majorVersion < MINIMUM_MAJOR_VERSION ||
                            (majorVersion == MINIMUM_MAJOR_VERSION && minorVersion < MINIMUM_MINOR_VERSION)) {
                if (newKitPath != null) {
                    return "Version of new VoltDB kit is lower than the minimum supported version (v7.2)";
                }
                return "Version of target VoltDB cluster is lower than the minimum supported version (v7.2)";
            }
            versionNumber[0] = majorVersion;
            versionNumber[1] = minorVersion;
            return null;
        }
    }

    // Be user-friendly, return reasons of all failed checks.
    private static String[] aggregatePerHostResults(VoltTable vtable) {
        String[] ret = new String[2];
        vtable.advanceRow();
        String kitCheckResult = vtable.getString("KIT_CHECK_RESULT");
        String rootCheckResult = vtable.getString("ROOT_CHECK_RESULT");
        String xdcrCheckResult = vtable.getString("XDCR_CHECK_RESULT");
        StringBuilder result = new StringBuilder();
        if (!kitCheckResult.equals(SUCCESS)) {
            result.append(kitCheckResult).append("\n");
        }
        if (!rootCheckResult.equals(SUCCESS)) {
            result.append(rootCheckResult).append("\n");
        }
        if (!xdcrCheckResult.equals(SUCCESS)) {
            result.append(xdcrCheckResult);
        }
        if (result.length() == 0) {
            result.append(SUCCESS);
        }

        ret[0] = result.toString();
        String warnings = vtable.getString("WARNINGS");
        if (warnings != null) {
            ret[1] = warnings;
        }

        return ret;
    }

    public VoltTable run(String newKitPath, String newRootPath) throws InterruptedException, ExecutionException {

        CompletableFuture> pf = callNTProcedureOnAllHosts("@PrerequisitesCheckNT", newKitPath, newRootPath);
        Map cr = pf.get();
        VoltTable vt = new VoltTable(
                new ColumnInfo[] { new ColumnInfo("HOST_ID", VoltType.INTEGER),
                                   new ColumnInfo("CHECK_RESULT", VoltType.STRING),
                                   new ColumnInfo("WARNINGS", VoltType.STRING)});
        cr.entrySet().stream()
        .forEach(e -> {
            String[] ret = aggregatePerHostResults(e.getValue().getResults()[0]);
            vt.addRow(e.getKey(), ret[0], ret[1]);
        });

        return vt;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy