Skip to content
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

Support reconfiguring a templated VM during deployment #123

Open
wants to merge 2 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
37 changes: 30 additions & 7 deletions src/main/java/org/jenkinsci/plugins/vSphereCloudSlaveTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import static org.jenkinsci.plugins.vsphere.tools.PermissionUtils.throwUnlessUserHasPermissionToConfigureCloud;

import com.vmware.vim25.VirtualMachineConfigSpec;
import hudson.DescriptorExtensionList;
import hudson.EnvVars;
import hudson.Extension;
Expand Down Expand Up @@ -49,7 +50,6 @@
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import javax.annotation.Nonnull;

Expand All @@ -61,6 +61,7 @@
import org.jenkinsci.plugins.vsphere.VSphereConnectionConfig;
import org.jenkinsci.plugins.vsphere.VSphereGuestInfoProperty;
import org.jenkinsci.plugins.vsphere.builders.Messages;
import org.jenkinsci.plugins.vsphere.builders.ReconfigureStep;
import org.jenkinsci.plugins.vsphere.tools.VSphere;
import org.jenkinsci.plugins.vsphere.tools.VSphereDuplicateException;
import org.jenkinsci.plugins.vsphere.tools.VSphereException;
Expand Down Expand Up @@ -124,6 +125,7 @@ public class vSphereCloudSlaveTemplate implements Describable<vSphereCloudSlaveT
private final List<? extends VSphereGuestInfoProperty> guestInfoProperties;
private ComputerLauncher launcher;
private RetentionStrategy<?> retentionStrategy;
private final List<ReconfigureStep> reconfigureSteps;

