Skip to content

Snapshot suppert Rebased to 0.8 #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions README.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,33 @@
If you're wanting to make changes, please clone the git repository at
This is an experimental branch for Virtualbox Snapshot testing.

git://github.com/jenkinsci/virtualbox-plugin.git

If you create a snapshot in a virtual machine this snapshot will be listed in the Jenkins node configuration at the Virtual Machine Name as <machineName>//<Snapshot>. It is also possible to create snapshot trees. In this case the Virtual Machine Name is build as <machineName>//<Snapshot>/<Snapshot2>.

When the virtualbox-plugin starts and stops the virtual machine the selected snapshot is restored. This allows you to setup a virtual machine in a defined statewith e.g. test data.

Note: An error will be caused, if the virtual machine is relaunched while it already runs. Therefore the virtual machine has to be stopped (maybe the plugin can do this in one of the next releases). To avoid this, set the Availability in the Jenkins node configuration to 'Take this slave on-line when in demand and off-line when idle', 'In demand delay' and 'Idle delay' to 1, and add a label which is also used for the build job definition. Jenkins will start the virtual machine when the build job is started and stops the virtual machine (the plugin will also restore the virtual machine).


Note: Snapshots are implemented for Virtualbox 4.0 and 4.1. Version 3.x isn't supported (at the moment).

Wiki page at

https://wiki.jenkins-ci.org/display/JENKINS/VirtualBox+Plugin
----

Some 'git stuff' for this branch:


git clone https://github.com/jenkinsci/virtualbox-plugin.git
## or git pull

git branch -r
## shows remote branch origin/snap

git branch --track snap origin/snap
## tracks the (see also ################################################################################)

