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

io.gem.api.impl.SbkGemBenchmark Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) KMG. All Rights Reserved..
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 */

package io.gem.api.impl;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.gem.api.SshResponse;
import io.gem.api.SshSession;
import io.gem.config.GemConfig;
import io.gem.api.GemBenchmark;
import io.gem.api.RemoteResponse;
import io.gem.api.ConnectionConfig;
import io.gem.params.GemParameters;
import io.sbk.api.Benchmark;
import io.sbk.system.Printer;
import io.state.State;
import lombok.Synchronized;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;

import javax.annotation.concurrent.GuardedBy;
import java.io.File;
import java.io.IOException;
import java.net.ConnectException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * Class SbkGemBenchmark.
 */
final public class SbkGemBenchmark implements GemBenchmark {
    private final Benchmark sbmBenchmark;
    private final GemConfig config;
    private final GemParameters params;
    private final String sbkArgs;
    private final CompletableFuture retFuture;
    private final RemoteResponse[] remoteResults;
    private final ExecutorService executor;
    private final SshSession[] nodes;
    private final ConnectionsMap consMap;

    @GuardedBy("this")
    private State state;

    /**
     * Constructor SbkGemBenchmark is responsible for initializing all values.
     *
     * @param sbmBenchmark  Benchmark
     * @param config        NotNull GemConfig
     * @param params        NotNull GemParameters
     * @param sbkArgs       String
     */
    public SbkGemBenchmark(Benchmark sbmBenchmark, @NotNull GemConfig config, @NotNull GemParameters params, String sbkArgs) {
        this.sbmBenchmark = sbmBenchmark;
        this.config = config;
        this.config.remoteTimeoutSeconds = Long.MAX_VALUE;
        this.params = params;
        this.sbkArgs = sbkArgs;
        this.retFuture = new CompletableFuture<>();
        this.state = State.BEGIN;
        final ConnectionConfig[] connections = params.getConnections();
        if (config.fork) {
            executor = new ForkJoinPool(connections.length + 10);
        } else {
            executor = Executors.newFixedThreadPool(connections.length + 10);
        }
        this.remoteResults = new RemoteResponse[connections.length];
        this.nodes = new SshSession[connections.length];
        for (int i = 0; i < connections.length; i++) {
            nodes[i] = new SshSession(connections[i], executor);
        }
        this.consMap = new ConnectionsMap(connections);
    }

    private static int parseJavaVersion(String text) {
        if (StringUtils.isEmpty(text)) {
            return Integer.MAX_VALUE;
        }
        final String[] tmp = text.split("\"", 2);
        return Integer.parseInt(tmp[1].split("\\.")[0]);
    }

