/*************************************************************************
* Copyright 2009-2015 Eucalyptus Systems, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
* Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
* CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
* additional information or have any questions.
************************************************************************/
package com.eucalyptus.vm;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.Nullable;
import org.apache.log4j.Logger;
import org.hibernate.criterion.Restrictions;
import com.eucalyptus.auth.principal.AccountFullName;
import com.eucalyptus.blockstorage.Storage;
import com.eucalyptus.bootstrap.Bootstrap;
import com.eucalyptus.compute.common.BlockDeviceMappingItemType;
import com.eucalyptus.compute.common.Compute;
import com.eucalyptus.compute.common.ComputeMessage;
import com.eucalyptus.compute.common.DescribeSnapshotsResponseType;
import com.eucalyptus.compute.common.DescribeSnapshotsType;
import com.eucalyptus.compute.common.EbsDeviceMapping;
import com.eucalyptus.compute.common.Filter;
import com.eucalyptus.compute.common.ImageMetadata;
import com.eucalyptus.cluster.callback.StartInstanceCallback;
import com.eucalyptus.cluster.callback.StopInstanceCallback;
import com.eucalyptus.component.ComponentId;
import com.eucalyptus.component.Partitions;
import com.eucalyptus.component.ServiceConfiguration;
import com.eucalyptus.component.Topology;
import com.eucalyptus.component.id.Eucalyptus;
import com.eucalyptus.compute.common.Snapshot;
import com.eucalyptus.compute.common.backend.CreateSnapshotResponseType;
import com.eucalyptus.compute.common.backend.CreateSnapshotType;
import com.eucalyptus.compute.common.internal.vm.VmCreateImageSnapshot;
import com.eucalyptus.compute.common.internal.vm.VmCreateImageTask;
import com.eucalyptus.compute.common.internal.vm.VmInstance;
import com.eucalyptus.compute.common.internal.vm.VmRuntimeState;
import com.eucalyptus.compute.common.internal.vm.VmVolumeAttachment;
import com.eucalyptus.entities.Entities;
import com.eucalyptus.entities.TransactionResource;
import com.eucalyptus.event.ClockTick;
import com.eucalyptus.event.EventListener;
import com.eucalyptus.event.Listeners;
import com.eucalyptus.compute.common.internal.images.BlockStorageImageInfo;
import com.eucalyptus.compute.common.internal.images.DeviceMapping;
import com.eucalyptus.compute.common.internal.images.ImageInfo;
import com.eucalyptus.images.Images;
import com.eucalyptus.images.Images.DeviceMappingDetails;
import com.eucalyptus.util.Callback;
import com.eucalyptus.util.DispatchingClient;
import com.eucalyptus.util.EucalyptusCloudException;
import com.eucalyptus.util.Exceptions;
import com.eucalyptus.util.Callback.Checked;
import com.eucalyptus.util.async.CheckedListenableFuture;
import com.eucalyptus.util.async.Futures;
import com.eucalyptus.compute.common.internal.vm.VmCreateImageTask.CreateImageState;
import com.eucalyptus.compute.common.internal.vm.VmInstance.VmState;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import edu.ucsb.eucalyptus.msgs.BaseMessage;
import com.eucalyptus.cluster.common.msgs.ClusterStartInstanceResponseType;
import com.eucalyptus.cluster.common.msgs.ClusterStopInstanceResponseType;
/**
* @author Sang-Min Park
*
*/
public class CreateImageTask {
private static Logger LOG = Logger.getLogger( CreateImageTask.class );
private static Map<String, CreateImageTask> createImageTasks =
new ConcurrentHashMap<String, CreateImageTask>();
/* from CreateImage request */
private String accountNumber = null;
private String instanceId = null;
private String imageId = null;
private Boolean noReboot = null;
private List<BlockDeviceMappingItemType> blockDevices = null;
public CreateImageTask(final String accountNumber, final String instanceId, final String imageId, final Boolean noReboot, List<BlockDeviceMappingItemType> blockDevices){
this.accountNumber = accountNumber;
this.instanceId = instanceId;
this.imageId = imageId;
this.noReboot = noReboot;
this.blockDevices = blockDevices;
}
//restore CreateImageTask object from VmInstance (DB record)
static Function<VmInstance, CreateImageTask> RESTORE = new Function<VmInstance, CreateImageTask>(){
@Override
@Nullable
public CreateImageTask apply(@Nullable VmInstance input) {
final String instanceId = input.getDisplayName();
Boolean noReboot =null;
List<BlockDeviceMappingItemType> deviceMaps = null;
final VmCreateImageTask vmTask = input.getRuntimeState().getVmCreateImageTask();
if(vmTask==null)
throw Exceptions.toUndeclared("Failed to find the VmCreateImageTask");
if(vmTask.getImageId()!=null){
try{
deviceMaps = getDeviceMappingsFromImage(vmTask.getImageId());
}catch(final Exception ex){
LOG.error("failed to pull device mapping information from the image", ex);
}
}
noReboot = vmTask.getNoReboot();
final CreateImageTask newTask = new CreateImageTask(input.getOwnerAccountNumber(), instanceId, vmTask.getImageId(), noReboot, deviceMaps);
try{
final String partition = input.getPartition();
final ServiceConfiguration sc = Topology.lookup(Storage.class,
Partitions.lookupByName(partition));
final Collection<ServiceConfiguration> enabledSCs = Topology.enabledServices(Storage.class);
if(enabledSCs.contains(sc))
return newTask;
else
return null;
}catch(final Exception ex){
LOG.error("failed to check state of storage controller", ex);
return null;
}
}
};
/* For each CreateImageTask, check its latest state and take appropriate action */
public static class CreateImageTasksManager implements EventListener<ClockTick> {
public static void register( ) {
Listeners.register( ClockTick.class, new CreateImageTasksManager() );
}
@Override
public void fireEvent(ClockTick event) {
if (!( Bootstrap.isOperational() &&
Topology.isEnabledLocally( Eucalyptus.class ) ) )
return;
/*
* Check if CreateImageTask is held in memory.
* If not, restore from VmInstance (i.e., when service restarts)
*/
final List<VmInstance> candidates = VmInstances.list(
null,
Restrictions.and(
VmInstance.criterion( VmState.RUNNING, VmState.STOPPED ),
Restrictions.not( VmCreateImageTask.inState( VmCreateImageTask.idleStates( ) ) )
),
Collections.<String,String>emptyMap( ),
new Predicate<VmInstance>(){
@Override
public boolean apply(@Nullable VmInstance input) {
if((VmState.RUNNING.equals(input.getState()) || VmState.STOPPED.equals(input.getState()))
&& input.isBlockStorage()
&& input.getRuntimeState().isCreatingImage())
return true;
else
return false;
}
});
try{
for(final VmInstance candidate : candidates){
if(!createImageTasks.containsKey(candidate.getInstanceId())){
LOG.debug(String.format("Restoring create image task for %s", candidate.getInstanceId()));
final CreateImageTask task = RESTORE.apply(candidate);
if(task!=null){
createImageTasks.put(candidate.getInstanceId(), task);
LOG.info(String.format("create image task for %s restored", candidate.getInstanceId()));
}
}
}
}catch(final Exception ex){
LOG.error("unable to retore create-image task", ex);
}
final Map<VmCreateImageTask.CreateImageState, List<CreateImageTask>> taskByState =
new HashMap<VmCreateImageTask.CreateImageState, List<CreateImageTask> >();
for (final String instId : createImageTasks.keySet()){
final CreateImageTask task = createImageTasks.get(instId);
final CreateImageState taskState = task.getVmCreateImageTaskState();
if(! taskByState.containsKey(taskState)){
taskByState.put(taskState, Lists.<CreateImageTask>newArrayList());
}
taskByState.get(taskState).add(task);
}
if(taskByState.containsKey(CreateImageState.pending)){
try{
this.processPendingTasks(taskByState.get(VmCreateImageTask.CreateImageState.pending));
}catch(final Exception ex){
LOG.error("exception while processing pending CreateImageTask", ex);
}
}
if(taskByState.containsKey(CreateImageState.guest_stopping)){
try{
this.processStoppingTasks(taskByState.get(VmCreateImageTask.CreateImageState.guest_stopping));
}catch(final Exception ex){
LOG.error("exception while processing stopping CreateImageTask", ex);
}
}
if(taskByState.containsKey(CreateImageState.creating_snapshot)){
try{
this.processSnapshottingTasks(taskByState.get(VmCreateImageTask.CreateImageState.creating_snapshot));
}catch(final Exception ex){
LOG.error("exception while processing snapshotting CreateImageTask", ex);
}
}
if(taskByState.containsKey(CreateImageState.guest_starting)){
try{
this.processStartingTasks(taskByState.get(VmCreateImageTask.CreateImageState.guest_starting));
}catch(final Exception ex){
LOG.error("exception while processing snapshotting CreateImageTask", ex);
}
}
if(taskByState.containsKey(CreateImageState.complete)){
try{
this.processCompleteTasks(taskByState.get(VmCreateImageTask.CreateImageState.complete));
}catch(final Exception ex){
LOG.error("exception while processing complete CreateImageTask", ex);
}
}
if(taskByState.containsKey(CreateImageState.failed)){
try{
this.processFailedTasks(taskByState.get(VmCreateImageTask.CreateImageState.failed));
}catch(final Exception ex){
LOG.error("exception while processing failed CreateImageTask", ex);
}
}
}
private void processPendingTasks(final List<CreateImageTask> tasks){
/*
* for each pending task, stop instance if !noReboot;
* otherwise, create snapshot;
*/
for(final CreateImageTask task : tasks){
if(!task.noReboot){
try{
task.stopInstance();
LOG.debug(String.format("Stopping instance %s", task.instanceId));
}catch(final Exception ex){
LOG.error(String.format("failed to stop instance %s", task.instanceId), ex);
task.setVmCreateImageTaskState(CreateImageState.failed);
}
}else{
try{
task.createSnapshot();
task.setVmCreateImageTaskState(CreateImageState.creating_snapshot);
}catch(final Exception ex){
LOG.error(String.format("failed to create the snapshots from %s", task.instanceId), ex);
task.setVmCreateImageTaskState(CreateImageState.failed);
}
}
}
}
private void processStoppingTasks(final List<CreateImageTask> tasks){
/*
* for each stopping instance, check the latest guestState
* if stopped, then create snapshot;
* otherwise, wait;
*/
for(final CreateImageTask task : tasks) {
VmInstance vm = null;
try {
vm = task.getVmInstance();
} catch (NoSuchElementException ex) {
return;
}
if(vm.getRuntimeState()!=null && "poweredOff".equals(vm.getRuntimeState().getGuestState())) {
try{
task.createSnapshot();
task.setVmCreateImageTaskState(CreateImageState.creating_snapshot);
}catch(final Exception ex){
LOG.error(String.format("failed to create the snapshot from %s", task.instanceId), ex);
task.setVmCreateImageTaskState(CreateImageState.failed);
}
} /// TODO: spark: should timeout?
}
}
private void processSnapshottingTasks(final List<CreateImageTask> tasks){
/*
* for each instance for which the snapshot is created from the root volume;
* describe snapshot;
* if snapshot is available:
* register image with the snapshot id
* if !noReboot:
* start instance;
* else
* mark complete
*/
for(final CreateImageTask task : tasks){
try{
final List<String> status = task.getSnapshotStatus();
boolean allDone = true;
for(final String s : status){
if("completed".equals(s)){
;
}else if("failed".equals(s)){
throw new EucalyptusCloudException("Snapshot creation failed");
}else
allDone=false;
}
if(allDone){
task.registerImage();
LOG.debug(String.format("Image %s is available", task.getImageId()));
if(task.noReboot)
task.setVmCreateImageTaskState(CreateImageState.complete);
}else
continue;
}catch(final Exception ex){
LOG.error("failed to register the image", ex);
task.setVmCreateImageTaskState(CreateImageState.failed);
}
if(!task.noReboot){
try{
final VmInstance vm = task.getVmInstance();
if(vm.getRuntimeState()!=null && "poweredOff".equals(vm.getRuntimeState().getGuestState())){
task.startInstance();
LOG.debug(String.format("Restarting instance %s", vm.getInstanceId()));
}
}catch(final Exception ex){
LOG.error(String.format("failed to start the instance %s", task.instanceId), ex);
task.setVmCreateImageTaskState(CreateImageState.failed);
}
}
}
}
private void processStartingTasks(final List<CreateImageTask> tasks){
/*
* for each starting instance, check the guestState;
* if guestState == "poweredOn":
* mark complete
*/
for(final CreateImageTask task : tasks){
try{
final VmInstance vm = task.getVmInstance();
if(vm.getRuntimeState()!=null && "poweredOn".equals(vm.getRuntimeState().getGuestState())){
task.setVmCreateImageTaskState(CreateImageState.complete);
}
}catch(final Exception ex){
LOG.error(String.format("failed to check the guest state for %s", task.instanceId), ex);
task.setVmCreateImageTaskState(CreateImageState.failed);
}
}
}
private void processCompleteTasks(final List<CreateImageTask> tasks){
for(final CreateImageTask task : tasks){
LOG.info(String.format("CreateImage is done for instance %s; Image %s registered", task.instanceId, task.getImageId()));
createImageTasks.remove(task.instanceId);
}
}
private void processFailedTasks(final List<CreateImageTask> tasks){
for(final CreateImageTask task : tasks){
LOG.error(String.format("CreateImage has failed for instance %s", task.instanceId));
try{
Images.setImageState(task.getImageId(), ImageMetadata.State.failed);
}catch(final Exception ex){
LOG.error("Unable to set image state as failed");
}
createImageTasks.remove(task.instanceId);
}
}
}
private void setVmCreateImageTaskState(final VmCreateImageTask.CreateImageState state){
try ( TransactionResource db =
Entities.transactionFor( VmInstance.class ) ) {
final VmInstance vm = Entities.uniqueResult(VmInstance.named(this.instanceId));
vm.getRuntimeState().setCreateImageTaskState(state);
Entities.persist(vm);
db.commit();
}catch(NoSuchElementException ex){
throw ex;
}catch(Exception ex){
throw Exceptions.toUndeclared(ex);
}
}
/*
* Finds VmInstance. Throws NoSuchElementException if instance can't be found
*/
private VmInstance getVmInstance() throws NoSuchElementException {
try ( TransactionResource db =
Entities.transactionFor( VmInstance.class ) ) {
final VmInstance vm = Entities.uniqueResult(VmInstance.named(this.instanceId));
if(VmState.TERMINATED.equals(vm.getState()) || VmState.BURIED.equals(vm.getState()))
throw new NoSuchElementException();
return vm;
}catch(NoSuchElementException ex){
LOG.error("Unable to find the vm instance (terminated?) - " + this.instanceId);
try{
Images.setImageState(this.getImageId(), ImageMetadata.State.failed);
}catch(final Exception innerEx){
LOG.error("Unable to set image state as failed", ex);
}
createImageTasks.remove(this.instanceId);
throw ex;
}catch(Exception ex){
throw Exceptions.toUndeclared(ex);
}
}
private static List<BlockDeviceMappingItemType> getDeviceMappingsFromImage(final String imageId){
List<BlockDeviceMappingItemType> deviceMaps = Lists.newArrayList();
try ( TransactionResource db =
Entities.transactionFor( VmInstance.class ) ) {
final BlockStorageImageInfo image = (BlockStorageImageInfo) Entities.uniqueResult(BlockStorageImageInfo.named(imageId));
final List<DeviceMapping> dmSet = image.getDeviceMappings();
for(final DeviceMapping dm : dmSet){
final BlockDeviceMappingItemType dmItem = DeviceMappingDetails.INSTANCE.apply(dm);
deviceMaps.add(dmItem);
}
db.commit();
return deviceMaps;
}catch(final Exception ex){
throw Exceptions.toUndeclared(ex);
}
}
private VmCreateImageTask.CreateImageState getVmCreateImageTaskState(){
final VmInstance vm = getVmInstance();
if(vm.getRuntimeState()!=null){
return vm.getRuntimeState().getCreateImageTaskState();
}else
throw Exceptions.toUndeclared(
new EucalyptusCloudException("No Runtime state found for the instance"));
}
private void validateVmInstance() throws Exception{
final VmInstance vm = this.getVmInstance();
if(vm==null)
throw new EucalyptusCloudException("Unable to find the vm");
if( ! VmState.RUNNING.equals(vm.getState()) && ! VmState.STOPPED.equals(vm.getState()))
throw new EucalyptusCloudException("Instance is not in running or stopped state");
if(vm.getBootRecord()==null || ! vm.getBootRecord().isBlockStorage())
throw new EucalyptusCloudException("createImage can be performed only to EBS-backed instances");
final VmRuntimeState runtime = vm.getRuntimeState();
if(runtime!=null && runtime.isCreatingImage()){
throw new EucalyptusCloudException("Existing CreateImage task is found for the instance");
}
if(createImageTasks.containsKey(this.instanceId))
throw new EucalyptusCloudException("Existing CreateImageTask is found");
}
public void create(final String imageId) throws Exception{
try{
LOG.info( String.format( "Starting create image task for %s : %s", this.instanceId, imageId ) );
// will throw Exception if check failed
this.validateVmInstance();
try ( TransactionResource db =
Entities.transactionFor( VmInstance.class ) ) {
final VmInstance vm = Entities.uniqueResult(VmInstance.named(this.instanceId));
if(VmState.STOPPED.equals(vm.getState()) && !this.noReboot){
LOG.debug("Reboot is not possible for stopped instance");
this.noReboot=true;
}
vm.getRuntimeState().resetCreateImageTask(CreateImageState.pending, imageId, null, this.noReboot);
Entities.persist(vm);
db.commit();
}catch(NoSuchElementException ex){
throw ex;
}catch(Exception ex){
throw Exceptions.toUndeclared(ex);
}
createImageTasks.put(this.instanceId, this);
return;
}catch(final Exception ex){
throw ex;
}
}
private String getInstanceImageId(){
return this.getVmInstance().getImageId();
}
/*
* @return key-value pair of device - volumeId mapping
*/
private Map.Entry<String, String> getInstanceRootVolume(){
final VmInstance vm = this.getVmInstance();
final List<Map.Entry<String, String>> rootVolumes = Lists.newArrayList();
vm.eachVolumeAttachment(new Predicate<VmVolumeAttachment>(){
@Override
public boolean apply(@Nullable VmVolumeAttachment input) {
if(input.getIsRootDevice())
rootVolumes.add( new AbstractMap.SimpleEntry<String,String>(input.getDevice(), input.getVolumeId()));
return true;
}
});
if(rootVolumes.size()>0)
return rootVolumes.get(0);
else
throw Exceptions.toUndeclared(new EucalyptusCloudException("Root volume is not found"));
}
private List<Map.Entry<String, String>> getInstanceNonRootVolumes(){
final VmInstance vm = this.getVmInstance();
final List<Map.Entry<String, String>> volumes = Lists.newArrayList();
vm.eachVolumeAttachment(new Predicate<VmVolumeAttachment>(){
@Override
public boolean apply(@Nullable VmVolumeAttachment input) {
if(! input.getIsRootDevice())
volumes.add( new AbstractMap.SimpleEntry<String,String>(input.getDevice(), input.getVolumeId()));
return true;
}
});
return volumes;
}
private Boolean isDeleteOnTerminate(final String volumeId){
final VmInstance vm = this.getVmInstance();
final List<String> deleteOnTerminates = Lists.newArrayList();
vm.eachVolumeAttachment(new Predicate<VmVolumeAttachment>(){
@Override
public boolean apply(@Nullable VmVolumeAttachment input) {
if(input.getDeleteOnTerminate())
deleteOnTerminates.add(input.getVolumeId());
return true;
}
});
return deleteOnTerminates.contains(volumeId);
}
private String getImageId(){
return this.imageId;
}
private List<String> getSnapshotIds(){
List<String> snapshots = null;
try ( TransactionResource db =
Entities.transactionFor( VmInstance.class ) ) {
final VmInstance vm = Entities.uniqueResult(VmInstance.named(this.instanceId));
snapshots = Lists.transform(Lists.newArrayList(vm.getRuntimeState().getVmCreateImageTask().getSnapshots()),
new Function<VmCreateImageSnapshot, String>(){
@Override
@Nullable
public String apply(@Nullable VmCreateImageSnapshot input) {
return input.getSnapshotId();
}
});
db.commit();
return snapshots;
}catch(NoSuchElementException ex){
throw ex;
}catch(Exception ex){
throw Exceptions.toUndeclared(ex);
}
}
private List<String> getSnapshotStatus() {
try{
final EucalyptusDescribeSnapshotTask task = new EucalyptusDescribeSnapshotTask(this.getSnapshotIds());
final CheckedListenableFuture<Boolean> result = task.dispatch();
if(result.get()){
final List<Snapshot> snapshots = task.getSnapshots();
final List<String> status = Lists.newArrayList();
for(final Snapshot s : snapshots){
LOG.debug(String.format("[%s] snapshot [%s] creating - %s", this.getImageId(), s.getSnapshotId(), s.getProgress()));
status.add(s.getStatus());
}
return status;
}else
throw new EucalyptusCloudException("Unable to describe snapshots");
}catch(final Exception ex){
throw Exceptions.toUndeclared(ex);
}
}
private void createSnapshot(){
/*
* take the snapshot of root device always
* if block-device-mapping is not requested explicitly, take snapshots of all attached volumes
*/
final List<Map.Entry<String, String>> volumesToSnapshot =
Lists.newArrayList();
volumesToSnapshot.add(this.getInstanceRootVolume());
// blockDevices might have devices that should be suppressed as well as ephemeral devices
// first check if there are any volumes that should be suppressed
List<String> suppressedDevice = new ArrayList<String>();
for(BlockDeviceMappingItemType device : blockDevices){
if(device.getNoDevice() != null)
suppressedDevice.add(device.getDeviceName());
}
// device -> volume-id
for(Map.Entry<String, String> vol:getInstanceNonRootVolumes()){
if (!suppressedDevice.contains(vol.getKey()))
volumesToSnapshot.add(vol);
}
Boolean isRootDevice = true;
for(final Map.Entry<String,String> volume: volumesToSnapshot){
final String deviceName = volume.getKey();
final String volumeId = volume.getValue();
String snapshotId = null;
try{
final EucalyptusCreateSnapshotTask task = new EucalyptusCreateSnapshotTask(volumeId,
String.format("Created by CreateImage(%s) for %s from %s", instanceId, getImageId(), volumeId));
final CheckedListenableFuture<Boolean> result = task.dispatch();
if(result.get()){
snapshotId = task.getSnapshotId();
}else
throw new EucalyptusCloudException(String.format("Unable to create the snapshot from volume %s", volumeId));
}catch(final Exception ex){
throw Exceptions.toUndeclared(ex);
}
LOG.debug(String.format("Created snapshot %s from volume %s for device %s", snapshotId, volumeId, deviceName));
try ( TransactionResource db =
Entities.transactionFor( VmInstance.class ) ) {
final VmInstance vm = Entities.uniqueResult(VmInstance.named(this.instanceId));
vm.getRuntimeState().getVmCreateImageTask().addSnapshot(deviceName, snapshotId, isRootDevice, isDeleteOnTerminate(volumeId));
isRootDevice=false;
Entities.persist(vm);
db.commit();
}catch(final Exception ex){
LOG.error("failed to add new snapshot", ex);
throw Exceptions.toUndeclared(ex);
}
}
}
private List<VmCreateImageSnapshot> getSnapshots(){
try ( TransactionResource db =
Entities.transactionFor( VmInstance.class ) ) {
final VmInstance vm = Entities.uniqueResult(VmInstance.named(this.instanceId));
final List<VmCreateImageSnapshot> snapshots =
Lists.newArrayList(vm.getRuntimeState().getVmCreateImageTask().getSnapshots());
db.commit();
return snapshots;
}catch(final Exception ex){
LOG.error("failed to pull snapshot info", ex);
throw Exceptions.toUndeclared(ex);
}
}
private class CreateImageStopInstanceCallback extends StopInstanceCallback{
@Override
public void fire(ClusterStopInstanceResponseType msg) {
if(!msg.get_return()){
LOG.error(String.format("failed to stop instance %s", CreateImageTask.this.instanceId));
CreateImageTask.this.setVmCreateImageTaskState(CreateImageState.failed);
}else{
CreateImageTask.this.setVmCreateImageTaskState(CreateImageState.guest_stopping);
}
}
}
private class CreateImageStartInstanceCallback extends StartInstanceCallback{
@Override
public void fire(ClusterStartInstanceResponseType msg) {
if(!msg.get_return()){
LOG.error(String.format("failed to start instance %s", CreateImageTask.this.instanceId));
CreateImageTask.this.setVmCreateImageTaskState(CreateImageState.failed);
}else{
final CreateImageState lastState = CreateImageTask.this.getVmCreateImageTaskState();
if( !(lastState == CreateImageState.failed || lastState== CreateImageState.complete))
CreateImageTask.this.setVmCreateImageTaskState(CreateImageState.guest_starting);
}
}
}
private void stopInstance() {
final VmInstance vm = this.getVmInstance( );
VmInstances.stopVmInstance( vm, new CreateImageStopInstanceCallback( ) );
}
private void startInstance() {
final VmInstance vm = this.getVmInstance( );
VmInstances.startVmInstance( vm, new CreateImageStartInstanceCallback( ) );
}
private void registerImage(){
try{
final String imageId = this.getImageId();
if(imageId==null)
throw new EucalyptusCloudException("Image Id should be available before full registration");
final List<VmCreateImageSnapshot> snapshots = this.getSnapshots();
final List<BlockDeviceMappingItemType> devices = Lists.newArrayList();
String rootDeviceName = null;
for(final VmCreateImageSnapshot snapshot : snapshots){
if(snapshot.isRootDevice())
rootDeviceName = snapshot.getDeviceName();
final BlockDeviceMappingItemType device = new BlockDeviceMappingItemType();
device.setDeviceName(snapshot.getDeviceName());
final EbsDeviceMapping ebsMap = new EbsDeviceMapping();
ebsMap.setSnapshotId(snapshot.getSnapshotId());
device.setEbs(ebsMap);
devices.add(device);
}
final AccountFullName accountFullName = AccountFullName.getInstance(this.accountNumber);
final ImageInfo updatedImage =
Images.updateWithDeviceMapping(imageId, accountFullName, rootDeviceName, devices );
}catch(final Exception ex){
throw Exceptions.toUndeclared(ex);
}
}
private class EucalyptusDescribeSnapshotTask extends EucalyptusActivityTask<ComputeMessage, Eucalyptus> {
private List<Snapshot> snapshots = null;
private List<String> snapshotIds = null;
private EucalyptusDescribeSnapshotTask(final List<String> snapshotIds){
this.snapshotIds = snapshotIds;
}
private DescribeSnapshotsType describeSnapshots(){
final DescribeSnapshotsType req = new DescribeSnapshotsType();
req.getFilterSet( ).add( Filter.filter( "snapshot-id", this.snapshotIds ) );
return req;
}
@Override
void dispatchInternal(Checked<ComputeMessage> callback) {
final DispatchingClient<ComputeMessage, Compute> client = this.getClient(Compute.class);
client.dispatch(describeSnapshots(), callback);
}
@Override
void dispatchSuccess(ComputeMessage response) {
final DescribeSnapshotsResponseType resp = (DescribeSnapshotsResponseType) response;
this.snapshots = resp.getSnapshotSet();
}
public List<Snapshot> getSnapshots(){
return this.snapshots;
}
}
private class EucalyptusCreateSnapshotTask extends EucalyptusActivityTask<ComputeMessage, Eucalyptus> {
private String volumeId = null;
private String snapshotId = null;
private String description = null;
private EucalyptusCreateSnapshotTask(final String volumeId, String description){
this.volumeId = volumeId;
this.description = description;
}
private CreateSnapshotType createSnapshot(){
final CreateSnapshotType req = new CreateSnapshotType();
req.setVolumeId(volumeId);
req.setDescription(description);
return req;
}
@Override
void dispatchInternal(Checked<ComputeMessage> callback) {
final DispatchingClient<ComputeMessage, Eucalyptus> client = this.getClient(Eucalyptus.class);
client.dispatch(createSnapshot(), callback);
}
@Override
void dispatchSuccess(ComputeMessage response) {
final CreateSnapshotResponseType resp = (CreateSnapshotResponseType) response;
this.snapshotId = resp.getSnapshot().getSnapshotId();
}
public String getSnapshotId(){
return this.snapshotId;
}
}
private abstract class EucalyptusActivityTask <TM extends BaseMessage, TC extends ComponentId>{
protected EucalyptusActivityTask(){}
final CheckedListenableFuture<Boolean> dispatch( ) {
try {
final CheckedListenableFuture<Boolean> future = Futures.newGenericeFuture();
dispatchInternal( new Callback.Checked<TM>(){
@Override
public void fireException( final Throwable throwable ) {
try {
dispatchFailure( throwable );
} finally {
future.set( false );
}
}
@Override
public void fire( final TM response ) {
try {
dispatchSuccess( response );
} finally {
future.set( true );
}
}
} );
return future;
} catch ( Exception e ) {
LOG.error( e, e );
}
return Futures.predestinedFuture( false );
}
abstract void dispatchInternal( Callback.Checked<TM> callback );
void dispatchFailure( Throwable throwable ) {
LOG.error( "CreateImage task error", throwable );
}
abstract void dispatchSuccess(TM response );
protected <T extends ComponentId> DispatchingClient<ComputeMessage, T> getClient( final Class<T> component ) {
try{
final DispatchingClient<ComputeMessage, T> client =
new DispatchingClient<>(AccountFullName.getInstance( CreateImageTask.this.accountNumber ), component );
client.init();
return client;
}catch(Exception e){
throw Exceptions.toUndeclared(e);
}
}
}
}