/*************************************************************************
* Copyright 2009-2016 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.
*
* This file may incorporate work covered under the following copyright
* and permission notice:
*
* Software License Agreement (BSD License)
*
* Copyright (c) 2008, Regents of the University of California
* All rights reserved.
*
* Redistribution and use of this software in source and binary forms,
* with or without modification, are permitted provided that the
* following conditions are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE
* THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL,
* COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE,
* AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
* IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA,
* SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY,
* WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION,
* REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO
* IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT
* NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS.
************************************************************************/
package com.eucalyptus.vm;
import com.eucalyptus.auth.AccessKeys;
import com.eucalyptus.auth.AuthException;
import com.eucalyptus.auth.login.AuthenticationException;
import com.eucalyptus.auth.principal.AccessKey;
import com.eucalyptus.auth.principal.AccountFullName;
import com.eucalyptus.auth.principal.OwnerFullName;
import com.eucalyptus.blockstorage.Volumes;
import com.eucalyptus.cluster.common.internal.ResourceToken;
import com.eucalyptus.cloud.VmInstanceLifecycleHelpers;
import com.eucalyptus.cloud.VmInstanceToken;
import com.eucalyptus.cloud.run.*;
import com.eucalyptus.cloud.run.Allocations.Allocation;
import com.eucalyptus.cluster.callback.RebootCallback;
import com.eucalyptus.cluster.common.msgs.ClusterBundleInstanceType;
import com.eucalyptus.cluster.common.msgs.ClusterCancelBundleTaskType;
import com.eucalyptus.component.ServiceConfiguration;
import com.eucalyptus.component.Topology;
import com.eucalyptus.component.annotation.ComponentNamed;
import com.eucalyptus.cluster.common.ClusterController;
import com.eucalyptus.compute.ClientComputeException;
import com.eucalyptus.compute.ClientUnauthorizedComputeException;
import com.eucalyptus.compute.ClusterComputeServiceUnavailableException;
import com.eucalyptus.compute.ComputeException;
import com.eucalyptus.compute.common.CloudMetadatas;
import com.eucalyptus.compute.common.GroupItemType;
import com.eucalyptus.compute.common.ImageMetadata;
import com.eucalyptus.compute.common.InstanceBlockDeviceMappingItemType;
import com.eucalyptus.compute.common.MonitorInstanceState;
import com.eucalyptus.compute.common.ReservationInfoType;
import com.eucalyptus.compute.common.ResourceTag;
import com.eucalyptus.compute.common.RunningInstancesItemType;
import com.eucalyptus.compute.common.SecurityGroupIdSetItemType;
import com.eucalyptus.compute.common.TerminateInstancesItemType;
import com.eucalyptus.compute.common.backend.BundleInstanceResponseType;
import com.eucalyptus.compute.common.backend.BundleInstanceType;
import com.eucalyptus.compute.common.backend.CancelBundleTaskResponseType;
import com.eucalyptus.compute.common.backend.CancelBundleTaskType;
import com.eucalyptus.compute.common.backend.CreatePlacementGroupResponseType;
import com.eucalyptus.compute.common.backend.CreatePlacementGroupType;
import com.eucalyptus.compute.common.backend.DeletePlacementGroupResponseType;
import com.eucalyptus.compute.common.backend.DeletePlacementGroupType;
import com.eucalyptus.compute.common.backend.DescribeBundleTasksResponseType;
import com.eucalyptus.compute.common.backend.DescribeBundleTasksType;
import com.eucalyptus.compute.common.backend.GetConsoleOutputResponseType;
import com.eucalyptus.compute.common.backend.GetConsoleOutputType;
import com.eucalyptus.compute.common.backend.GetConsoleScreenshotResponseType;
import com.eucalyptus.compute.common.backend.GetConsoleScreenshotType;
import com.eucalyptus.compute.common.backend.GetPasswordDataResponseType;
import com.eucalyptus.compute.common.backend.GetPasswordDataType;
import com.eucalyptus.compute.common.backend.ModifyInstanceAttributeResponseType;
import com.eucalyptus.compute.common.backend.ModifyInstanceAttributeType;
import com.eucalyptus.compute.common.backend.ModifyInstancePlacementResponseType;
import com.eucalyptus.compute.common.backend.ModifyInstancePlacementType;
import com.eucalyptus.compute.common.backend.MonitorInstancesResponseType;
import com.eucalyptus.compute.common.backend.MonitorInstancesType;
import com.eucalyptus.compute.common.backend.RebootInstancesResponseType;
import com.eucalyptus.compute.common.backend.RebootInstancesType;
import com.eucalyptus.compute.common.backend.ReportInstanceStatusResponseType;
import com.eucalyptus.compute.common.backend.ReportInstanceStatusType;
import com.eucalyptus.compute.common.backend.ResetInstanceAttributeResponseType;
import com.eucalyptus.compute.common.backend.ResetInstanceAttributeType;
import com.eucalyptus.compute.common.backend.RunInstancesResponseType;
import com.eucalyptus.compute.common.backend.RunInstancesType;
import com.eucalyptus.compute.common.backend.StartInstancesResponseType;
import com.eucalyptus.compute.common.backend.StartInstancesType;
import com.eucalyptus.compute.common.backend.StopInstancesResponseType;
import com.eucalyptus.compute.common.backend.StopInstancesType;
import com.eucalyptus.compute.common.backend.TerminateInstancesResponseType;
import com.eucalyptus.compute.common.backend.TerminateInstancesType;
import com.eucalyptus.compute.common.backend.UnmonitorInstancesResponseType;
import com.eucalyptus.compute.common.backend.UnmonitorInstancesType;
import com.eucalyptus.compute.common.internal.identifier.InvalidResourceIdentifier;
import com.eucalyptus.compute.common.internal.identifier.ResourceIdentifiers;
import com.eucalyptus.compute.common.internal.images.BlockStorageImageInfo;
import com.eucalyptus.compute.common.internal.images.ImageInfo;
import com.eucalyptus.compute.common.internal.images.KernelImageInfo;
import com.eucalyptus.compute.common.internal.images.RamdiskImageInfo;
import com.eucalyptus.compute.common.internal.keys.NoSuchKeyMetadataException;
import com.eucalyptus.compute.common.internal.network.NetworkGroup;
import com.eucalyptus.compute.common.internal.network.NoSuchGroupMetadataException;
import com.eucalyptus.compute.common.internal.tags.Filter;
import com.eucalyptus.compute.common.internal.tags.Filters;
import com.eucalyptus.compute.common.internal.tags.Tag;
import com.eucalyptus.compute.common.internal.tags.TagSupport;
import com.eucalyptus.compute.common.internal.tags.Tags;
import com.eucalyptus.compute.common.internal.util.*;
import com.eucalyptus.compute.common.internal.vm.MigrationState;
import com.eucalyptus.compute.common.internal.vm.VmBundleTask;
import com.eucalyptus.compute.common.internal.vm.VmBundleTask.BundleState;
import com.eucalyptus.compute.common.internal.vm.VmInstance;
import com.eucalyptus.compute.common.internal.vm.VmInstance.VmState;
import com.eucalyptus.compute.common.internal.vm.VmInstance.VmStateSet;
import com.eucalyptus.compute.common.internal.vm.VmVolumeAttachment;
import com.eucalyptus.compute.common.internal.vmtypes.VmType;
import com.eucalyptus.compute.common.internal.vpc.NetworkInterface;
import com.eucalyptus.compute.vpc.*;
import com.eucalyptus.context.Context;
import com.eucalyptus.context.Contexts;
import com.eucalyptus.crypto.Hmac;
import com.eucalyptus.crypto.util.B64;
import com.eucalyptus.entities.Entities;
import com.eucalyptus.entities.TransactionException;
import com.eucalyptus.entities.TransactionResource;
import com.eucalyptus.images.Images;
import com.eucalyptus.network.NetworkGroups;
import com.eucalyptus.records.EventRecord;
import com.eucalyptus.records.EventType;
import com.eucalyptus.records.Logs;
import com.eucalyptus.system.tracking.MessageContexts;
import com.eucalyptus.util.EucalyptusCloudException;
import com.eucalyptus.util.Exceptions;
import com.eucalyptus.util.RestrictedTypes;
import com.eucalyptus.util.TypeMappers;
import com.eucalyptus.util.async.AsyncRequests;
import com.eucalyptus.util.async.Request;
import com.eucalyptus.vm.Bundles.BundleCallback;
import com.eucalyptus.vmtypes.VmTypes;
import com.eucalyptus.ws.util.HmacUtils;
import com.google.common.base.*;
import com.google.common.collect.*;
import com.eucalyptus.cluster.common.msgs.ClusterGetConsoleOutputResponseType;
import com.eucalyptus.cluster.common.msgs.ClusterGetConsoleOutputType;
import com.eucalyptus.cluster.common.msgs.ClusterRebootInstancesResponseType;
import com.eucalyptus.cluster.common.msgs.ClusterRebootInstancesType;
import net.sf.json.JSONArray;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;
import org.apache.log4j.Logger;
import org.bouncycastle.util.encoders.DecoderException;
import javax.persistence.EntityTransaction;
import java.security.MessageDigest;
import java.util.*;
import static com.eucalyptus.cloud.run.VerifyMetadata.ImageInstanceTypeVerificationException;
import static com.eucalyptus.compute.common.internal.vm.VmInstances.TerminatedInstanceException;
import static com.eucalyptus.util.Strings.substringAfter;
import static com.eucalyptus.util.Strings.substringBefore;
@SuppressWarnings( "UnusedDeclaration" )
@ComponentNamed("computeVmControl")
public class VmControl {
private static Logger LOG = Logger.getLogger( VmControl.class );
public static RunInstancesResponseType runInstances( RunInstancesType request ) throws Exception {
RunInstancesResponseType reply = request.getReply( );
Allocation allocInfo = Allocations.run( request );
final EntityTransaction db = Entities.get( VmInstance.class );
try {
if ( !Strings.isNullOrEmpty( allocInfo.getClientToken( ) ) ) {
final List<VmInstance> instances = VmInstances.listByClientToken(
allocInfo.getOwnerFullName( ).asAccountFullName( ),
allocInfo.getClientToken( ), RestrictedTypes.filterPrivileged( ) );
if ( !instances.isEmpty( ) ) {
final VmInstance vm = instances.get( 0 );
final Map<String, List<Tag>> tagsMap = TagSupport.forResourceClass( VmInstance.class )
.getResourceTagMap( AccountFullName.getInstance( vm.getOwnerAccountNumber( ) ),
Iterables.transform( instances, CloudMetadatas.toDisplayName( ) ) );
final ReservationInfoType reservationInfoType = TypeMappers.transform( vm, ReservationInfoType.class );
for ( final VmInstance instance : instances ) {
final RunningInstancesItemType item = VmInstance.transform( instance );
Tags.addFromTags( item.getTagSet( ), ResourceTag.class, tagsMap.get( item.getInstanceId( ) ) );
reservationInfoType.getInstancesSet( ).add( item );
}
reply.setRsvInfo( reservationInfoType );
return reply;
}
}
Predicates.and( VerifyMetadata.get( ), AdmissionControl.run( ), ContractEnforcement.run( ) ).apply( allocInfo );
allocInfo.commit( );
ReservationInfoType reservation = new ReservationInfoType(
allocInfo.getReservationId( ),
allocInfo.getOwnerFullName( ).getAccountNumber( ),
Collections2.transform(
allocInfo.getNetworkGroups( ),
TypeMappers.lookup( NetworkGroup.class, GroupItemType.class ) ) );
reply.setRsvInfo( reservation );
final Map<String, List<Tag>> tagsMap = TagSupport.forResourceClass( VmInstance.class )
.getResourceTagMap( allocInfo.getOwnerFullName( ).asAccountFullName( ), allocInfo.getInstanceIds( ) );
for ( VmInstanceToken allocToken : allocInfo.getAllocationTokens( ) ) {
final RunningInstancesItemType item = VmInstance.transform( allocToken.getVmInstance( ) );
Tags.addFromTags( item.getTagSet( ), ResourceTag.class, tagsMap.get( item.getInstanceId( ) ) );
reservation.getInstancesSet( ).add( item );
}
db.commit( );
} catch ( Exception ex ) {
allocInfo.abort( );
throwClientIfFound( ex, ImageInstanceTypeVerificationException.class, "InvalidParameterCombination" );
throwClientIfFound( ex, NotEnoughPrivateAddressResourcesException.class, "InsufficientFreeAddressesInSubnet" );
throwClientIfFound( ex, PrivateAddressResourceAllocationException.class, "InvalidIPAddress.InUse" );
final NotEnoughResourcesException e1 = Exceptions.findCause( ex, NotEnoughResourcesException.class );
if ( e1 != null ) throw new ClusterComputeServiceUnavailableException( "InsufficientInstanceCapacity", e1.getMessage( ) );
final IllegalMetadataAccessException e2 = Exceptions.findCause( ex, IllegalMetadataAccessException.class );
if ( e2 != null ) throw new ClientUnauthorizedComputeException( e2.getMessage( ) );
throwClientIfFound( ex, NoSuchKeyMetadataException.class, "InvalidKeyPair.NotFound" );
throwClientIfFound( ex, VpcRequiredMetadataException.class, "VPCIdNotSpecified", "Default VPC not found, please specify a subnet.");
throwClientIfFound( ex, InvalidParameterCombinationMetadataException.class, "InvalidParameterCombination" );
throwClientIfFound( ex, NetworkInterfaceInUseMetadataException.class, "InvalidNetworkInterface.InUse" );
throwClientIfFound( ex, SecurityGroupLimitMetadataException.class, "SecurityGroupLimitExceeded", "Security group limit exceeded" );
throwClientIfFound( ex, InvalidMetadataException.class, "InvalidParameterValue" );
throwClientIfFound( ex, NoSuchImageIdException.class, "InvalidAMIID.NotFound" );
throwClientIfFound( ex, NoSuchSubnetMetadataException.class, "InvalidSubnetID.NotFound" );
throwClientIfFound( ex, NoSuchNetworkInterfaceMetadataException.class, "InvalidNetworkInterfaceID.NotFound" );
throwClientIfFound( ex, NoSuchGroupMetadataException.class, "InvalidGroup.NotFound" );
LOG.error( ex, ex );
throw ex;
} finally {
if ( db.isActive() ) db.rollback();
}
MessageContexts.remember(allocInfo.getReservationId(), request.getClass(), request);
for( final VmInstanceToken allocToken : allocInfo.getAllocationTokens()){
MessageContexts.remember(allocToken.getInstanceId(), request.getClass(), request);
}
ClusterAllocator.get( ).apply( allocInfo );
return reply;
}
public ReportInstanceStatusResponseType reportInstanceStatus( final ReportInstanceStatusType request ) {
return request.getReply( );
}
public TerminateInstancesResponseType terminateInstances( final TerminateInstancesType request ) throws EucalyptusCloudException {
final TerminateInstancesResponseType reply = request.getReply( );
final List<String> failedVmList = new ArrayList<>( );
final List<VmInstance> vmList = new ArrayList<>( );
final Context context = Contexts.lookup( );
final Collection<String> identifiers = normalizeIdentifiers( request.getInstancesSet( ) );
for ( String requestedInstanceId : identifiers ) {
try {
VmInstance vm = RestrictedTypes.doPrivileged( requestedInstanceId, VmInstance.class );
if ( !context.isPrivileged( ) && Boolean.TRUE.equals( vm.getDisableApiTermination( ) ) ) {
throw new ClientComputeException( "OperationNotPermitted", "Termination protection enabled for instance " + requestedInstanceId );
}
vmList.add( vm );
} catch ( final ClientComputeException e ) {
throw e;
} catch ( final AuthException | NoSuchElementException e ) {
failedVmList.add( requestedInstanceId );
} catch ( final Exception e ) {
LOG.error( "Error looking up instance for termination: " + requestedInstanceId, e );
failedVmList.add( requestedInstanceId );
}
}
if ( !failedVmList.isEmpty( ) ) {
String failedVms = Joiner.on( ", " ).join( failedVmList );
if ( failedVmList.size( ) > 1 )
throw new ClientComputeException( "InvalidInstanceID.NotFound", "The instance IDs '%s' do not exist", failedVms );
else
throw new ClientComputeException( "InvalidInstanceID.NotFound", "The instance ID '%s' does not exist", failedVms );
}
for ( VmInstance vm : vmList ) {
if (MigrationState.isMigrating(vm)) {
throw new ClientComputeException( "OperationNotPermitted", "Cannot terminate an instance which is currently migrating: %s", vm.getInstanceId() );
}
}
try {
final List<TerminateInstancesItemType> results = reply.getInstancesSet( );
Function<VmInstance,TerminateInstancesItemType> terminateFunction = (VmInstance vm) -> {
String oldState, newState = null;
int oldCode, newCode = 0;
TerminateInstancesItemType result = null;
try {
oldCode = vm.getState( ).getCode( );
oldState = vm.getState( ).getName( );
if ( VmState.STOPPED.apply( vm ) ) {
newCode = VmState.TERMINATED.getCode( );
newState = VmState.TERMINATED.getName( );
VmInstances.terminated( vm );
} else if ( VmStateSet.RUN.apply( vm ) ) {
newCode = VmState.SHUTTING_DOWN.getCode( );
newState = VmState.SHUTTING_DOWN.getName( );
VmInstances.shutDown( vm );
} else if ( VmState.SHUTTING_DOWN.apply( vm ) ) {
newCode = VmState.SHUTTING_DOWN.getCode( );
newState = VmState.SHUTTING_DOWN.getName( );
} else if ( VmStateSet.DONE.apply( vm ) ) {
oldCode = newCode = VmState.TERMINATED.getCode( );
oldState = newState = VmState.TERMINATED.getName( );
VmInstances.buried( vm );
}
MessageContexts.remember(vm.getInstanceId(), request.getClass(), request);
result = new TerminateInstancesItemType( vm.getInstanceId( ), oldCode, oldState, newCode, newState );
} catch ( final TerminatedInstanceException e ) {
oldCode = newCode = VmState.TERMINATED.getCode( );
oldState = newState = VmState.TERMINATED.getName( );
try {
VmInstances.buried( vm.getInstanceId( ) );
} catch ( TransactionException e1 ) {
throw Exceptions.toUndeclared( e1 );
}
result = new TerminateInstancesItemType( vm.getInstanceId( ), oldCode, oldState, newCode, newState );
} catch ( final NoSuchElementException e ) {
LOG.debug( "Ignoring terminate request for non-existent instance: " + vm.getInstanceId( ) );
} catch ( final Exception e ) {
throw Exceptions.toUndeclared( e );
}
return result;
};
Function<VmInstance, TerminateInstancesItemType> terminateTx = Entities.asTransaction( VmInstance.class, terminateFunction, VmInstances.TX_RETRIES );
for ( VmInstance vm : vmList ) {
try {
TerminateInstancesItemType termInstance = terminateTx.apply( vm );
if ( termInstance != null ) {
results.add( termInstance );
}
} catch ( Exception ex ) {
LOG.error( ex );
Logs.extreme( ).error( ex, ex );
}
}
reply.set_return( !reply.getInstancesSet( ).isEmpty( ) );
return reply;
} catch ( final Throwable e ) {
LOG.error( e );
LOG.debug( e, e );
throw new EucalyptusCloudException( e.getMessage( ) );
}
}
public RebootInstancesResponseType rebootInstances( final RebootInstancesType request ) throws EucalyptusCloudException {
final RebootInstancesResponseType reply = request.getReply( );
try {
List <String> instanceSet = normalizeIdentifiers( request.getInstancesSet() );
ArrayList <String> noAccess = new ArrayList<>();
ArrayList <String> migrating = new ArrayList<>();
ArrayList <String> noSuchElement = new ArrayList<>();
for( int i = 0; i < instanceSet.size(); i++) {
String currentInstance = instanceSet.get(i);
try {
final VmInstance v = VmInstances.lookup( currentInstance );
if( !RestrictedTypes.filterPrivileged( ).apply( v ) ) {
noAccess.add( currentInstance );
}
if( MigrationState.isMigrating( v ) ) {
migrating.add( currentInstance );
}
} catch (NoSuchElementException nse) {
if( !( nse instanceof TerminatedInstanceException ) ) {
noSuchElement.add( currentInstance );
} else {
instanceSet.remove(i--);
}
}
if( ( i == instanceSet.size( ) - 1 ) && ( !noSuchElement.isEmpty( ) ) ) {
String outList = noSuchElement.toString();
throw new EucalyptusCloudException( "No such instance(s): " + outList.substring( 1, outList.length( ) - 1 ) );
} else if( ( i == instanceSet.size( ) - 1 ) && ( !noAccess.isEmpty( ) ) ) {
String outList = noAccess.toString( );
throw new EucalyptusCloudException( "Permission denied for vm(s): " + outList.substring( 1, outList.length( ) - 1 ) );
} else if( ( i == instanceSet.size( ) - 1 ) && ( !migrating.isEmpty( ) ) ) {
String outList = noAccess.toString( );
throw new EucalyptusCloudException( "Cannot reboot an instances which is currently migrating: " + outList.substring( 1, outList.length( ) - 1 ) );
}
}
final boolean result = Iterables.all( instanceSet , new Predicate<String>( ) {
@Override
public boolean apply( final String instanceId ) {
try {
final VmInstance v = VmInstances.lookup( instanceId );
final Request<ClusterRebootInstancesType, ClusterRebootInstancesResponseType> req =
AsyncRequests.newRequest( new RebootCallback( v.getInstanceId( ) ) );
ServiceConfiguration ccConfig = Topology.lookup( ClusterController.class, v.lookupPartition( ) );
req.dispatch( ccConfig );
return true;
} catch ( final NoSuchElementException e ) {
return false;
}
}
} );
reply.set_return( result );
return reply;
} catch ( final Exception e ) {
LOG.error( e );
LOG.debug( e, e );
throw new EucalyptusCloudException( e.getMessage( ) );
}
}
public GetConsoleOutputResponseType getConsoleOutput( final GetConsoleOutputType request ) throws EucalyptusCloudException {
final String instanceId = normalizeIdentifier( request.getInstanceId( ) );
VmInstance v;
try {
v = VmInstances.lookup( instanceId );
} catch ( final NoSuchElementException ex ) {
throw new ClientComputeException( "InvalidInstanceID.NotFound", "The instance ID '"+instanceId+"' does not exist" );
}
if ( !RestrictedTypes.filterPrivileged( ).apply( v ) ) {
throw new EucalyptusCloudException( "Permission denied for vm: " + instanceId );
} else if ( !VmState.RUNNING.apply( v ) ) {
throw new EucalyptusCloudException( "Instance " + instanceId + " is not in a running state." );
} else {
ServiceConfiguration ccConfig;
try {
ccConfig = Topology.lookup( ClusterController.class, v.lookupPartition( ) );
} catch ( final NoSuchElementException e1 ) {
throw new ComputeException( "InternalError", "Failed to find enabled cluster controller for cluster '"+v.getPartition()+"'");
}
try {
final ClusterGetConsoleOutputResponseType response =
AsyncRequests.sendSync( ccConfig, new ClusterGetConsoleOutputType( instanceId ) );
GetConsoleOutputResponseType reply = request.getReply();
reply.setInstanceId( instanceId );
reply.setTimestamp( response.getTimestamp() );
reply.setOutput( response.getOutput() );
return reply;
} catch( Exception e ) {
LOG.error( e, e );
throw new ComputeException( "InternalError", "Error processing request: " + e.getMessage( ) );
}
}
}
public GetConsoleScreenshotResponseType getConsoleScreenshot( final GetConsoleScreenshotType request ) throws EucalyptusCloudException {
GetConsoleScreenshotResponseType reply = request.getReply();
reply.setInstanceId( normalizeIdentifier( request.getInstanceId( ) ) );
return reply;
}
public DescribeBundleTasksResponseType describeBundleTasks( final DescribeBundleTasksType request ) throws EucalyptusCloudException {
final DescribeBundleTasksResponseType reply = request.getReply( );
final boolean showAll = request.getBundleIds( ).remove( "verbose" ) || !request.getBundleIds( ).isEmpty( );
final Filter filter = Filters.generate( request.getFilterSet(), VmBundleTask.class );
try ( final TransactionResource db = Entities.transactionFor( VmInstance.class ) ) {
// Get all from cache that match filters......
final Predicate<? super VmBundleTask> filteredAndBundling =
Predicates.and(filter.asPredicate(), VmBundleTask.Filters.BUNDLING);
Collection<VmBundleTask> cachedValues = Bundles.getPreviousBundleTasks().values();
final Map<String, VmBundleTask> cachedBundles = buildMap(Collections2.filter(cachedValues, filteredAndBundling));
final Predicate<? super VmInstance> requestedAndAccessible = CloudMetadatas.filteringFor( VmInstance.class )
.byId( toInstanceIds( request.getBundleIds( ) ) )
.byPrivileges()
.buildPredicate();
// Get all from the db that are owned
final Predicate<? super VmInstance> filteredInstances =
Predicates.compose( filter.asPredicate(), VmInstances.bundleTask() );
final Filter noFilters = Filters.generate( new ArrayList<com.eucalyptus.compute.common.Filter>(), VmBundleTask.class );
final Context ctx = Contexts.lookup();
final OwnerFullName ownerFullName = ( ctx.isAdministrator( ) && showAll )
? null
: ctx.getUserFullName( ).asAccountFullName( );
final Collection<VmInstance> dbBundles =
VmInstances.list( ownerFullName, noFilters.asCriterion(), noFilters.getAliases(), requestedAndAccessible );
for ( final VmInstance v : dbBundles) {
if ( filteredInstances.apply(v) && VmInstance.Filters.BUNDLING.apply(v)) {
LOG.debug("Getting current bundle for " + v.getInstanceId());
reply.getBundleTasks( ).add( Bundles.transform( v.getRuntimeState( ).getBundleTask( ) ) );
} else {
if ( !VmInstance.Filters.BUNDLING.apply(v) && cachedBundles.containsKey(v.getInstanceId())) {
LOG.debug("Getting previous bundle for " + v.getInstanceId());
reply.getBundleTasks( ).add( Bundles.transform( cachedBundles.get(v.getInstanceId())));
}
}
}
} catch ( Exception ex ) {
Logs.exhaust( ).error( ex, ex );
throw new EucalyptusCloudException( ex );
}
return reply;
}
private static Map<String, VmBundleTask> buildMap(Collection<VmBundleTask> tasks) {
Map<String, VmBundleTask> map = Maps.newHashMap();
for (VmBundleTask task: tasks) {
map.put(task.getInstanceId(), task);
}
return map;
}
public UnmonitorInstancesResponseType unmonitorInstances( final UnmonitorInstancesType request ) throws EucalyptusCloudException {
final UnmonitorInstancesResponseType reply = request.getReply();
final List<String> instanceSet = normalizeIdentifiers( request.getInstancesSet( ) );
final List<MonitorInstanceState> monitorFalseList = Lists.newArrayList( );
for ( final String inst : instanceSet ) {
final MonitorInstanceState monitorInstanceState = new MonitorInstanceState();
monitorInstanceState.setInstanceId( inst );
monitorInstanceState.setMonitoringState( "disabled" );
monitorFalseList.add( monitorInstanceState );
}
reply.setInstancesSet( SetMonitorFunction.INSTANCE.apply( monitorFalseList ) );
return reply;
}
public StartInstancesResponseType startInstances( final StartInstancesType request ) throws Exception {
final StartInstancesResponseType reply = request.getReply( );
for ( String instanceId : normalizeIdentifiers( request.getInstancesSet() ) ) {
try ( final TransactionResource db = Entities.transactionFor( VmInstance.class ) ) {//scope for transaction
final VmInstance vm = RestrictedTypes.doPrivileged( instanceId, VmInstance.class );
if ( VmState.STOPPED.equals( vm.getState( ) ) ) {
Allocation allocInfo = Allocations.start( vm );
VmInstanceLifecycleHelpers.get( ).prepareAllocation( vm, allocInfo );
try {//scope for allocInfo
AdmissionControl.run( ).apply( allocInfo );
for ( final ResourceToken resourceToken : allocInfo.getAllocationTokens( ) ) {
VmInstanceLifecycleHelpers.get( ).startVmInstance( resourceToken, vm );
}
final int oldCode = vm.getState( ).getCode( );
final int newCode = VmState.PENDING.getCode( );
final String oldState = vm.getState( ).getName( );
final String newState = VmState.PENDING.getName( );
vm.setState( VmState.PENDING );
db.commit( );
MessageContexts.remember(instanceId, request.getClass(), request);
ClusterAllocator.get( ).apply( allocInfo );
reply.getInstancesSet( ).add( new TerminateInstancesItemType( vm.getInstanceId( ), oldCode, oldState, newCode, newState ) );
} catch ( Exception ex ) {
db.rollback( );
allocInfo.abort( );
throw ex;
}
}
} catch ( Exception ex1 ) {
LOG.trace( ex1, ex1 );
throw ex1;
}
}
return reply;
}
public StopInstancesResponseType stopInstances( final StopInstancesType request ) throws EucalyptusCloudException {
final StopInstancesResponseType reply = request.getReply( );
try {
final Context ctx = Contexts.lookup( );
final List<TerminateInstancesItemType> results = reply.getInstancesSet( );
Predicate<String> stopPredicate = new Predicate<String>( ) {
@Override
public boolean apply( final String instanceId ) {
try {
final VmInstance v = VmInstances.lookup( instanceId );
if ( RestrictedTypes.filterPrivileged( ).apply( v ) ) {
if ( !MigrationState.isMigrating( v ) && v.getBootRecord( ).getMachine( ) instanceof BlockStorageImageInfo ) {
final int oldCode = v.getState( ).getCode( ), newCode = VmState.STOPPING.getCode( );
final String oldState = v.getState( ).getName( ), newState = VmState.STOPPING.getName( );
TerminateInstancesItemType termInfo = new TerminateInstancesItemType( v.getInstanceId( ), oldCode, oldState, newCode, newState );
if ( !results.contains( termInfo ) ) {
results.add( termInfo );
}
VmInstances.stopped( v );
}
}
MessageContexts.remember(instanceId, request.getClass(), request);
return true;//GRZE: noop needs to be true to continue Iterables.all
} catch ( final NoSuchElementException e ) {
try {
VmInstances.stopped( instanceId );
return true;
} catch ( final NoSuchElementException e1 ) {
return true;
} catch ( TransactionException ex ) {
Logs.extreme( ).error( ex, ex );
return true;
}
} catch ( Exception ex ) {
LOG.error( ex );
Logs.extreme( ).error( ex, ex );
throw Exceptions.toUndeclared( ex );
}
}
};
final List<String> identifiers = normalizeIdentifiers( request.getInstancesSet( ) );
for( final String instanceId : identifiers ){
try {
final VmInstance vm = VmInstances.lookup( instanceId );
if ( !vm.isBlockStorage() )
throw new ClientComputeException( "UnsupportedOperation",
String.format( "The instance '%s' does not have an 'ebs' root device type and cannot be stopped.", instanceId ) );
} catch ( final TerminatedInstanceException ex ) {
throw new ClientComputeException( "IncorrectInstanceState",
String.format( "This instance '%s' is not in a state from which it can be stopped.", instanceId ) );
} catch ( final NoSuchElementException ex ) {
throw new ClientComputeException( "InvalidInstanceID.NotFound",
String.format( "The instance ID '%s' does not exist", instanceId ) );
}
}
for(final String instanceId : identifiers){
final VmInstance vm = VmInstances.lookup( instanceId );
// EUCA-9596: forget windows password
if(ImageMetadata.Platform.windows.name().equals(vm.getPlatform())){
try ( final TransactionResource db =
Entities.transactionFor( VmInstance.class )){
try{
final VmInstance updatedVm = Entities.uniqueResult(vm);
updatedVm.updatePasswordData(null);
Entities.persist(updatedVm);
db.commit();
}catch(final Exception ex){
throw new EucalyptusCloudException("Failed to erase Windows password");
}
}
}
}
Predicate<String> stopTx = Entities.asTransaction( VmInstance.class, stopPredicate );
Iterables.all( identifiers, stopTx );
reply.set_return( !reply.getInstancesSet( ).isEmpty( ) );
return reply;
} catch( final EucalyptusCloudException ex){
throw ex;
} catch ( final Throwable e ) {
LOG.error( e );
LOG.debug( e, e );
throw new EucalyptusCloudException( e.getMessage( ) );
}
}
public ResetInstanceAttributeResponseType resetInstanceAttribute( final ResetInstanceAttributeType request )
throws EucalyptusCloudException {
final ResetInstanceAttributeResponseType reply = request.getReply( );
final String instanceId = normalizeIdentifier( request.getInstanceId( ) );
try ( final TransactionResource tx = Entities.transactionFor( VmInstance.class ) ) {
final VmInstance vm = RestrictedTypes.doPrivileged( instanceId, VmInstance.class );
if ( "sourceDestCheck".equals( request.getAttribute( ) ) ) {
if ( vm.getNetworkInterfaces( ) != null ) {
for ( final NetworkInterface networkInterface : vm.getNetworkInterfaces( ) ) {
if ( networkInterface.getAttachment( ).getDeviceIndex( ) == 0 ) {
networkInterface.setSourceDestCheck( true );
tx.commit( );
NetworkGroups.flushRules( );
break;
}
}
}
} else if ( "disableApiTermination".equals( request.getAttribute( ) ) ) {
vm.setDisableApiTermination( false );
tx.commit( );
} else if ( VmState.STOPPED.equals( vm.getState( ) ) ) {
if ( request.getAttribute( ).equals( "kernel" ) ) {
String kernelId = vm.getKernelId( );
if ( kernelId == null ) {
vm.getBootRecord( ).setKernel( );
} else {
KernelImageInfo kernelImg = Images.lookupKernel( kernelId );
if ( !ImageMetadata.State.available.equals( kernelImg.getState( ) ) ) {
throw new NoSuchElementException( "InvalidAMIID.NotFound: Unable to start instance with deregistered/failed image : " + kernelImg.getImageName( ) );
}
vm.getBootRecord( ).setKernel( kernelImg );
}
Entities.merge( vm );
tx.commit( );
} else if ( request.getAttribute( ).equals( "ramdisk" ) ) {
String ramdiskId = vm.getRamdiskId( );
if ( ramdiskId == null ) {
vm.getBootRecord( ).setRamdisk( );
} else {
RamdiskImageInfo ramdiskImg = Images.lookupRamdisk( ramdiskId );
if ( !ImageMetadata.State.available.equals( ramdiskImg.getState( ) ) ) {
throw new NoSuchElementException( "InvalidAMIID.NotFound: Unable to start instance with deregistered/failed image : " + ramdiskImg.getImageName( ) );
}
vm.getBootRecord( ).setRamdisk( ramdiskImg );
}
Entities.merge( vm );
tx.commit( );
}
reply.set_return( true );
} else {
throw new EucalyptusCloudException( "IncorrectInstanceState: The instance '" + instanceId + "' is not in the 'stopped' state." );
}
} catch ( Exception ex ) {
LOG.error( ex );
if ( Exceptions.isCausedBy( ex, EucalyptusCloudException.class ) ) {
throw new ClientComputeException( "IncorrectInstanceState", "The instance '" + instanceId + "' is not in the 'stopped' state." );
} else if ( Exceptions.isCausedBy( ex, NoSuchElementException.class ) && ex.toString( ).contains( "InvalidAMIID.NotFound" ) ) {
throw new ClientComputeException( "InvalidAMIID.NotFound", "The default " + request.getAttribute( ) + " does not exist" );
}
throw new ClientComputeException( "InvalidInstanceID.NotFound", "The instance ID '" + instanceId + "' does not exist" );
}
return reply;
}
public MonitorInstancesResponseType monitorInstances( final MonitorInstancesType request ) throws EucalyptusCloudException {
final MonitorInstancesResponseType reply = request.getReply();
final List<String> instanceSet = normalizeIdentifiers( request.getInstancesSet() );
final List<MonitorInstanceState> monitorTrueList = Lists.newArrayList();
for(final String inst : instanceSet) {
final MonitorInstanceState monitorInstanceState = new MonitorInstanceState();
monitorInstanceState.setInstanceId(inst);
monitorInstanceState.setMonitoringState("enabled");
monitorTrueList.add(monitorInstanceState);
}
reply.setInstancesSet(SetMonitorFunction.INSTANCE.apply( monitorTrueList ) );
return reply;
}
private enum SetMonitorFunction implements Function<List<MonitorInstanceState>, ArrayList<MonitorInstanceState>> {
INSTANCE;
@Override
public ArrayList<MonitorInstanceState> apply( final List<MonitorInstanceState> monitorList ) {
final ArrayList<MonitorInstanceState> monitorInstanceSet = Lists.newArrayList();
for ( final MonitorInstanceState monitorInst : monitorList ) {
try ( final TransactionResource db = Entities.transactionFor( VmInstance.class ) ) {
final VmInstance vmInst = VmInstances.lookup( monitorInst.getInstanceId() );
if ( RestrictedTypes.filterPrivileged().apply( vmInst ) ) {
vmInst.getBootRecord( ).setMonitoring( "enabled".equals( monitorInst.getMonitoringState( ) ) );
monitorInstanceSet.add( monitorInst );
db.commit();
}
} catch ( final NoSuchElementException nse ) {
LOG.debug( "Unable to find instance : " + monitorInst.getInstanceId() );
} catch ( final Exception ex ) {
LOG.debug( "Unable to set monitoring state for instance : " + monitorInst.getInstanceId( ), ex );
}
}
return monitorInstanceSet;
}
}
public ModifyInstanceAttributeResponseType modifyInstanceAttribute( final ModifyInstanceAttributeType request )
throws EucalyptusCloudException, NoSuchMetadataException {
final ModifyInstanceAttributeResponseType reply = request.getReply( );
final String instanceId = normalizeIdentifier( request.getInstanceId( ) );
Context ctx = Contexts.lookup( );
try ( final TransactionResource tx = Entities.transactionFor( VmInstance.class ) ) {
final VmInstance vm;
try {
vm = RestrictedTypes.doPrivileged( instanceId, VmInstance.class );
} catch ( AuthException | NoSuchElementException e ) {
throw new ClientComputeException( "InvalidInstanceID.NotFound", "The instance ID '" + instanceId + "' does not exist" );
}
if ( request.getBlockDeviceMappingSet() != null && !request.getBlockDeviceMappingSet( ).getItem( ).isEmpty( ) ) {
nextmapping:
for ( final InstanceBlockDeviceMappingItemType mapping : request.getBlockDeviceMappingSet( ).getItem( ) ) {
for ( VmVolumeAttachment vmVolumeAttachment : Iterables.concat( vm.getBootRecord().getPersistentVolumes(), vm.getTransientVolumeState().getAttachments() ) ) {
if ( vmVolumeAttachment.getDevice().equals( mapping.getDeviceName() ) ) {
// NOTE: AWS looks for a valid device name with any valid volume Id.
// Invalid volume Id results an InvalidVolumeID.Malformed.
// Current implementation for this negative use case is to throw InvalidVolumeID.Malformed exception
// when user is not allowed to access the requested volume
if ( mapping.getEbs( ) != null && mapping.getEbs( ).getVolumeId( ) != null ) {
if ( mapping.getEbs( ).getVolumeId( ).equals( vmVolumeAttachment.getVolumeId( ) ) ) try {
Volumes.lookup(
ctx.getUserFullName().asAccountFullName(),
ResourceIdentifiers.tryNormalize().apply( mapping.getEbs().getVolumeId() ) );
} catch ( Exception e ) {
throw new ClientComputeException( "InvalidInstanceAttributeValue", "Invalid volume ("+mapping.getEbs().getVolumeId()+")" );
} else {
throw new ClientComputeException( "InvalidInstanceAttributeValue", "Invalid volume ("+mapping.getEbs().getVolumeId()+")" );
}
}
vmVolumeAttachment.setDeleteOnTerminate( mapping.getEbs( ) == null ?
true :
MoreObjects.firstNonNull( mapping.getEbs( ).getDeleteOnTermination( ), true ) );
continue nextmapping;
}
}
throw new ClientComputeException( "InvalidInstanceAttributeValue", "No device is currently mapped at " + mapping.getDeviceName( ) );
}
tx.commit();
} else if ( request.getDisableApiTermination() != null && request.getDisableApiTermination( ).getValue( ) != null ) {
vm.setDisableApiTermination( request.getDisableApiTermination( ).getValue( ) );
tx.commit( );
} else if ( request.getEbsOptimized() != null ) {
// not currently supported
} else if ( request.getGroupIdSet( ) != null && !request.getGroupIdSet( ).getItem( ).isEmpty( ) ) {
final Collection<NetworkGroup> groups = Lists.newArrayList( );
final AccountFullName accountFullName = ctx.getUserFullName( ).asAccountFullName( );
for ( final SecurityGroupIdSetItemType groupIdItemType : request.getGroupIdSet( ).getItem( ) ) try {
final String groupId = ResourceIdentifiers.tryNormalize().apply( groupIdItemType.getGroupId( ) );
final NetworkGroup networkGroup = NetworkGroups.lookupByGroupId( ctx.isAdministrator( ) ? null : accountFullName, groupId );
if ( !RestrictedTypes.filterPrivileged( ).apply( networkGroup ) ) {
throw new IllegalAccessException( "Not authorized to access security group " + groupId + " for " + ctx.getUserFullName( ) );
}
if ( !MoreObjects.firstNonNull( networkGroup.getVpcId( ), "" ).equals( vm.getVpcId( ) ) ) {
throw new ClientComputeException( "InvalidGroup.NotFound", "Security group ("+groupId+") not found" );
}
groups.add( networkGroup );
} catch ( NoSuchMetadataException e ) {
throw new ClientComputeException( "InvalidGroup.NotFound", "Security group ("+groupIdItemType.getGroupId( )+") not found" );
}
if ( !Collections.singleton( vm.getVpcId( ) ).equals( Sets.newHashSet( Iterables.transform( groups, NetworkGroup.vpcId( ) ) ) ) ) {
throw Exceptions.toUndeclared( new ClientComputeException( "InvalidParameterValue", "Invalid security groups (inconsistent VPC)" ) );
}
vm.getNetworkGroups( ).clear( );
vm.getNetworkGroups( ).addAll( groups );
if ( vm.getNetworkInterfaces( ) != null ) {
for ( final NetworkInterface networkInterface : vm.getNetworkInterfaces( ) ) {
if ( networkInterface.getAttachment( ).getDeviceIndex( ) == 0 ) {
networkInterface.getNetworkGroups( ).clear( );
networkInterface.getNetworkGroups( ).addAll( groups );
break;
}
}
}
tx.commit();
NetworkGroups.flushRules( );
} else if ( request.getInstanceInitiatedShutdownBehavior( ) != null ) {
// not currently supported
} else if ( request.getSourceDestCheck( ) != null ) {
if ( vm.getNetworkInterfaces( ) != null ) {
for ( final NetworkInterface networkInterface : vm.getNetworkInterfaces( ) ) {
if ( networkInterface.getAttachment( ).getDeviceIndex( ) == 0 ) {
networkInterface.setSourceDestCheck( request.getSourceDestCheck( ).getValue( ) );
tx.commit();
NetworkGroups.flushRules( );
break;
}
}
}
} else if ( request.getSriovNetSupport( ) != null ) {
// not currently supported
} else {
if ( !VmState.STOPPED.apply( vm ) ) {
throw new ClientComputeException( "IncorrectInstanceState", "The instance (" + instanceId + ") is not in the 'stopped' state." );
}
if ( request.getInstanceType( ) != null ) {
VmType vmType = VmTypes.lookup( request.getInstanceType( ).getValue( ) ); // throws NoSuchMetadataException
if ( !RestrictedTypes.filterPrivileged( ).apply( vmType ) ) {
throw new IllegalAccessException( "Not authorized to allocate vm type " + vmType + " for " + ctx.getUserFullName( ) );
}
vm.getBootRecord( ).setVmType( vmType );
tx.commit( );
} else if ( request.getKernel() != null ) {
try {
final KernelImageInfo kernelImg = Images.lookupKernel( request.getKernel( ).getValue( ) );
if ( Images.FilterPermissions.INSTANCE.apply( kernelImg )
&& ImageMetadata.State.available.equals( kernelImg.getState( ) ) ) {
if ( !RestrictedTypes.filterPrivilegedWithoutOwner( ).apply( kernelImg ) )
throw new IllegalAccessException( "Not authorize to use image " + kernelImg.getName( ) + " for ModifyInstanceAttribute" );
vm.getBootRecord( ).setKernel( kernelImg );
tx.commit( );
} else {
throw new ClientComputeException( "InvalidAMIID.NotFound", "Image id (" + request.getRamdisk( ).getValue( ) + ") not found" );
}
} catch ( final NoSuchElementException e ) {
throw new ClientComputeException( "InvalidAMIID.NotFound", "Image id (" + request.getRamdisk( ).getValue( ) + ") not found" );
}
} else if ( request.getRamdisk() != null ) {
try {
final RamdiskImageInfo ramdiskImg = Images.lookupRamdisk( request.getRamdisk( ).getValue( ) );
if ( Images.FilterPermissions.INSTANCE.apply( ramdiskImg )
&& ImageMetadata.State.available.equals( ramdiskImg.getState( ) ) ) {
if ( !RestrictedTypes.filterPrivilegedWithoutOwner( ).apply( ramdiskImg ) )
throw new IllegalAccessException( "Not authorize to use image " + ramdiskImg.getName( ) + " for ModifyInstanceAttribute" );
vm.getBootRecord( ).setRamdisk( ramdiskImg );
tx.commit( );
} else {
throw new ClientComputeException( "InvalidAMIID.NotFound", "Image id (" + request.getRamdisk( ).getValue( ) + ") not found" );
}
} catch ( final NoSuchElementException e ) {
throw new ClientComputeException( "InvalidAMIID.NotFound", "Image id (" + request.getRamdisk( ).getValue( ) + ") not found" );
}
} else if ( request.getUserData() != null ) {
final byte[] userData;
try {
userData = Strings.nullToEmpty(request.getUserData().getValue()).isEmpty() ? new byte[0] : B64.standard.dec( request.getUserData( ).getValue( ) );
} catch ( ArrayIndexOutOfBoundsException | StringIndexOutOfBoundsException | DecoderException e ) {
throw new ClientComputeException( "InvalidParameterValue", "User data decoding error." );
}
if ( userData.length > Integer.parseInt( VmInstances.USER_DATA_MAX_SIZE_KB ) * 1024 ) {
throw new InvalidMetadataException( "User data may not exceed " + VmInstances.USER_DATA_MAX_SIZE_KB + " KB" );
}
vm.getBootRecord( ).setUserData( userData );
tx.commit( );
} else {
// InstanceInitiatedShutdownBehavior, GroupId [EC2-VPC], EbsOptimized are not supported yet.
}
}
reply.set_return( true );
} catch ( final ComputeException e ) {
throw e;
} catch ( Exception ex ) {
if ( Exceptions.isCausedBy( ex, NoSuchMetadataException.class ) ) {
throw new ClientComputeException( "InvalidInstanceAttributeValue", "The instanceType '" + request.getInstanceType() + "' is invalid." );
} else if ( Exceptions.isCausedBy( ex, IllegalAccessException.class ) ) {
throw new ClientComputeException( "UnauthorizedOperation", "You are not authorized to perform this operation." );
} else if ( Exceptions.isCausedBy( ex, InvalidMetadataException.class ) ) {
throw new ClientComputeException( "InvalidParameterValue", "User data is limited to 16384 bytes" );
}
LOG.error( ex, ex );
throw new ComputeException( "InternalError", "Error processing request: " + ex.getMessage( ) );
}
return reply;
}
public DeletePlacementGroupResponseType deletePlacementGroup( final DeletePlacementGroupType request ) {
final DeletePlacementGroupResponseType reply = request.getReply( );
return reply;
}
public CreatePlacementGroupResponseType createPlacementGroup( final CreatePlacementGroupType request ) {
final CreatePlacementGroupResponseType reply = request.getReply( );
return reply;
}
public ModifyInstancePlacementResponseType modifyInstancePlacement( final ModifyInstancePlacementType request ) {
final ModifyInstancePlacementResponseType reply = request.getReply( );
return reply;
}
public CancelBundleTaskResponseType cancelBundleTask( final CancelBundleTaskType request ) throws EucalyptusCloudException {
final CancelBundleTaskResponseType reply = request.getReply( );
reply.set_return( true );
final Context ctx = Contexts.lookup( );
try {
final VmInstance v = VmInstances.lookupByBundleId( normalizeBundleIdentifier( request.getBundleId() ) );
BundleState bundleState = v.getRuntimeState( ).getBundleTaskState( );
if ( !( bundleState == BundleState.pending || bundleState == BundleState.storing ) )
throw new EucalyptusCloudException( "Can't cancel bundle task when the bundle task is " + bundleState );
if ( RestrictedTypes.filterPrivileged( ).apply( v ) ) {
Bundles.updateBundleTaskState( v, BundleState.canceling, 0.0d );
LOG.info( EventRecord.here( BundleCallback.class, EventType.BUNDLE_CANCELING, ctx.getUserFullName( ).toString( ),
v.getRuntimeState( ).getBundleTask( ).getBundleId( ),
v.getInstanceId( ) ) );
ServiceConfiguration ccConfig = Topology.lookup( ClusterController.class, v.lookupPartition( ) );
final ClusterCancelBundleTaskType cancelRequest = new ClusterCancelBundleTaskType( );
cancelRequest.setInstanceId( v.getInstanceId( ) );
reply.setTask( Bundles.transform( v.getRuntimeState( ).getBundleTask( ) ) );
AsyncRequests.newRequest( Bundles.cancelCallback( cancelRequest ) ).dispatch( ccConfig );
return reply;
} else {
throw new EucalyptusCloudException( "Failed to find bundle task: " + request.getBundleId( ) );
}
} catch ( final NoSuchElementException e ) {
throw new EucalyptusCloudException( "Failed to find bundle task: " + request.getBundleId( ) );
}
}
public BundleInstanceResponseType bundleInstance( final BundleInstanceType request ) throws EucalyptusCloudException {
final Context ctx = Contexts.lookup( );
final BundleInstanceResponseType reply = request.getReply( );
final String instanceId = normalizeIdentifier( request.getInstanceId( ) );
if (!validBucketName(request.getBucket( ) ) ) {
throw new ClientComputeException(" InvalidParameterValue", "Value (" + request.getBucket( ) + ") for parameter Bucket is invalid." );
} else if (!validBucketName(request.getPrefix( ) ) ) {
throw new ClientComputeException(" InvalidParameterValue", "Value (" + request.getPrefix( ) + ") for parameter Prefix is invalid." );
}
if ( request.getUploadPolicy( ) != null && request.getUploadPolicy( ).length( ) > 4096 ) {
throw new ClientComputeException( " InvalidParameterValue", "Value for parameter UploadPolicy is invalid (too long)" );
}
final String uploadPolicyJson = B64.standard.decString( request.getUploadPolicy( ) );
Bundles.checkAndCreateBucket( ctx.getUser( ), request.getBucket( ), request.getPrefix( ) );
final Function<String, VmInstance> bundleFunc = new Function<String,VmInstance>( ) {
@Override
public VmInstance apply( String input ) {
reply.set_return( false );
try {
final VmInstance v = RestrictedTypes.doPrivileged( input, VmInstance.class );
if ( !VmState.RUNNING.equals( v.getState( ) ) ) {
throw new ClientComputeException( "InvalidState", "Failed to bundle requested vm because it is not currently 'running': " + instanceId );
} else {
final VmBundleTask bundleTask = Bundles.create( v, request.getBucket(), request.getPrefix(), uploadPolicyJson );
if ( Bundles.startBundleTask( bundleTask ) ) {
reply.setTask( Bundles.transform( bundleTask ) );
reply.markWinning( );
} else {
throw new ClientComputeException( "BundlingInProgress", "Instance is already being bundled: " + v.getRuntimeState().getBundleTask().getBundleId() );
}
EventRecord.here( VmControl.class,
EventType.BUNDLE_PENDING,
ctx.getUserFullName( ).toString( ),
v.getRuntimeState( ).getBundleTask( ).getBundleId( ),
v.getInstanceId( ) ).debug( );
}
return v;
} catch ( final ComputeException e ) {
throw Exceptions.toUndeclared( e );
} catch ( final AuthException | NoSuchElementException e ) {
throw Exceptions.toUndeclared( new ClientComputeException( "InvalidInstanceID.NotFound", "The instance ID '" + instanceId + "' does not exist" ) );
} catch ( final Exception ex ) {
LOG.error( ex );
Logs.extreme( ).error( ex, ex );
throw Exceptions.toUndeclared( ex );
}
}
};
AccessKey accessKey;
try {
final String accessKeyId = request.getAwsAccessKeyId();
final JSONObject policyJsonObj = JSONObject.fromObject( uploadPolicyJson );
final JSONArray conditions = policyJsonObj.getJSONArray( "conditions" );
String securityToken = null;
for ( final Object object : conditions ) {
if ( object instanceof JSONObject && ( (JSONObject) object ).has( "x-amz-security-token" ) ) {
securityToken = ( (JSONObject) object ).getString( "x-amz-security-token" );
break;
}
}
accessKey = AccessKeys.lookupAccessKey( accessKeyId, securityToken );
final byte[] sig = HmacUtils.getSignature( accessKey.getSecretKey( ), request.getUploadPolicy( ), Hmac.HmacSHA1 );
if ( !MessageDigest.isEqual( sig, B64.standard.dec( request.getUploadPolicySignature() ) ) ) {
throw new ClientComputeException( "InvalidParameterValue", "Value for UploadPolicySignature is invalid." );
}
} catch ( final JSONException e ) {
throw new ClientComputeException( "InvalidParameterValue", "Value for UploadPolicy is invalid." );
} catch ( final AuthException e ) {
throw new ClientComputeException( "InvalidParameterValue", "Value ("+request.getAwsAccessKeyId( )+") for AWSAccessKeyId is invalid." );
} catch ( final AuthenticationException e ) {
LOG.error( "Error processing upload policy signature ", e );
throw new ClientComputeException( "InternalError", "Error processing request; upload policy signature error." );
}
final VmInstance bundledVm;
try {
bundledVm = Entities.asTransaction( VmInstance.class, bundleFunc ).apply( instanceId );
} catch ( final RuntimeException e ) {
LOG.error( e, e );
Exceptions.findAndRethrow( e, ComputeException.class );
throw e;
}
final ImageInfo imageInfo = Images.lookupImage(bundledVm.getImageId());
final AccessKey accessKeyForPolicySignature = accessKey;
try {
ServiceConfiguration cluster = Topology.lookup( ClusterController.class, bundledVm.lookupPartition( ) );
ClusterBundleInstanceType reqInternal = new ClusterBundleInstanceType( );
reqInternal.setInstanceId(request.getInstanceId());
reqInternal.setBucket(request.getBucket());
reqInternal.setPrefix(request.getPrefix());
reqInternal.setAwsAccessKeyId(accessKeyForPolicySignature.getAccessKey());
reqInternal.setUploadPolicy(request.getUploadPolicy());
reqInternal.setUploadPolicySignature(request.getUploadPolicySignature());
reqInternal.setArchitecture(imageInfo != null ? imageInfo.getArchitecture().name() : "i386");
reqInternal.regardingUserRequest(request);
AsyncRequests.newRequest( Bundles.createCallback(reqInternal)).dispatch( cluster );
} catch ( Exception ex ) {
LOG.error( ex );
Logs.extreme( ).error( ex, ex );
throw Exceptions.toUndeclared( ex );
}
return reply;
}
public GetPasswordDataResponseType getPasswordData( final GetPasswordDataType request ) throws Exception {
final String instanceId = normalizeIdentifier( request.getInstanceId( ) );
final VmInstance v;
try {
v = VmInstances.lookup( instanceId );
} catch ( NoSuchElementException e ) {
throw new ClientComputeException( "InvalidInstanceID.NotFound", "The instance ID '"+instanceId+"' does not exist" );
}
if ( !RestrictedTypes.filterPrivileged( ).apply( v ) ) {
throw new EucalyptusCloudException( "Instance " + instanceId + " does not exist." );
}
if ( !VmState.RUNNING.equals( v.getState( ) ) ) {
throw new EucalyptusCloudException( "Instance " + instanceId + " is not in a running state." );
}
if ( v.getPasswordData( ) == null && !Strings.isNullOrEmpty( v.getKeyPair( ).getPublicKey( ) ) ) {
try {
final GetConsoleOutputResponseType consoleOutput = getConsoleOutput( new GetConsoleOutputType( (String) instanceId ) );
final String tempCo = B64.standard.decString( String.valueOf( consoleOutput.getOutput( ) ) ).replaceAll( "[\r\n]*", "" );
final String passwordData = substringBefore( "</Password>", substringAfter( "<Password>", tempCo ) );
if ( tempCo.matches( ".*<Password>[\\w=+/]*</Password>.*" ) ) {
Entities.asTransaction( VmInstance.class, new Predicate<String>() {
@Override
public boolean apply( final String passwordData ) {
final VmInstance vm = Entities.merge( v );
vm.updatePasswordData( passwordData );
return true;
}
} ).apply( passwordData );
v.updatePasswordData( passwordData );
}
} catch(final EucalyptusCloudException e) {
throw e;
} catch ( Exception e ) {
throw new ComputeException( "InternalError", "Error processing request: " + e.getMessage( ) );
}
}
final GetPasswordDataResponseType reply = request.getReply();
reply.set_return( true );
reply.setOutput( Strings.nullToEmpty( v.getPasswordData( ) ) );
reply.setTimestamp( new Date() );
reply.setInstanceId( v.getInstanceId() );
return reply;
}
private static void throwClientIfFound(
final Throwable throwable,
final Class<? extends Throwable> exceptionClass,
final String code
) throws ClientComputeException {
throwClientIfFound( throwable, exceptionClass, code, null );
}
private static void throwClientIfFound(
final Throwable throwable,
final Class<? extends Throwable> exceptionClass,
final String code,
final String message
) throws ClientComputeException {
final Throwable cause = Exceptions.findCause( throwable, exceptionClass );
if ( cause != null ) throw new ClientComputeException( code, message!=null ? message : cause.getMessage( ) );
}
private static Set<String> toInstanceIds( final Iterable<String> ids ) throws EucalyptusCloudException {
final Set<String> result = Sets.newHashSet();
if ( ids != null ) for ( final String id : ids ) {
result.add( normalizeBundleIdentifier( id ).replace( "bun-", "i-" ) );
}
return result;
}
private boolean validBucketName(String name) {
return java.util.regex.Pattern.matches( "^[a-zA-Z\\d\\.\\-_]{3,255}$", name );
}
private static String normalizeIdentifier( final String identifier ) throws EucalyptusCloudException {
try {
return ResourceIdentifiers.parse( VmInstance.ID_PREFIX, identifier ).getIdentifier( );
} catch ( final InvalidResourceIdentifier e ) {
throw new ClientComputeException( "InvalidInstanceID.Malformed", "Invalid id: \""+e.getIdentifier()+"\"" );
}
}
private static List<String> normalizeIdentifiers( final List<String> identifiers ) throws EucalyptusCloudException {
try {
return ResourceIdentifiers.normalize( VmInstance.ID_PREFIX, identifiers );
} catch ( final InvalidResourceIdentifier e ) {
throw new ClientComputeException( "InvalidInstanceID.Malformed", "Invalid id: \""+e.getIdentifier()+"\"" );
}
}
private static String normalizeBundleIdentifier( final String identifier ) throws EucalyptusCloudException {
try {
return ResourceIdentifiers.parse( "bun", identifier ).getIdentifier();
} catch ( final InvalidResourceIdentifier e ) {
throw new ClientComputeException( "InvalidInstanceID.Malformed", "Invalid id: \""+e.getIdentifier()+"\"" );
}
}
}