    @Override
    @Synchronized
    @SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
    public CompletableFuture start() throws IOException, InterruptedException, ExecutionException,
            IllegalStateException {
        if (state != State.BEGIN) {
            if (state == State.RUN) {
                Printer.log.warn("SBK GEM Benchmark is already running..");
            } else {
                Printer.log.warn("SBK GEM Benchmark is already shutdown..");
            }
            return retFuture.toCompletableFuture();
        }
        state = State.RUN;
        Printer.log.info("SBK GEM Benchmark Started");
        final CompletableFuture[] cfArray = new CompletableFuture[nodes.length];

        for (int i = 0; i < nodes.length; i++) {
            cfArray[i] = nodes[i].createSessionAsync(config.remoteTimeoutSeconds);
        }
        final CompletableFuture connsFuture = CompletableFuture.allOf(cfArray);

        for (int i = 0; i < config.maxIterations && !connsFuture.isDone(); i++) {
            try {
                connsFuture.get(config.timeoutSeconds, TimeUnit.SECONDS);
            } catch (TimeoutException ex) {
                Printer.log.info("SBK-GEM [" + (i + 1) + "]: Waiting for ssh session to remote hosts timeout");
            }
        }
        if (!connsFuture.isDone() || connsFuture.isCompletedExceptionally()) {
            final String errMsg = "SBK-GEM, remote session failed after " + config.maxIterations + " iterations";
            Printer.log.error(errMsg);
            throw new InterruptedException(errMsg);
        }
        Printer.log.info("SBK-GEM: Ssh session establishment Success..");

        final int javaMajorVersion = Integer.parseInt(System.getProperty("java.runtime.version").
                split("\\.")[0].substring(0, 2));

        final CompletableFuture[] cfResults = new CompletableFuture[nodes.length];
        final SshResponse[] sshResults = new SshResponse[cfArray.length];
        final String cmd = "java -version";
        consMap.reset();
        for (int i = 0; i < nodes.length; i++) {
            if (consMap.isVisited(nodes[i].connection)) {
                cfResults[i] = new CompletableFuture<>();
                SshResponse dummyResponse = new SshResponse(true);
                dummyResponse.returnCode = -1;
                cfResults[i].complete(dummyResponse);
            } else {
                consMap.visit(nodes[i].connection);
                cfResults[i] = nodes[i].runCommandAsync(cmd, true, config.remoteTimeoutSeconds );
            }
        }
        final CompletableFuture ret = CompletableFuture.allOf(cfResults);

        for (int i = 0; i < config.maxIterations && !ret.isDone(); i++) {
            try {
                ret.get(config.timeoutSeconds, TimeUnit.SECONDS);
            } catch (TimeoutException ex) {
                Printer.log.info("SBK-GEM [" + (i + 1) + "]: Waiting for command: " + cmd + " timeout");
            }
        }
        boolean stop = false;
        if (!ret.isDone()) {
            final String errMsg = "SBK-GEM: command: " + cmd + " time out after " + config.maxIterations + " iterations";
            Printer.log.error(errMsg);
            throw new InterruptedException(errMsg);
        } else {
            for (int i = 0; i < cfResults.length; i++) {
                sshResults[i] =  cfResults[i].get();
                if (sshResults[i] != null) {
                    String stdOut = sshResults[i].stdOutputStream.toString();
                    String stdErr = sshResults[i].errOutputStream.toString();
                    if (javaMajorVersion > parseJavaVersion(stdOut) && javaMajorVersion > parseJavaVersion(stdErr)) {
                        Printer.log.info("Java version :" + javaMajorVersion + " , mismatch at : " + nodes[i].connection.getHost());
                        stop = true;
                    }
                }
            }
            fillSshResults(sshResults);
        }

        if (stop) {
            throw new InterruptedException();
        }
        Printer.log.info("SBK-GEM: Matching Java Major Version: " + javaMajorVersion + " Success..");
        final String remoteDir = Paths.get(params.getSbkDir()).getFileName().toString();

        if (params.isCopy()) {

            if (remoteDirectoryDelete()) {
                Printer.log.info("SBK-GEM: Removing the remote directory: '" + remoteDir + "'  Success..");
            } else {
                String errStr = "SBK-GEM: Removing the remote directory: '" + remoteDir + "'  failed..";
                Printer.log.info(errStr);
                throw new InterruptedException(errStr);
            }

            consMap.reset();
            for (int i = 0; i < nodes.length; i++) {
                if (consMap.isVisited(nodes[i].connection)) {
                    cfArray[i] = new CompletableFuture<>();
                    cfArray[i].complete(null);
                } else {
                    consMap.visit(nodes[i].connection);
                    cfArray[i] = nodes[i].runCommandAsync("mkdir -p " + nodes[i].connection.getDir(),
                            true, config.remoteTimeoutSeconds );
                }
            }

            final CompletableFuture mkDirFuture = CompletableFuture.allOf(cfArray);
            for (int i = 0; !mkDirFuture.isDone(); i++) {
                try {
                    mkDirFuture.get(config.timeoutSeconds, TimeUnit.SECONDS);
                } catch (TimeoutException ex) {
                    Printer.log.info("SBK-GEM [" + (i + 1) + "]: Waiting for command 'mkdir -p' timeout");
                }
            }

            if (!mkDirFuture.isDone()) {
                final String errMsg = "SBK-GEM, command:  'mkdir' time out after " + config.maxIterations + " iterations";
                Printer.log.error(errMsg);
                throw new InterruptedException(errMsg);
            }

            Printer.log.info("SBK-GEM: Creating remote directory: '" + remoteDir + "'  Success..");
            consMap.reset();
            for (int i = 0; i < nodes.length; i++) {
                if (consMap.isVisited(nodes[i].connection)) {
                    cfArray[i] = new CompletableFuture<>();
                    cfArray[i].complete(null);
                } else {
                    consMap.visit(nodes[i].connection);
                    cfArray[i] = nodes[i].copyDirectoryAsync(params.getSbkDir(), nodes[i].connection.getDir());
                }
            }
            final CompletableFuture copyCB = CompletableFuture.allOf(cfArray);

            for (int i = 0; !copyCB.isDone(); i++) {
                try {
                    copyCB.get(config.timeoutSeconds, TimeUnit.SECONDS);
                } catch (TimeoutException ex) {
                    Printer.log.info("SBK-GEM [" + (i + 1) + "]: Waiting for copy command to complete");
                }
            }

            if (copyCB.isCompletedExceptionally()) {
                final String errMsg = "SBK-GEM, command:  copy command failed!";
                Printer.log.error(errMsg);
                throw new InterruptedException(errMsg);
            }

            Printer.log.info("Copy SBK application: '" + params.getSbkCommand() + "' to remote nodes Success..");

        } //end of copy

        // start SBM
        sbmBenchmark.start();

        // Start remote SBK instances
        final SshResponse[] sbkResults = new SshResponse[nodes.length];
        final String sbkDir = Paths.get(params.getSbkDir()).getFileName().toString();
        final String sbkCommand = sbkDir + File.separator + params.getSbkCommand() + " " + sbkArgs;
        Printer.log.info("SBK-GEM: Remote SBK command: " + sbkCommand);
        for (int i = 0; i < nodes.length; i++) {
            cfResults[i] = nodes[i].runCommandAsync(nodes[i].connection.getDir() + File.separator + sbkCommand,
                    false, config.remoteTimeoutSeconds );
        }
        final CompletableFuture sbkFuture = CompletableFuture.allOf(cfResults);
        sbkFuture.exceptionally(ex -> {
            shutdown(ex);
            return null;
        });

        sbkFuture.thenAccept(x -> {
            for (int i = 0; i < cfResults.length; i++) {
                try {
                    sbkResults[i] = cfResults[i].get();
                } catch (InterruptedException | ExecutionException e) {
                    throw new RuntimeException(e);
                }
            }
            fillSshResults(sbkResults);
            shutdown(null);
        });

        return retFuture.toCompletableFuture();
    }