git checkout snap



Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public synchronized List<VirtualBoxMachine> getMachines(VirtualBoxCloud host, Vi
List<VirtualBoxMachine> result = new ArrayList<VirtualBoxMachine>();
ConnectionHolder holder = connect(hostUrl, userName, password);
for (IMachine machine : holder.vbox.getMachines()) {
result.add(new VirtualBoxMachine(host, machine.getName()));
result.add(new VirtualBoxMachine(host, machine.getName(), machine.getId(), null));
}
holder.disconnect();
return result;
Expand Down
146 changes: 112 additions & 34 deletions plugin/src/main/java/hudson/plugins/virtualbox/VirtualBoxControlV40.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import org.virtualbox_4_0.*;

/**
* @author Mihai Serban
* @author Mihai Serban, Lars Gregori
*/
public final class VirtualBoxControlV40 implements VirtualBoxControl {

Expand Down Expand Up @@ -42,52 +42,104 @@ public synchronized boolean isConnected() {
public synchronized List<VirtualBoxMachine> getMachines(VirtualBoxCloud host, VirtualBoxLogger log) {
List<VirtualBoxMachine> result = new ArrayList<VirtualBoxMachine>();
for (IMachine machine : vbox.getMachines()) {
result.add(new VirtualBoxMachine(host, machine.getName()));
result.add(new VirtualBoxMachine(host, machine.getName(), machine.getId(), null));
if (machine.getSnapshotCount() > 0) {
ISnapshot root = findRootSnapshot(machine);
String machineName = machine.getName();
List<SnapshotData> snapshots = fillSnapshot(new ArrayList<SnapshotData>(), "", root);
for (SnapshotData snapshot : snapshots) {
result.add(new VirtualBoxMachine(host, machineName + "/" + snapshot.name, machine.getId(), snapshot.id));
}
}
}
return result;
}

private static ISnapshot findRootSnapshot(IMachine machine) {
ISnapshot root = machine.getCurrentSnapshot();
while (root.getParent() != null) {
root = root.getParent();
}
return root;
}

private class SnapshotData {
String name;
String id;
}

private List<SnapshotData> fillSnapshot(List<SnapshotData> snapshotList, String snapshotPath, ISnapshot snapshot) {
if (snapshot != null) {
SnapshotData snapshotData = new SnapshotData();
snapshotData.name = snapshotPath + "/" + snapshot.getName();
snapshotData.id = snapshot.getId();
snapshotList.add(snapshotData);
if (snapshot.getChildren() != null) {
for (ISnapshot child : snapshot.getChildren()) {
// call fillSnapshot recursive
snapshotList = fillSnapshot(snapshotList, snapshotData.name, child);
}
}
}
return snapshotList;
}

/**
* Starts specified VirtualBox virtual machine.
*
* @param vbMachine virtual machine to start
* @param type session type (can be headless, vrdp, gui, sdl)
* @param log
* @param vbMachine virtual machine getto start
* @param type session type (can be headless, vrdp, gui, sdl)
* @param log virtual box logging
* @return result code
*/
public synchronized long startVm(VirtualBoxMachine vbMachine, String type, VirtualBoxLogger log) {
IMachine machine = vbox.findMachine(vbMachine.getName());
String machineName = vbMachine.getName();
String machineId = vbMachine.getMachineId();
String snapshotId = vbMachine.getSnapshotId();

IMachine machine = vbox.findMachine(machineId);
if (null == machine) {
log.logFatalError("Cannot find node: " + vbMachine.getName());
log.logFatalError("Cannot find node: " + machineName);
return -1;
}

ISnapshot snapshot = null;
if (snapshotId != null) {
snapshot = machine.findSnapshot(snapshotId);
}

// states diagram: https://www.virtualbox.org/sdkref/_virtual_box_8idl.html#80b08f71210afe16038e904a656ed9eb
MachineState state = machine.getState();
ISession session;
IProgress progress;

// wait for transient states to finish
while (state.value() >= MachineState.FirstTransient.value() && state.value() <= MachineState.LastTransient.value()) {
log.logInfo("node " + vbMachine.getName() + " in state " + state.toString());
log.logInfo("node " + machineName + " in state " + state.toString());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
state = machine.getState();
}

if (MachineState.Running == state) {
log.logInfo("node " + vbMachine.getName() + " in state " + state.toString());
log.logInfo("node " + vbMachine.getName() + " started");
log.logInfo("node " + machineName + " in state " + state.toString());

if (snapshotId != null) {
log.logInfo("node " + machineName + " already started and snapshot could not be restored");
return -1;
}

log.logInfo("node " + machineName + " started");
return 0;
}

if (MachineState.Stuck == state || MachineState.Paused == state) {
log.logInfo("starting node " + vbMachine.getName() + " from state " + state.toString());
log.logInfo("starting node " + machineName + " from state " + state.toString());
try {
session = getSession(machine);
} catch (Exception e) {
log.logFatalError("node " + vbMachine.getName() + " openMachineSession: " + e.getMessage());
log.logFatalError("node " + machineName + " openMachineSession: " + e.getMessage());
return -1;
}

Expand All @@ -108,32 +160,41 @@ public synchronized long startVm(VirtualBoxMachine vbMachine, String type, Virtu

releaseSession(session, machine);
if (0 != result) {
log.logFatalError("node " + vbMachine.getName() + " error: " + getVBProcessError(progress));
log.logFatalError("node " + machineName + " error: " + getVBProcessError(progress));
return -1;
}

if (MachineState.Stuck != state) {
log.logInfo("node " + vbMachine.getName() + " started");
log.logInfo("node " + machineName + " started");
return 0;
}
// continue from PoweredOff state
state = machine.getState(); // update state
}

log.logInfo("starting node " + vbMachine.getName() + " from state " + state.toString());
log.logInfo("starting node " + machineName + " from state " + state.toString());

// powerUp from Saved, Aborted or PoweredOff states
session = getSession(null);
String env = "";
progress = machine.launchVMProcess(session, type, env);
if (snapshot == null) {
session = getSession(null);
progress = machine.launchVMProcess(session, type, env);
} else {
session = getSession(machine);
progress = session.getConsole().restoreSnapshot(snapshot);
progress.waitForCompletion(-1);
session.unlockMachine();
session = getSession(null);
progress = machine.launchVMProcess(session, type, env);
}
//
progress.waitForCompletion(-1);
long result = progress.getResultCode();
releaseSession(session, machine);

if (0 != result) {
log.logFatalError("node " + vbMachine.getName() + " error: " + getVBProcessError(progress));
log.logFatalError("node " + machineName + " error: " + getVBProcessError(progress));
} else {
log.logInfo("node " + vbMachine.getName() + " started");
log.logInfo("node " + machineName + " started");
}

return result;
Expand All @@ -146,45 +207,62 @@ public synchronized long startVm(VirtualBoxMachine vbMachine, String type, Virtu
* @param log
* @return result code
*/
public synchronized long stopVm(VirtualBoxMachine vbMachine, String stopMode, VirtualBoxLogger log) {
IMachine machine = vbox.findMachine(vbMachine.getName());
public synchronized long stopVm(VirtualBoxMachine vbMachine, String stopMode, VirtualBoxLogger log) {
String machineName = vbMachine.getName();
String machineId = vbMachine.getMachineId();
String snapshotId = vbMachine.getSnapshotId();

IMachine machine = vbox.findMachine(machineId);
if (null == machine) {
log.logFatalError("Cannot find node: " + vbMachine.getName());
log.logFatalError("Cannot find node: " + machineName);
return -1;
}

ISnapshot snapshot = null;
if (snapshotId != null) {
snapshot = machine.findSnapshot(snapshotId);
}

// states diagram: https://www.virtualbox.org/sdkref/_virtual_box_8idl.html#80b08f71210afe16038e904a656ed9eb
MachineState state = machine.getState();
ISession session;
IProgress progress;

// wait for transient states to finish
while (state.value() >= MachineState.FirstTransient.value() && state.value() <= MachineState.LastTransient.value()) {
log.logInfo("node " + vbMachine.getName() + " in state " + state.toString());
log.logInfo("node " + machineName + " in state " + state.toString());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
state = machine.getState();
}

log.logInfo("stopping node " + vbMachine.getName() + " from state " + state.toString());
log.logInfo("stopping node " + machineName + " from state " + state.toString());

if (MachineState.Aborted == state || MachineState.PoweredOff == state
|| MachineState.Saved == state) {
log.logInfo("node " + vbMachine.getName() + " stopped");
|| MachineState.Saved == state) {
log.logInfo("node " + machineName + " stopped");
return 0;
}

try {
session = getSession(machine);
} catch (Exception e) {
log.logFatalError("node " + vbMachine.getName() + " openMachineSession: " + e.getMessage());
log.logFatalError("node " + machineName + " openMachineSession: " + e.getMessage());
return -1;
}

if (MachineState.Stuck == state || "powerdown".equals(stopMode)) {
// for Stuck state call powerDown and go to PoweredOff state
progress = session.getConsole().powerDown();
} else if (snapshot != null) {
// power down and restore snapshot
progress = session.getConsole().powerDown();
progress.waitForCompletion(-1);
session = getSession(machine);
progress = session.getConsole().restoreSnapshot(snapshot);
progress.waitForCompletion(-1);
session.unlockMachine();
} else {
// Running or Paused
progress = session.getConsole().saveState();
Expand All @@ -196,9 +274,9 @@ public synchronized long stopVm(VirtualBoxMachine vbMachine, String stopMode, Vi
releaseSession(session, machine);

if (0 != result) {
log.logFatalError("node " + vbMachine.getName() + " error: " + getVBProcessError(progress));
log.logFatalError("node " + machineName + " error: " + getVBProcessError(progress));
} else {
log.logInfo("node " + vbMachine.getName() + " stopped");
log.logInfo("node " + machineName + " stopped");
}

return result;
Expand Down Expand Up @@ -236,23 +314,23 @@ private boolean isTransientState(SessionState state) {
}

private ISession getSession(IMachine machine) {
ISession s = manager.getSessionObject();
ISession session = manager.getSessionObject();
if (null != machine) {
machine.lockMachine(s, LockType.Shared);
machine.lockMachine(session, LockType.Shared);
while (isTransientState(machine.getSessionState())) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {}
}
}

while (isTransientState(s.getState())) {
while (isTransientState(session.getState())) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {}
}

return s;
return session;
}

private void releaseSession(ISession s, IMachine machine) {
Expand Down
Loading