private transient Set<LabelAtom> labelSet;
protected transient vSphereCloud parent;
Expand Down Expand Up @@ -156,7 +158,8 @@ public vSphereCloudSlaveTemplate(final String cloneNamePrefix,
final ComputerLauncher launcher,
final RetentionStrategy<?> retentionStrategy,
final List<? extends NodeProperty<?>> nodeProperties,
final List<? extends VSphereGuestInfoProperty> guestInfoProperties) {
final List<? extends VSphereGuestInfoProperty> guestInfoProperties,
final List<ReconfigureStep> reconfigureSteps) {
this.configVersion = CURRENT_CONFIG_VERSION;
this.cloneNamePrefix = cloneNamePrefix;
this.masterImageName = masterImageName;
Expand Down Expand Up @@ -186,6 +189,7 @@ public vSphereCloudSlaveTemplate(final String cloneNamePrefix,
this.guestInfoProperties = Util.fixNull(guestInfoProperties);
this.launcher = launcher;
this.retentionStrategy = retentionStrategy;
this.reconfigureSteps = Util.fixNull(reconfigureSteps);
readResolve();
}

Expand Down Expand Up @@ -319,6 +323,10 @@ public RetentionStrategy<?> getRetentionStrategy() {
return this.retentionStrategy;
}

public List<ReconfigureStep> getReconfigureSteps() {
return this.reconfigureSteps;
}

protected Object readResolve() {
this.labelSet = Label.parse(labelString);
if(this.templateInstanceCap == 0) {
Expand Down Expand Up @@ -395,20 +403,19 @@ protected Object readResolve() {
}

public vSphereCloudProvisionedSlave provision(final String cloneName, final TaskListener listener) throws VSphereException, FormException, IOException, InterruptedException {
final PrintStream logger = listener.getLogger();
final Map<String, String> resolvedExtraConfigParameters = calculateExtraConfigParameters(cloneName, listener);
final VSphere vSphere = getParent().vSphereInstance();
final vSphereCloudProvisionedSlave slave;
try {
slave = provision(cloneName, logger, resolvedExtraConfigParameters, vSphere);
slave = provision(cloneName, listener, resolvedExtraConfigParameters, vSphere);
} finally {
vSphere.disconnect();
}
return slave;
}

private vSphereCloudProvisionedSlave provision(final String cloneName, final PrintStream logger, final Map<String, String> resolvedExtraConfigParameters, final VSphere vSphere) throws VSphereException, FormException, IOException {
final boolean POWER_ON = true;
private vSphereCloudProvisionedSlave provision(final String cloneName, final TaskListener listener, final Map<String, String> resolvedExtraConfigParameters, final VSphere vSphere) throws VSphereException, FormException, IOException {
final PrintStream logger = listener.getLogger();
final boolean useCurrentSnapshot;
final String snapshotToUse;
if (getUseSnapshot()) {
Expand All @@ -425,8 +432,24 @@ private vSphereCloudProvisionedSlave provision(final String cloneName, final Pri
snapshotToUse = null;
}
try {
vSphere.cloneOrDeployVm(cloneName, this.masterImageName, this.linkedClone, this.resourcePool, this.cluster, this.datastore, this.folder, useCurrentSnapshot, snapshotToUse, POWER_ON, resolvedExtraConfigParameters, this.customizationSpec, logger);
final boolean willReconfigure = reconfigureSteps != null && !reconfigureSteps.isEmpty();
vSphere.cloneOrDeployVm(cloneName, this.masterImageName, this.linkedClone, this.resourcePool, this.cluster, this.datastore, this.folder, useCurrentSnapshot, snapshotToUse, !willReconfigure, resolvedExtraConfigParameters, this.customizationSpec, logger);
LOGGER.log(Level.FINE, "Created new VM {0} from image {1}", new Object[]{ cloneName, this.masterImageName });
if(willReconfigure) {
final VirtualMachine vm = vSphere.getVmByName(cloneName);
final VirtualMachineConfigSpec spec = new VirtualMachineConfigSpec();
final EnvVars env = new EnvVars();
for (ReconfigureStep globalStep : reconfigureSteps) {
// Do not mutate global steps to perform reconfiguration - use a clone
ReconfigureStep actionStep = (ReconfigureStep)Jenkins.XSTREAM2.fromXML(Jenkins.XSTREAM2.toXML(globalStep));
actionStep.setVsphere(vSphere);
actionStep.setVM(vm);
actionStep.setVirtualMachineConfigSpec(spec);
actionStep.perform(env, listener);
}
vSphere.reconfigureVm(cloneName, spec);
vSphere.startVm(cloneName, 120);
}
} catch (VSphereDuplicateException ex) {
final String vmJenkinsUrl = findWhichJenkinsThisVMBelongsTo(vSphere, cloneName);
if ( vmJenkinsUrl==null ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ public boolean getAppend() {
return append;
}

@Override
public void perform(@Nonnull EnvVars env, @Nonnull TaskListener listener) throws VSphereException {
reconfigureAnnotation(env, listener);
}

@Override
public void perform(@Nonnull Run<?, ?> run, @Nonnull FilePath filePath, @Nonnull Launcher launcher, @Nonnull TaskListener listener) throws InterruptedException, IOException {
try {
Expand All @@ -77,19 +82,14 @@ public boolean perform(final AbstractBuild<?, ?> build, final Launcher launcher,
}

public boolean reconfigureAnnotation(final Run<?, ?> run, final Launcher launcher, final TaskListener listener) throws VSphereException {
EnvVars env = extractEnvironment(run, listener);

return reconfigureAnnotation(env, listener);
}

private boolean reconfigureAnnotation(final EnvVars env, final TaskListener listener) throws VSphereException {
final PrintStream jLogger = listener.getLogger();
String expandedText = getAnnotation();
final EnvVars env;
try {
env = run.getEnvironment(listener);
} catch (Exception e) {
throw new VSphereException(e);
}
if (run instanceof AbstractBuild) {
env.overrideAll(((AbstractBuild) run).getBuildVariables()); // Add in matrix axes..
expandedText = env.expand(expandedText);
}
String expandedText = env.expand(getAnnotation());

VSphereLogger.vsLogger(jLogger, "Preparing reconfigure: Annotation");
if (getAppend()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ public String getCoresPerSocket() {
return coresPerSocket;
}

@Override
public void perform(@Nonnull EnvVars env, @Nonnull TaskListener listener) throws VSphereException {
reconfigureCPU(env, listener);
}

@Override
public void perform(@Nonnull Run<?, ?> run, @Nonnull FilePath filePath, @Nonnull Launcher launcher, @Nonnull TaskListener listener) throws InterruptedException, IOException {
try {
Expand All @@ -70,32 +75,23 @@ public boolean perform(final AbstractBuild<?, ?> build, final Launcher launcher,
//TODO throw AbortException instead of returning value
}

public boolean reconfigureCPU (final Run<?, ?> run, final Launcher launcher, final TaskListener listener) throws VSphereException {
public boolean reconfigureCPU(final Run<?, ?> run, final Launcher launcher, final TaskListener listener) throws VSphereException {
EnvVars env = extractEnvironment(run, listener);

PrintStream jLogger = listener.getLogger();
String expandedCPUCores = cpuCores;
String expandedCoresPerSocket = coresPerSocket;
EnvVars env;
try {
env = run.getEnvironment(listener);
} catch (Exception e) {
throw new VSphereException(e);
}
return reconfigureCPU(env, listener);
}

if (run instanceof AbstractBuild) {
env.overrideAll(((AbstractBuild) run).getBuildVariables()); // Add in matrix axes..
expandedCPUCores = env.expand(cpuCores);
expandedCoresPerSocket = env.expand(coresPerSocket);
}
private boolean reconfigureCPU(final EnvVars env, final TaskListener listener) throws VSphereException {
PrintStream jLogger = listener.getLogger();
String expandedCPUCores = env.expand(cpuCores);
String expandedCoresPerSocket = env.expand(coresPerSocket);

VSphereLogger.vsLogger(jLogger, "Preparing reconfigure: CPU");
spec.setNumCPUs(Integer.valueOf(expandedCPUCores));
spec.setNumCoresPerSocket(Integer.valueOf(expandedCoresPerSocket));

VSphereLogger.vsLogger(jLogger, "Finished!");
return true;
}

}

@Extension
public static final class ReconfigureCpuDescriptor extends ReconfigureStepDescriptor {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ public String getDataStore() {
return datastore;
}

@Override
public void perform(@Nonnull EnvVars env, @Nonnull TaskListener listener) throws VSphereException {
reconfigureDisk(env, listener);
}

@Override
public void perform(@Nonnull Run<?, ?> run, @Nonnull FilePath filePath, @Nonnull Launcher launcher, @Nonnull TaskListener listener) throws InterruptedException, IOException {
try {
Expand All @@ -86,20 +91,18 @@ public boolean perform(final AbstractBuild<?, ?> build, final Launcher launcher,
}

public boolean reconfigureDisk(final Run<?, ?> run, final Launcher launcher, final TaskListener listener) throws VSphereException {
EnvVars env = extractEnvironment(run, listener);

return reconfigureDisk(env, listener);
}

private boolean reconfigureDisk(final EnvVars env, final TaskListener listener) throws VSphereException {
PrintStream jLogger = listener.getLogger();
int diskSize = Integer.parseInt(this.diskSize);
EnvVars env;
int diskSize = Integer.parseInt(env.expand(this.diskSize));

try {
env = run.getEnvironment(listener);
if (run instanceof AbstractBuild) {
env.overrideAll(((AbstractBuild) run).getBuildVariables()); // Add in matrix axes..
diskSize = Integer.parseInt(env.expand(this.diskSize));
}
VirtualDeviceConfigSpec vdiskSpec = createAddDiskConfigSpec(vm, diskSize, jLogger);
VirtualDeviceConfigSpec [] vdiskSpecArray = {vdiskSpec};

spec.setDeviceChange(vdiskSpecArray);
VSphereLogger.vsLogger(jLogger, "Configuration done");
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public String getMemorySize() {
return memorySize;
}

@Override
public void perform(@Nonnull EnvVars env, @Nonnull TaskListener listener) throws VSphereException {
reconfigureMemory(env, listener);
}

@Override
public void perform(@Nonnull Run<?, ?> run, @Nonnull FilePath filePath, @Nonnull Launcher launcher, @Nonnull TaskListener listener) throws InterruptedException, IOException {
try {
Expand All @@ -65,24 +70,19 @@ public boolean perform(final AbstractBuild<?, ?> build, final Launcher launcher,
}

public boolean reconfigureMemory(final Run<?, ?> run, final Launcher launcher, final TaskListener listener) throws VSphereException {
EnvVars env = extractEnvironment(run, listener);

PrintStream jLogger = listener.getLogger();
String expandedMemorySize = memorySize;
EnvVars env;
try {
env = run.getEnvironment(listener);
} catch (Exception e) {
throw new VSphereException(e);
}
if (run instanceof AbstractBuild) {
env.overrideAll(((AbstractBuild) run).getBuildVariables()); // Add in matrix axes..
expandedMemorySize = env.expand(memorySize);
}
return reconfigureMemory(env, listener);
}

private boolean reconfigureMemory(final EnvVars env, final TaskListener listener) throws VSphereException {
PrintStream jLogger = listener.getLogger();
String expandedMemorySize = env.expand(memorySize);

VSphereLogger.vsLogger(jLogger, "Preparing reconfigure: Memory");
spec.setMemoryMB(Long.valueOf(expandedMemorySize));
VSphereLogger.vsLogger(jLogger, "Finished!");
return true;
VSphereLogger.vsLogger(jLogger, "Preparing reconfigure: Memory");
spec.setMemoryMB(Long.valueOf(expandedMemorySize));
VSphereLogger.vsLogger(jLogger, "Finished!");
return true;
}

@Extension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ public String getDistributedPortId() {
return distributedPortId;
}

@Override
public void perform(@Nonnull EnvVars env, @Nonnull TaskListener listener) throws VSphereException {
reconfigureNetwork(env, listener);
}

@Override
public void perform(@Nonnull Run<?, ?> run, @Nonnull FilePath filePath, @Nonnull Launcher launcher, @Nonnull TaskListener listener) throws InterruptedException, IOException {
try {
Expand All @@ -114,27 +119,20 @@ public boolean perform(final AbstractBuild<?, ?> build, final Launcher launcher,
//TODO throw AbortException instead of returning value
}

public boolean reconfigureNetwork(final Run<?, ?> run, final Launcher launcher, final TaskListener listener) throws VSphereException {
public boolean reconfigureNetwork(final Run<?, ?> run, final Launcher launcher, final TaskListener listener) throws VSphereException {
EnvVars env = extractEnvironment(run, listener);

return reconfigureNetwork(env, listener);
}

private boolean reconfigureNetwork(final EnvVars env, final TaskListener listener) throws VSphereException {
PrintStream jLogger = listener.getLogger();
String expandedDeviceLabel = deviceLabel;
String expandedMacAddress = macAddress;
String expandedPortGroup = portGroup;
String expandedDistributedPortGroup = distributedPortGroup;
String expandedDistributedPortId = distributedPortId;
EnvVars env;
try {
env = run.getEnvironment(listener);
} catch (Exception e) {
throw new VSphereException(e);
}
if (run instanceof AbstractBuild) {
env.overrideAll(((AbstractBuild) run).getBuildVariables()); // Add in matrix axes..
expandedDeviceLabel = env.expand(deviceLabel);
expandedMacAddress = env.expand(macAddress);
expandedPortGroup = env.expand(portGroup);
expandedDistributedPortGroup = env.expand(distributedPortGroup);
expandedDistributedPortId = env.expand(distributedPortId);
}
String expandedDeviceLabel = env.expand(deviceLabel);
String expandedMacAddress = env.expand(macAddress);
String expandedPortGroup = env.expand(portGroup);
String expandedDistributedPortGroup = env.expand(distributedPortGroup);
String expandedDistributedPortId = env.expand(distributedPortId);

VSphereLogger.vsLogger(jLogger, "Preparing reconfigure: "+ deviceAction.getLabel() +" Network Adapter \"" + expandedDeviceLabel + "\"");
VirtualEthernetCard vEth = null;
if (deviceAction == DeviceAction.ADD) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ public static List<ReconfigureStepDescriptor> all() {

public abstract void perform(@Nonnull Run<?, ?> run, FilePath filePath, @Nonnull Launcher launcher, @Nonnull TaskListener listener) throws InterruptedException, IOException;

public abstract void perform(@Nonnull EnvVars env, @Nonnull TaskListener listener) throws VSphereException;

protected VirtualDevice findDeviceByLabel(VirtualDevice[] devices, String label) {
for(VirtualDevice d : devices) {
if(d.getDeviceInfo().getLabel().contentEquals(label)) {
Expand All @@ -78,6 +80,20 @@ protected VirtualDevice findDeviceByLabel(VirtualDevice[] devices, String label)
return null;
}

protected EnvVars extractEnvironment(final Run<?, ?> run, final TaskListener listener) throws VSphereException {
try {
EnvVars env = run.getEnvironment(listener);

if (run instanceof AbstractBuild) {
env.overrideAll(((AbstractBuild) run).getBuildVariables()); // Add in matrix axes..
}

return env;
} catch (Exception e) {
throw new VSphereException(e);
}
}

public static abstract class ReconfigureStepDescriptor extends Descriptor<ReconfigureStep> {

protected ReconfigureStepDescriptor() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@
<f:repeatableHeteroProperty field="nodeProperties" oneEach="true" hasHeader="true"
addCaption="Add Node Property" deleteCaption="Delete Node Property"/>
</f:entry>

<f:entry title="Reconfigure VM" help="${descriptor.getHelpFile('reconfigureSteps')}">
<f:repeatableHeteroProperty field="reconfigureSteps" oneEach="true" hasHeader="true"
addCaption="Add Modification" deleteCaption="Delete Modification"/>
</f:entry>
</f:advanced>

<f:entry title="">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div>
Reconfigure the virtual machine hardware. Allows adjusting RAM, CPU, etc.
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ private CloudProvisioningRecord createInstance(int capacity, int provisioned, in
private static vSphereCloudSlaveTemplate stubTemplate(String prefix, int templateInstanceCap) {
return new vSphereCloudSlaveTemplate(prefix, "", null, null, false, null, null, null, null, null, null, templateInstanceCap, 1,
null, null, null, false, false, 0, 0, false, null, null, null, new JNLPLauncher(),
RetentionStrategy.NOOP, null, null);
RetentionStrategy.NOOP, null, null, null);
}

private static String toHexString(byte[] bytes) {
Expand Down
Loading