org.jclouds.virtualbox.util.MachineUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of virtualbox Show documentation
Show all versions of virtualbox Show documentation
jclouds components to access an implementation of virtualbox
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.util;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.Uninterruptibles;
import com.google.inject.Inject;
import org.jclouds.compute.callables.RunScriptOnNode;
import org.jclouds.compute.callables.RunScriptOnNode.Factory;
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.scriptbuilder.domain.Statement;
import org.jclouds.util.Throwables2;
import org.virtualbox_4_2.IMachine;
import org.virtualbox_4_2.ISession;
import org.virtualbox_4_2.LockType;
import org.virtualbox_4_2.SessionState;
import org.virtualbox_4_2.VBoxException;
import org.virtualbox_4_2.VirtualBoxManager;
import javax.annotation.Resource;
import javax.inject.Named;
import javax.inject.Singleton;
import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.util.Predicates2.retry;
/**
* Utilities for executing functions on a VirtualBox machine.
*/
@Singleton
public class MachineUtils {
public static final String IP_V4_ADDRESS_PATTERN = "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+ "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+ "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$";
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final Supplier manager;
private final Factory scriptRunner;
@Inject
public MachineUtils(Supplier manager, RunScriptOnNode.Factory scriptRunner) {
this.manager = manager;
this.scriptRunner = scriptRunner;
}
public ListenableFuture runScriptOnNode(NodeMetadata metadata, Statement statement,
RunScriptOptions options) {
return scriptRunner.submit(metadata, statement, options);
}
/**
* Locks the machine and executes the given function using the machine matching the given id.
* Since the machine is locked it is possible to perform some modifications to the IMachine.
*
* Unlocks the machine before returning.
*
* @param machineId
* the id of the machine
* @param function
* the function to execute
* @return the result from applying the function to the machine.
*/
public T writeLockMachineAndApply(final String machineId, final Function function) {
return lockSessionOnMachineAndApply(machineId, LockType.Write, new Function() {
@Override
public T apply(ISession session) {
return function.apply(session.getMachine());
}
@Override
public String toString() {
return function.toString();
}
});
}
/**
* Locks the machine and executes the given function using the machine matching the given id. The
* machine is write locked and modifications to the session that reflect on the machine can be
* done safely.
*
* Unlocks the machine before returning.
*
* @param machineId
* the id of the machine
* @param function
* the function to execute
* @return the result from applying the function to the machine.
*/
public T writeLockMachineAndApplyToSession(final String machineId, final Function function) {
return lockSessionOnMachineAndApply(machineId, LockType.Write, function);
}
/**
* Locks the machine and executes the given function using the machine matching the given id. The
* machine is read locked, which means that settings can be read safely (but not changed) by
* function.
*
* Unlocks the machine before returning.
*
* @param machineId
* the id of the machine
* @param function
* the function to execute
* @return the result from applying the function to the machine.
*/
public T sharedLockMachineAndApply(final String machineId, final Function function) {
return lockSessionOnMachineAndApply(machineId, LockType.Shared, new Function() {
@Override
public T apply(ISession session) {
return function.apply(session.getMachine());
}
@Override
public String toString() {
return function.toString();
}
});
}
/**
* Locks the machine and executes the given function to the session using the machine matching
* the given id. The machine is read locked, which means that settings can be read safely (but
* not changed) by function.
*
* Unlocks the machine before returning.
*
* @param machineId
* the id of the machine
* @param function
* the function to execute
* @return the result from applying the function to the machine.
*/
public T sharedLockMachineAndApplyToSession(final String machineId, final Function function) {
return lockSessionOnMachineAndApply(machineId, LockType.Shared, function);
}
/**
* Locks the machine and executes the given function using the current session. Since the machine
* is locked it is possible to perform some modifications to the IMachine.
*
* Unlocks the machine before returning.
*
* Tries to obtain a lock 15 times before giving up waiting 1 sec between tries. When no machine
* is found null is returned.
*
* @param type
* the kind of lock to use when initially locking the machine.
* @param machineId
* the id of the machine
* @param function
* the function to execute
* @return the result from applying the function to the session.
*/
protected T lockSessionOnMachineAndApply(String machineId, LockType type, Function function) {
int retries = 15;
ISession session = checkNotNull(lockSession(machineId, type, retries), "session");
try {
return function.apply(session);
} catch (VBoxException e) {
throw new RuntimeException(String.format("error applying %s to %s with %s lock: %s", function, machineId,
type, e.getMessage()), e);
} finally {
// this is a workaround for shared lock type, where session state is not updated immediately
if (type == LockType.Shared) {
Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);
}
if (session.getState().equals(SessionState.Locked)) {
session.unlockMachine();
}
if (!session.getState().equals(SessionState.Unlocked)) {
checkSessionIsUnlocked(session, 5, 3L, TimeUnit.SECONDS);
}
}
}
private ISession lockSession(String machineId, LockType type, int retries) {
int count = 0;
IMachine immutableMachine = manager.get().getVBox().findMachine(machineId);
ISession session;
while (true) {
try {
session = manager.get().getSessionObject();
immutableMachine.lockMachine(session, type);
break;
} catch (VBoxException e) {
VBoxException vbex = Throwables2.getFirstThrowableOfType(e, VBoxException.class);
if (vbex != null && machineNotFoundException(vbex)) {
return null;
}
count++;
logger.debug("Could not lock machine (try %d of %d). Error: %s", count, retries, e.getMessage());
if (count == retries) {
throw new RuntimeException(String.format("error locking %s with %s lock: %s", machineId, type,
e.getMessage()), e);
}
Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);
}
}
checkState(session.getState().equals(SessionState.Locked));
return checkNotNull(session, "session");
}
/**
* @param machineId
* @param function
* @return
*/
public T applyForMachine(final String machineId, final Function function) {
final IMachine immutableMachine = manager.get().getVBox().findMachine(machineId);
return new Function() {
@Override
public T apply(IMachine machine) {
return function.apply(machine);
}
@Override
public String toString() {
return function.toString();
}
}.apply(immutableMachine);
}
public static boolean machineNotFoundException(VBoxException e) {
return e.getMessage().contains("VirtualBox error: Could not find a registered machine named ")
|| e.getMessage().contains("Could not find a registered machine with UUID {");
}
private void checkSessionIsUnlocked(ISession session, int attempts, long period, TimeUnit timeUnit) {
checkState(
retry(new SessionStatePredicate(session), attempts * period, period, timeUnit).apply(SessionState.Unlocked),
"timed out or number of retries(%s) reached waiting for session to be unlocked", attempts);
}
private static class SessionStatePredicate implements Predicate {
private final ISession session;
SessionStatePredicate(ISession session) {
this.session = session;
}
@Override
public boolean apply(SessionState input) {
return session.getState().equals(input);
}
}
}