package betsy.bpel.virtual.host.virtualbox; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Set; import betsy.bpel.virtual.host.ServiceAddress; import betsy.bpel.virtual.host.VirtualBoxException; import betsy.bpel.virtual.host.VirtualBoxMachine; import betsy.bpel.virtual.host.engines.EngineNamingConstants; import betsy.bpel.virtual.host.exceptions.VirtualEngineServiceException; import betsy.bpel.virtual.host.virtualbox.utils.ServiceValidator; import betsy.bpel.virtual.host.virtualbox.utils.port.PortUsageException; import betsy.bpel.virtual.host.virtualbox.utils.port.PortVerifier; import betsy.common.tasks.WaitTasks; import betsy.common.timeouts.timeout.Timeout; import betsy.common.timeouts.timeout.TimeoutRepository; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; public class SnapshotCreator { private static final Logger log = Logger.getLogger(SnapshotCreator.class); private final VirtualBoxMachine virtualBoxMachine; private final String engineName; private final List<ServiceAddress> engineServices; private final Set<Integer> forwardingPorts; public SnapshotCreator(VirtualBoxMachine virtualBoxMachine, String engineName, List<ServiceAddress> engineServices, Set<Integer> forwardingPorts) { this.virtualBoxMachine = virtualBoxMachine; this.engineName = engineName; this.engineServices = engineServices; this.forwardingPorts = forwardingPorts; } public void invoke() throws VirtualBoxException { if (virtualBoxMachine.hasRunningSnapshot()) { return; } if (StringUtils.isBlank(engineName)) { throw new IllegalArgumentException("The name of the engine to " + "import must not be blank"); } if (engineServices == null || engineServices.isEmpty()) { throw new IllegalArgumentException("The list of services to verify" + " if a vm has been started must not be null or empty"); } if (virtualBoxMachine.isActive()) { throw new IllegalStateException("Can't create a running snapshot " + "if the VM is already active. The VM should be " + "poweredOff!"); } try { PortVerifier.failForUsedPorts(forwardingPorts); } catch (PortUsageException e) { throw new VirtualBoxException("ports could not be forwarded", e); } log.debug("Create running-state snapshot"); try { // configure virtualBoxMachine.applyPortForwarding(forwardingPorts); virtualBoxMachine.start(); // ensure all is up and running failIfEngineServicesTimeout(engineName, engineServices); failIfBetsyServerTimesOut(); WaitTasks.sleep(TimeoutRepository.getTimeout("SnapshotCreator.invoke").getTimeoutInMs()); virtualBoxMachine.takeSnapshot(createSnapshotName(engineName), createSnapshotDescription()); virtualBoxMachine.stop(); } finally { // stop if vm is still running if (virtualBoxMachine.isRunning()) { log.warn("VM was still running, stop now"); virtualBoxMachine.stop(); } } } private void failIfEngineServicesTimeout(String engineName, List<ServiceAddress> engineServices) throws VirtualEngineServiceException { try { if (!ServiceValidator.isEngineReady(engineServices, getServiceTimeout(engineName))) { log.warn("engine services not found withing " + getServiceTimeout(engineName).getTimeoutInSeconds() + " seconds"); // timeout in CountDownLatch throw new VirtualEngineServiceException( "The required services for the engine were " + "not available within " + getServiceTimeout(engineName).getTimeoutInSeconds() + "s after starting the vm. If using a debian/" + "ubuntu system, make sure to delete the " + "'/etc/udev/rules.d/70-persistent-net.rules'" + "file before exporting the appliance. " + "If this error occurs " + "repeatedly, please import the vm manually" + " with a valid snapshot in 'Running' state."); } } catch (Exception e) { throw new VirtualEngineServiceException(e); } } private Timeout getServiceTimeout(String engineName) { return TimeoutRepository.getTimeout(engineName + ".service"); } private void failIfBetsyServerTimesOut() throws VirtualEngineServiceException { if (!ServiceValidator.isBetsyServerReady(TimeoutRepository.getTimeout("SnapshotCreator.failIfBetsyServerTimesOut").getTimeoutInMs())) { log.warn("betsy server not found withing " + TimeoutRepository.getTimeout("SnapshotCreator.failIfBetsyServerTimesOut").getTimeoutInSeconds() + "s"); throw new VirtualEngineServiceException( "The required betsy server was " + "not available within 15s " + "after having found all other services. "); } } private String createSnapshotName(String engineName) { return EngineNamingConstants.VIRTUAL_NAME_PREFIX + engineName + "_import-snapshot"; } private String createSnapshotDescription() { Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("EEE, d MMM yyyy HH:mm"); return "Machine is in 'saved' state. Snapshot created during import on " + sdf.format(date); } }