    private boolean remoteDirectoryDelete() throws InterruptedException, ConnectException {
        final CompletableFuture[] rmCfArray = new CompletableFuture[nodes.length];
        consMap.reset();
        for (int i = 0; i < nodes.length; i++) {
            if (consMap.isVisited(nodes[i].connection)) {
                rmCfArray[i] = new CompletableFuture<>();
                rmCfArray[i].complete(null);
            } else {
                consMap.visit(nodes[i].connection);
                rmCfArray[i] = nodes[i].runCommandAsync("rm -rf " + nodes[i].connection.getDir(),
                        true, config.remoteTimeoutSeconds );
            }
        }
        final CompletableFuture rmFuture = CompletableFuture.allOf(rmCfArray);

        for (int i = 0; i < config.maxIterations && !rmFuture.isDone(); i++) {
            try {
                rmFuture.get(config.timeoutSeconds, TimeUnit.SECONDS);
            } catch (TimeoutException | ExecutionException ex) {
                Printer.log.info("SBK-GEM [" + (i + 1) + "]: remote directory delete timeout");
            }
        }

        if (!rmFuture.isDone()) {
            final String errMsg = "SBK-GEM: command:  'rm -rf' time out after " + config.maxIterations +
                    " iterations";
            Printer.log.error(errMsg);
            throw new InterruptedException(errMsg);
        }
        return true;
    }

    @Synchronized
    private void fillSshResults(SshResponse[] responseStreams) {
        final ConnectionConfig[] connections = params.getConnections();
        for (int i = 0; i < remoteResults.length; i++) {
            remoteResults[i] = new RemoteResponse(responseStreams[i].returnCode, responseStreams[i].stdOutputStream.toString(),
                    responseStreams[i].errOutputStream.toString(), connections[i].getHost());
        }
    }

    /**
     * Shutdown SBK Benchmark.
     *
     * closes all writers/readers.
     * closes the storage device/client.
     *
     * @param ex Throwable exception
     */
    @Synchronized
    private void shutdown(Throwable ex) {
        if (state != State.END) {
            state = State.END;
            if (params.isDelete()) {
                try {
                    remoteDirectoryDelete();
                } catch (InterruptedException | ConnectException rmEx) {
                    rmEx.printStackTrace();
                }
            }
            for (SshSession node : nodes) {
                node.stop();
            }
            sbmBenchmark.stop();
            if (ex != null) {
                Printer.log.warn("SBK GEM Benchmark Shutdown with Exception " + ex);
                retFuture.completeExceptionally(ex);
            } else {
                Printer.log.info("SBK GEM Benchmark Shutdown");
                retFuture.complete(remoteResults);
            }
        }
    }

    @Override
    public void stop() {
        shutdown(null);
    }

    private final static class ConnectionsMap {
        private final Map, Boolean> kMap;

        public ConnectionsMap(@NotNull ConnectionConfig[] conn) {
            this.kMap = new HashMap<>();
            for (ConnectionConfig connectionConfig : conn) {
                this.kMap.put(Map.entry(connectionConfig.getHost().toLowerCase(), connectionConfig.getDir().toLowerCase()), false);
            }
        }

        void reset() {
            this.kMap.keySet().forEach(k -> this.kMap.put(k, false));
        }

        void visit(@NotNull ConnectionConfig conn) {
            this.kMap.put(Map.entry(conn.getHost().toLowerCase(), conn.getDir().toLowerCase()), true);
        }

        boolean isVisited(@NotNull ConnectionConfig conn) {
            return this.kMap.get(Map.entry(conn.getHost().toLowerCase(), conn.getDir().toLowerCase()));
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy