package betsy.bpel.virtual.host.virtualbox; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import betsy.bpel.virtual.host.VirtualBoxMachine; import betsy.bpel.virtual.host.exceptions.vm.VirtualMachineNotFoundException; import betsy.common.tasks.FileTasks; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.virtualbox_4_2.CleanupMode; import org.virtualbox_4_2.IAppliance; import org.virtualbox_4_2.IMachine; import org.virtualbox_4_2.IMedium; import org.virtualbox_4_2.ISession; import org.virtualbox_4_2.IVirtualBox; import org.virtualbox_4_2.LockType; import org.virtualbox_4_2.VBoxException; import org.virtualbox_4_2.VirtualBoxManager; /** * The {@link VBoxController} establishes the connection between betsy and * VirtualBox. It can be used to resolve machines, import or delete them and to * adapt some settings of VirtualBox. * * @author Cedric Roeck * @version 1.0 */ public class VBoxController { public static final String BETSY_VBOX_GROUP = "/betsy-engines"; private static final Logger log = Logger.getLogger(VBoxController.class); private final Map<String, VirtualBoxMachineImpl> virtualMachines = new HashMap<>(); private final IVirtualBox vBox; private final VBoxApplianceImporter vBoxImporter; private final VirtualBoxManager vBoxManager; public VBoxController() { log.trace("Initializing VBoxController"); VBoxConnector vBoxConn = new VBoxConnector(); this.vBox = vBoxConn.connect(); this.vBoxManager = vBoxConn.getVBoxManager(); this.vBoxImporter = vBoxConn.getVBoxImporter(); log.trace("VirtualBoxController initialized"); } /** * Import the EngineExtended's virtualMachine from the given file. * * @param vmName desired name of the virtualMachine * @param engineName name of the engine the new VM belongs to * @param importFile file of the appliance to import */ public void importVirtualMachine(final String vmName, final String engineName, final Path importFile) { if (StringUtils.isBlank(vmName)) { throw new IllegalArgumentException( "The name of the vm to import must not be null or empty"); } if (StringUtils.isBlank(engineName)) { throw new IllegalArgumentException( "The name of the engine to import must not be null or empty"); } Objects.requireNonNull(importFile, "The file to import must not be null"); IMachine importedVm = null; ISession session = null; try { log.info("Importing appliance from file " + importFile); IAppliance appliance = vBoxImporter.importAppliance(importFile); log.info("Appliance imported (machines: " + appliance.getMachines() + ")"); if(appliance.getMachines().isEmpty()){ throw new IllegalStateException("The appliance " + importFile + " has no machines inside"); } // by definition the appliance container could contain several // separated machines which must be imported each at it's own. for (String uuid : appliance.getMachines()) { importedVm = vBox.findMachine(uuid); // acquire session lock session = vBoxManager.getSessionObject(); importedVm.lockMachine(session, LockType.Write); IMachine lockedVM = session.getMachine(); vBoxImporter.adjustApplianceSettings(lockedVM, vmName); try { session.unlockMachine(); session = null; } catch (VBoxException exception) { // ignore if was not locked log.debug("Failed to unlock session after import"); } } // END FOR ITERATION } catch (VBoxException exception) { // session must be unlocked for deleting the vm if (session != null) { try { session.unlockMachine(); } catch (VBoxException ignore) { log.debug("Failed to unlock session after import exception", ignore); // ignore if was not locked } } if (importedVm != null) { log.debug("Exception during import, delete VM again."); // Error --> delete VM again this.deleteMachine(importedVm); } log.warn("Unexpected import exception:", exception); } } /** * Get the {@link VirtualBoxMachineImpl} of betsy with the given name. * * @param name name of the VirtualMachine to get * @return VirtualBoxMachine with the searched name * @throws VirtualMachineNotFoundException thrown if there is no VirtualMachine with this name */ public VirtualBoxMachine getVirtualMachine(final String name) throws VirtualMachineNotFoundException { if (virtualMachines.containsKey(name)) { return virtualMachines.get(name); } else { VirtualBoxMachineImpl vm = new VirtualBoxMachineImpl(vBoxManager, getMachine(name)); virtualMachines.put(name, vm); return vm; } } /** * Get the {@link IMachine} of VirtualBox with the given name. * * @param name name of the IMachine to get * @return IMachine with the searched name * @throws VirtualMachineNotFoundException thrown if there is no IMachine with this name */ public IMachine getMachine(final String name) throws VirtualMachineNotFoundException { if (StringUtils.isBlank(name)) { throw new IllegalArgumentException( "name of the virtual machine must not be null or empty"); } List<String> groups = new LinkedList<>(); groups.add(BETSY_VBOX_GROUP); List<IMachine> machines = vBox.getMachinesByGroups(groups); for (IMachine machine : machines) { if (machine.getName().equals(name)) { return machine; } } throw new VirtualMachineNotFoundException("VirtualMachine with name '" + name + "' could not be found in group " + BETSY_VBOX_GROUP); } private void deleteMachine(final IMachine machine) { Path logFolder = Paths.get(machine.getLogFolder()); FileTasks.deleteDirectory(logFolder); List<IMedium> removableMediums = machine.unregister(CleanupMode.Full); machine.delete(removableMediums); } }