blob: bf75aa686db08c38dbab5988f78443557dfac76a [file] [log] [blame]
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you 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 org.jclouds.virtualbox.functions;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.transform;
import static org.jclouds.scriptbuilder.domain.Statements.call;
import java.net.URI;
import java.util.List;
import javax.annotation.Resource;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.Constants;
import org.jclouds.compute.domain.ExecResponse;
import org.jclouds.compute.domain.NodeMetadata;
import org.jclouds.compute.options.RunScriptOptions;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.logging.Logger;
import org.jclouds.ssh.SshClient;
import org.jclouds.virtualbox.Preconfiguration;
import org.jclouds.virtualbox.domain.IsoSpec;
import org.jclouds.virtualbox.domain.MasterSpec;
import org.jclouds.virtualbox.domain.VmSpec;
import org.jclouds.virtualbox.statements.InstallGuestAdditions;
import org.jclouds.virtualbox.util.MachineController;
import org.jclouds.virtualbox.util.MachineUtils;
import org.virtualbox_4_1.IMachine;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.Inject;
@Singleton
public class CreateAndInstallVm implements Function<MasterSpec, IMachine> {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final CreateAndRegisterMachineFromIsoIfNotAlreadyExists createAndRegisterMachineFromIsoIfNotAlreadyExists;
private final Predicate<SshClient> sshResponds;
private LoadingCache<IsoSpec, URI> preConfiguration;
private final Function<IMachine, SshClient> sshClientForIMachine;
private final MachineUtils machineUtils;
private final IMachineToNodeMetadata imachineToNodeMetadata;
private final MachineController machineController;
private final String version;
@Inject
public CreateAndInstallVm(
CreateAndRegisterMachineFromIsoIfNotAlreadyExists CreateAndRegisterMachineFromIsoIfNotAlreadyExists,
IMachineToNodeMetadata imachineToNodeMetadata,
Predicate<SshClient> sshResponds,
Function<IMachine, SshClient> sshClientForIMachine,
MachineUtils machineUtils,
@Preconfiguration LoadingCache<IsoSpec, URI> preConfiguration,
MachineController machineController, @Named(Constants.PROPERTY_BUILD_VERSION) String version) {
this.createAndRegisterMachineFromIsoIfNotAlreadyExists = CreateAndRegisterMachineFromIsoIfNotAlreadyExists;
this.sshResponds = sshResponds;
this.sshClientForIMachine = sshClientForIMachine;
this.machineUtils = machineUtils;
this.preConfiguration = preConfiguration;
this.imachineToNodeMetadata = imachineToNodeMetadata;
this.machineController = machineController;
this.version = Iterables.get(Splitter.on('r').split(version), 0);
}
@Override
public IMachine apply(MasterSpec masterSpec) {
VmSpec vmSpec = masterSpec.getVmSpec();
IsoSpec isoSpec = masterSpec.getIsoSpec();
String vmName = vmSpec.getVmName();
IMachine vm = createAndRegisterMachineFromIsoIfNotAlreadyExists
.apply(masterSpec);
// Launch machine and wait for it to come online
machineController.ensureMachineIsLaunched(vmName);
URI uri = preConfiguration.getUnchecked(isoSpec);
String installationKeySequence = isoSpec.getInstallationKeySequence()
.replace("PRECONFIGURATION_URL", uri.toASCIIString());
configureOsInstallationWithKeyboardSequence(vmName,
installationKeySequence);
SshClient client = sshClientForIMachine.apply(vm);
logger.debug(">> awaiting installation to finish node(%s)", vmName);
checkState(sshResponds.apply(client),
"timed out waiting for guest %s to be accessible via ssh",
vmName);
logger.debug(">> awaiting installation of guest additions on vm: %s", vmName);
ListenableFuture<ExecResponse> execFuture = machineUtils.runScriptOnNode(imachineToNodeMetadata.apply(vm),
new InstallGuestAdditions(vmSpec, version), RunScriptOptions.NONE);
ExecResponse execResponse = Futures.getUnchecked(execFuture);
checkState(execResponse.getExitStatus() == 0);
logger.debug(">> awaiting post-installation actions on vm: %s", vmName);
NodeMetadata vmMetadata = imachineToNodeMetadata.apply(vm);
execFuture = machineUtils.runScriptOnNode(vmMetadata, call("cleanupUdevIfNeeded"), RunScriptOptions.NONE);
execResponse = Futures.getUnchecked(execFuture);
checkState(execResponse.getExitStatus() == 0);
logger.debug("<< installation of image complete. Powering down node(%s)", vmName);
machineController.ensureMachineHasPowerDown(vmName);
return vm;
}
private void configureOsInstallationWithKeyboardSequence(String vmName,
String installationKeySequence) {
Iterable<List<Integer>> scancodelist = transform(Splitter.on(" ")
.split(installationKeySequence), new StringToKeyCode());
for (List<Integer> scancodes : scancodelist) {
machineUtils.sharedLockMachineAndApplyToSession(vmName,new SendScancodes(scancodes));
}
}
}