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

com.github.jjYBdx4IL.utils.vmmgmt.VMControl Maven / Gradle / Ivy

/*
 * Copyright (C) 2016 jjYBdx4IL (https://github.com/jjYBdx4IL)
 *
 * 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
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.jjYBdx4IL.utils.vmmgmt;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;

import org.libvirt.Connect;
import org.libvirt.Domain;
import org.libvirt.LibvirtException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import freemarker.template.TemplateException;

/**
 *
 * @author jjYBdx4IL
 */
public class VMControl {

    private static final Logger log = LoggerFactory.getLogger(VMControl.class);
    public static final int vmOperationTimeoutSecs = 300;
    public static final long VM_STATUS_POLL_IVAL_MILLIS = 1000;
    public static final int MAX_START_TRIES = 10;
    private final VMData vm;
    private final Connect virtConn;
    private Domain domain = null;

    public VMControl(Connect conn, VMData vm) {
        this.virtConn = conn;
        this.vm = vm;
    }

    public Domain startVM(BootMode bootMode) throws IOException, LibvirtException, TemplateException {
        int ntries = 0;
        while(true) {
            ntries++;
            try {
                domain = virtConn.domainCreateXML(
                        vm.getDomainCreateXML(bootMode),
                        vm.isAutoDestroy() ? 2 : 0); // 2 == auto-destroy on connection lost
                return domain;
            } catch (LibvirtException ex) {
                if (ntries >= MAX_START_TRIES) {
                    throw ex;
                }
                log.info(String.format("failed to start VM, it follows retry no %d/%d", ntries, MAX_START_TRIES-1));
                vm.randomizePorts();
            }
        }
    }

    public void waitUntilVMHasStoppedByName(int timeoutSecs) {
        long timeout = System.currentTimeMillis() + timeoutSecs * 1000L;
        do {
            try {
                Thread.sleep(VM_STATUS_POLL_IVAL_MILLIS);
            } catch (InterruptedException ex) {}
        } while (isVMRunningByName() && System.currentTimeMillis() < timeout);
        if (isVMRunningByName()) {
            throw new RuntimeException("waiting for VM termination failed: " + vm.getName());
        }
    }

    public boolean isVMRunningByName() {
        try {
            virtConn.domainLookupByName(vm.getName());
            return true;
        } catch(LibvirtException ex) {}
        return false;
    }

    public void destroyVMByName() {
        try {
            Domain domain = virtConn.domainLookupByName(vm.getName());
            domain.destroy();
        } catch(LibvirtException ex) {}
        waitUntilVMHasStoppedByName(vmOperationTimeoutSecs);
    }

    public void sshExec(String cmd) throws IOException {
        final SSHClient ssh = new SSHClient();
        ssh.addHostKeyVerifier(new PromiscuousVerifier());
        try {
            ssh.connect("localhost", vm.getSshForwardPort());
            ssh.authPassword("root", "");
            try (Session session = ssh.startSession()) {
                final Session.Command sessionCmd = session.exec(cmd);
                sessionCmd.join(5, TimeUnit.SECONDS);
                int exitCode = sessionCmd.getExitStatus();
                if (exitCode != 0) {
                    throw new RuntimeException("command " + cmd + " returned exit status code " + exitCode);
                }
            }
        } finally {
            if (ssh.isConnected()) {
                ssh.disconnect();
            }
        }
    }

    public void waitVMOnlineBySSH() {
        long timeout = System.currentTimeMillis() + vmOperationTimeoutSecs * 1000L;
        do {
            try {
                Thread.sleep(VM_STATUS_POLL_IVAL_MILLIS);
            } catch (InterruptedException ex) {}
        } while (!isVMOnlineBySSH() && System.currentTimeMillis() < timeout);
        if (!isVMOnlineBySSH()) {
            throw new RuntimeException("waiting for VM to go online failed: " + vm.getName());
        }
    }

    /**
     * Never returns null. Throws RuntimeException on timeout.
     * @return
     */
    public SSHResources waitForSSHSession() {
        long timeout = System.currentTimeMillis() + vmOperationTimeoutSecs * 1000L;
        SSHResources sshResources = null;
        do {
            try {
                Thread.sleep(VM_STATUS_POLL_IVAL_MILLIS);
            } catch (InterruptedException ex) {}
            try {
                sshResources = tryGetSSHSession();
            } catch (IOException ex) {
            }
        } while (sshResources == null && System.currentTimeMillis() < timeout);
        if (sshResources == null) {
            throw new RuntimeException("waiting for VM to go online failed: " + vm.getName());
        }
        return sshResources;
    }

    public boolean isVMOnlineBySSH() {
        try {
            sshExec("true");
            return true;
        } catch (Exception ex) {}
        return false;
    }

    void shutdownAndWait() throws LibvirtException {
        domain.shutdown();
        waitUntilVMHasStoppedByName(vmOperationTimeoutSecs);
    }

    class SSHResources {
        final SSHClient ssh;
        final Session session;
        SSHResources(SSHClient ssh, Session session) {
            this.ssh = ssh;
            this.session = session;
        }

        void close() {
            if (ssh.isConnected()) {
                if (session != null) {
                    try {
                        session.close();
                    } catch (ConnectionException | TransportException ex) {
                    }
                }
                try {
                    ssh.disconnect();
                } catch (IOException ex) {
                }
            }
        }
    }

    private SSHResources tryGetSSHSession() throws IOException {
        final SSHClient ssh = new SSHClient();
        Session session = null;
        try {
            ssh.addHostKeyVerifier(new PromiscuousVerifier());
            ssh.connect("localhost", vm.getSshForwardPort());
            ssh.authPassword("root", "");
            session = ssh.startSession();
            return new SSHResources(ssh, session);
        } catch (Throwable ex) {
            if (session != null) {
                try {
                    session.close();
                } catch (Throwable ex1) {
                }
            }
            if (ssh.isConnected()) {
                ssh.disconnect();
            }
            throw ex;
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy