/************************************************************************* * 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. * * 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.images; import static com.eucalyptus.images.Images.DeviceMappingValidationOption.AllowDevSda1; import static com.eucalyptus.images.Images.DeviceMappingValidationOption.AllowEbsMapping; import static com.eucalyptus.images.Images.DeviceMappingValidationOption.AllowSuppressMapping; import static com.eucalyptus.util.Parameters.checkParam; import static org.hamcrest.Matchers.notNullValue; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.NoSuchElementException; import javax.annotation.Nullable; import javax.persistence.EntityTransaction; import javax.persistence.PersistenceException; import com.eucalyptus.component.annotation.ComponentNamed; 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.compute.common.internal.util.MetadataException; import com.eucalyptus.compute.ClientComputeException; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.hibernate.exception.ConstraintViolationException; import com.eucalyptus.auth.Accounts; import com.eucalyptus.auth.AuthException; import com.eucalyptus.compute.common.BlockDeviceMappingItemType; import com.eucalyptus.compute.common.CloudMetadatas; import com.eucalyptus.compute.common.ImageMetadata; import com.eucalyptus.component.Topology; import com.eucalyptus.cluster.common.ClusterController; import com.eucalyptus.compute.ComputeException; import com.eucalyptus.compute.common.backend.CopyImageResponseType; import com.eucalyptus.compute.common.backend.CopyImageType; import com.eucalyptus.compute.common.internal.identifier.InvalidResourceIdentifier; import com.eucalyptus.compute.common.internal.identifier.ResourceIdentifiers; import com.eucalyptus.context.Context; import com.eucalyptus.context.Contexts; import com.eucalyptus.context.IllegalContextAccessException; import com.eucalyptus.entities.Entities; import com.eucalyptus.entities.TransactionException; import com.eucalyptus.images.ImageManifests.ImageManifest; import com.eucalyptus.records.Logs; import com.eucalyptus.util.EucalyptusCloudException; import com.eucalyptus.util.Exceptions; import com.eucalyptus.util.RestrictedTypes; import com.eucalyptus.vm.CreateImageTask; import com.eucalyptus.compute.common.internal.vm.VmInstance; import com.eucalyptus.compute.common.internal.vm.VmInstance.VmState; import com.eucalyptus.vm.VmInstances; import com.google.common.base.Predicate; import com.google.common.base.Strings; import com.google.common.base.Supplier; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.eucalyptus.compute.common.backend.ConfirmProductInstanceResponseType; import com.eucalyptus.compute.common.backend.ConfirmProductInstanceType; import com.eucalyptus.compute.common.backend.CreateImageResponseType; import com.eucalyptus.compute.common.backend.CreateImageType; import com.eucalyptus.compute.common.backend.DeregisterImageResponseType; import com.eucalyptus.compute.common.backend.DeregisterImageType; import com.eucalyptus.compute.common.backend.ModifyImageAttributeResponseType; import com.eucalyptus.compute.common.backend.ModifyImageAttributeType; import com.eucalyptus.compute.common.backend.RegisterImageResponseType; import com.eucalyptus.compute.common.backend.RegisterImageType; import com.eucalyptus.compute.common.backend.ResetImageAttributeResponseType; import com.eucalyptus.compute.common.backend.ResetImageAttributeType; @ComponentNamed("computeImageManager") public class ImageManager { public static Logger LOG = Logger.getLogger( ImageManager.class ); private static final long GB = 1024*1024*1024; //bytes-per-gb public CopyImageResponseType copyImage( final CopyImageType request ) { return request.getReply( ); } public RegisterImageResponseType register( final RegisterImageType request ) throws EucalyptusCloudException, AuthException, IllegalContextAccessException, NoSuchElementException, PersistenceException { final Context ctx = Contexts.lookup(); ImageInfo imageInfo = null; final String rootDevName = ( request.getRootDeviceName( ) != null ) ? request.getRootDeviceName( ) : Images.DEFAULT_ROOT_DEVICE; final String eki = ImageMetadata.Platform.windows.name( ).equals( request.getKernelId( ) ) ? request.getKernelId() : normalizeOptionalImageIdentifier( request.getKernelId() ); final String eri = normalizeOptionalImageIdentifier( request.getRamdiskId() ); verifyImageNameAndDescription( request.getName( ), request.getDescription( ) ); ImageMetadata.VirtualizationType virtType = ImageMetadata.VirtualizationType.paravirtualized; if(request.getVirtualizationType() != null){ if(StringUtils.equalsIgnoreCase("paravirtual", request.getVirtualizationType())) virtType = ImageMetadata.VirtualizationType.paravirtualized; else if(StringUtils.equalsIgnoreCase("hvm", request.getVirtualizationType())) virtType = ImageMetadata.VirtualizationType.hvm; else throw new EucalyptusCloudException("Unknown virtualization-type"); } if ( request.getImageLocation( ) != null ) { // Verify all the device mappings first. bdmInstanceStoreImageVerifier( ).apply( request ); // download manifest with AwsExecRead account final ImageManifest manifest = ImageManifests.lookup( request.getImageLocation( ) , ctx.getUser()); LOG.debug( "Obtained manifest information for requested image registration: " + manifest ); final ImageMetadata.Platform imagePlatform = request.getPlatform()!=null ? ImageMetadata.Platform.valueOf(request.getPlatform()) : manifest.getPlatform( ); if(ImageMetadata.Platform.windows.equals(imagePlatform)) virtType = ImageMetadata.VirtualizationType.hvm; final ImageMetadata.VirtualizationType virtualizationType = virtType; if(ImageMetadata.Type.machine.equals(manifest.getImageType( )) && ImageMetadata.VirtualizationType.paravirtualized.equals(virtualizationType)){ // make sure kernel and ramdisk are present with the request or manifest if(request.getKernelId()==null && manifest.getKernelId()==null) throw new ClientComputeException("MissingParameter","Kernel ID must be specified"); if(request.getRamdiskId()==null && manifest.getRamdiskId()==null) throw new ClientComputeException("MissingParameter","Ramdisk ID must be specified"); } //Check that the manifest-specified size of the image is within bounds. //If null image max size then always allow Integer maxSize = ImageConfiguration.getInstance().getMaxImageSizeGb(); if(maxSize != null && maxSize > 0 && manifest.getSize() > (maxSize * GB)) { throw new EucalyptusCloudException("Cannot register image of size " + manifest.getSize() + " bytes because it exceeds the configured maximum instance-store image size of " + maxSize + " GB. Please contact your administrator "); } List<DeviceMapping> vbr = Lists.transform( request.getBlockDeviceMappings( ), Images.deviceMappingGenerator( imageInfo, null ) ); final ImageMetadata.Architecture arch = ( request.getArchitecture( ) == null ? null : ImageMetadata.Architecture.valueOf( request.getArchitecture( ) ) ); final String amiFromManifest = manifest.getAmi(); Supplier<ImageInfo> allocator = new Supplier<ImageInfo>( ) { @Override public ImageInfo get( ) { try { if(ImageMetadata.Type.machine.equals(manifest.getImageType( )) && ImageMetadata.VirtualizationType.paravirtualized.equals(virtualizationType) && (amiFromManifest.isEmpty() || isPathAPartition(amiFromManifest)) ) return Images.registerFromManifest( ctx.getUserFullName( ), request.getName( ), request.getDescription( ), arch, virtualizationType, ImageMetadata.Platform.linux, ImageMetadata.ImageFormat.partitioned, eki, eri, manifest ); else return Images.registerFromManifest( ctx.getUserFullName( ), request.getName( ), request.getDescription( ), arch, virtualizationType, imagePlatform, ImageMetadata.ImageFormat.fulldisk, eki, eri, manifest ); } catch ( Exception ex ) { LOG.error( ex ); Logs.extreme( ).error( ex, ex ); throw Exceptions.toUndeclared( ex ); } } }; imageInfo = RestrictedTypes.allocateUnitlessResource( allocator ); imageInfo.getDeviceMappings( ).addAll( vbr ); } else if ( rootDevName != null && Iterables.any( request.getBlockDeviceMappings( ), Images.findEbsRoot( rootDevName ) ) ) { Supplier<ImageInfo> allocator = null; // Verify all the device mappings first. Dont fuss if both snapshot id and volume size are left blank bdmBfebsImageVerifier( ).apply( request ); ImageMetadata.Platform platform = ImageMetadata.Platform.linux; if (request.getPlatform()!=null) platform = ImageMetadata.Platform.valueOf(request.getPlatform()); else if (ImageMetadata.Platform.windows.name( ).equals( eki )) platform = ImageMetadata.Platform.windows; final ImageMetadata.Platform imagePlatform = platform; final ImageMetadata.Architecture arch = ( request.getArchitecture( ) == null ? ImageMetadata.Architecture.i386 : ImageMetadata.Architecture.valueOf( request.getArchitecture( ) ) ); allocator = new Supplier<ImageInfo>( ) { @Override public ImageInfo get( ) { try { return Images.createFromDeviceMapping( ctx.getUserFullName( ), request.getName( ), request.getDescription( ), imagePlatform, eki, eri, rootDevName, request.getBlockDeviceMappings( ), arch ); } catch ( EucalyptusCloudException ex ) { throw new RuntimeException( ex ); } } }; imageInfo = RestrictedTypes.allocateUnitlessResource( allocator ); } else { throw new EucalyptusCloudException( "Invalid request: the request must specify either ImageLocation for an " + "instance-store image or a snapshot for the root device for an EBS image. " + "Provided values were: ImageLocation=" + request.getImageLocation( ) + " BlockDeviceMappings=" + request.getBlockDeviceMappings( ) ); } RegisterImageResponseType reply = ( RegisterImageResponseType ) request.getReply( ); reply.setImageId( imageInfo.getDisplayName( ) ); return reply; } public static boolean isPathAPartition(String str) { char lastChar = str.charAt(str.length() - 1); // get last letter/number return !Character.isLetter(lastChar); } public DeregisterImageResponseType deregister( DeregisterImageType request ) throws EucalyptusCloudException { DeregisterImageResponseType reply = request.getReply( ); EntityTransaction tx = Entities.get( ImageInfo.class ); try { ImageInfo imgInfo = Entities.uniqueResult( Images.exampleWithImageId( imageIdentifier( request.getImageId( ) ) ) ); if ( !canModifyImage( imgInfo ) ) { throw new EucalyptusCloudException( "Not authorized to deregister image" ); } Images.deregisterImage( imgInfo.getDisplayName( ) ); tx.commit( ); return reply; } catch ( NoSuchImageException | NoSuchElementException ex ) { throw new ClientComputeException( "InvalidAMIID.NotFound", "The image ID '" + request.getImageId() + "' does not exist"); } catch ( InstanceNotTerminatedException | ConstraintViolationException re ) { throw new ClientComputeException( "InvalidAMIID.Unavailable", "The image ID '" + request.getImageId() + "' is no longer available" ); } catch ( TransactionException ex ) { if ( ex.getCause() instanceof NoSuchElementException ) throw new ClientComputeException( "InvalidAMIID.NotFound", "The image ID '" + request.getImageId() + "' does not exist"); else throw new EucalyptusCloudException( ex ); } finally { if ( tx.isActive() ) tx.rollback(); } } public ConfirmProductInstanceResponseType confirmProductInstance( ConfirmProductInstanceType request ) throws EucalyptusCloudException { ConfirmProductInstanceResponseType reply = ( ConfirmProductInstanceResponseType ) request.getReply( ); reply.set_return( false ); VmInstance vm = null; try { vm = VmInstances.lookup( request.getInstanceId( ) ); //ASAP: FIXME: GRZE: RESTORE! // EntityWrapper<ImageInfo> db = EntityWrapper.get( ImageInfo.class ); // try { // ImageInfo found = db.getUnique( new ImageInfo( vm.getImageInfo( ).getImageId( ) ) ); // if ( found.getProductCodes( ).contains( new ProductCode( request.getProductCode( ) ) ) ) { // reply.set_return( true ); // reply.setOwnerId( found.getImageOwnerId( ) ); // } // db.commit( ); // } catch ( EucalyptusCloudException e ) { // db.commit( ); // } } catch ( NoSuchElementException e ) {} return reply; } private static List<String> verifyUserIds(final List<String> userIds) throws EucalyptusCloudException { for ( String userId : userIds ) { if ( !Accounts.isAccountNumber( userId ) ) { throw new EucalyptusCloudException( "Not a valid userId : " + userId ); } } return Lists.newArrayList( userIds ); } public ModifyImageAttributeResponseType modifyImageAttribute( final ModifyImageAttributeType request ) throws EucalyptusCloudException { final ModifyImageAttributeResponseType reply = ( ModifyImageAttributeResponseType ) request.getReply( ); final EntityTransaction tx = Entities.get( ImageInfo.class ); try { final ImageInfo imgInfo = Entities.uniqueResult( Images.exampleWithImageId( imageIdentifier( request.getImageId( ) ) ) ); if ( !canModifyImage( imgInfo ) ) { throw new EucalyptusCloudException( "Not authorized to modify image attribute" ); } switch ( request.imageAttribute() ) { case LaunchPermission: if ( request.add() ) { imgInfo.addPermissions( verifyUserIds( request.userIds() ) ); if ( request.groupAll() ) { imgInfo.setImagePublic( true ); } } else { imgInfo.removePermissions( request.userIds() ); if ( request.groupAll() ) { imgInfo.setImagePublic( false ); } } break; case ProductCode: for ( String productCode : request.getProductCodes( ) ) { imgInfo.addProductCode( productCode ); } break; case Description: imgInfo.setDescription( request.getDescription() ); break; } tx.commit( ); reply.set_return( true ); } catch ( EucalyptusCloudException e ) { tx.rollback( ); reply.set_return( false ); throw e; } catch ( TransactionException | NoSuchElementException ex ) { tx.rollback( ); throw new EucalyptusCloudException( ex ); } return reply; } public ResetImageAttributeResponseType resetImageAttribute( ResetImageAttributeType request ) throws EucalyptusCloudException { ResetImageAttributeResponseType reply = ( ResetImageAttributeResponseType ) request.getReply( ); reply.set_return( true ); EntityTransaction tx = Entities.get( ImageInfo.class ); try { ImageInfo imgInfo = Entities.uniqueResult( Images.exampleWithImageId( imageIdentifier( request.getImageId( ) ) ) ); if ( canModifyImage( imgInfo ) ) { imgInfo.resetPermission( ); tx.commit( ); return reply.markWinning( ); } else { tx.rollback( ); return reply.markFailed( ); } } catch ( EucalyptusCloudException e ) { LOG.error( e, e ); tx.rollback( ); return reply.markFailed( ); } catch ( TransactionException | NoSuchElementException ex ) { tx.rollback( ); return reply.markFailed( ); } } public CreateImageResponseType createImage( CreateImageType request ) throws EucalyptusCloudException { final CreateImageResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); verifyImageNameAndDescription( request.getName( ), request.getDescription( ) ); final String instanceId = normalizeInstanceIdentifier( request.getInstanceId( ) ); VmInstance vm; // IAM auth check, validate instance states, etc try { vm = RestrictedTypes.doPrivileged( instanceId, VmInstance.class ); if (!vm.isBlockStorage()) throw new EucalyptusCloudException("Cannot create an image from an instance which is not booted from an EBS volume"); if ( !VmState.RUNNING.equals( vm.getState( ) ) && !VmState.STOPPED.equals( vm.getState( ) ) ) { throw new EucalyptusCloudException( "Cannot create an image from an instance which is not in either the 'running' or 'stopped' state: " + vm.getInstanceId( ) + " is in state " + vm.getState( ).getName( ) ); } try { Topology.lookup( ClusterController.class, vm.lookupPartition( ) ); } catch ( NoSuchElementException e ) { LOG.debug( e ); throw new EucalyptusCloudException( "Cluster does not exist: " + vm.getPartition( ) ); } } catch ( AuthException ex ) { throw new EucalyptusCloudException( "Not authorized to create an image from instance " + request.getInstanceId( ) + " as " + ctx.getUser( ).getName( ) ); } catch ( NoSuchElementException ex ) { throw new EucalyptusCloudException( "Instance does not exist: " + request.getInstanceId( ), ex ); } catch ( PersistenceException ex ) { throw new EucalyptusCloudException( "Instance does not exist: " + request.getInstanceId( ), ex ); } final String name = request.getName(); final boolean noReboot = true ? request.getNoReboot()!=null && request.getNoReboot().booleanValue() : false; final String desc = request.getDescription(); String rootDeviceName = null; List<BlockDeviceMappingItemType> blockDevices = request.getBlockDeviceMappings(); ImageMetadata.Architecture arch = null; ImageMetadata.Platform platform = null; try{ final BlockStorageImageInfo image = Emis.LookupBlockStorage.INSTANCE.apply(vm.getImageId()); arch= image.getArchitecture(); platform = image.getPlatform(); rootDeviceName = image.getRootDeviceName(); }catch(final Exception ex){ throw new EucalyptusCloudException("Unable to get the image information"); } final ImageMetadata.Architecture imageArch = arch; final ImageMetadata.Platform imagePlatform = platform; if( blockDevices==null ) blockDevices = new ArrayList<>(); // validate input List<String> suppressedDevice = new ArrayList<>(); List<BlockDeviceMappingItemType> creteImageDevices = new ArrayList<>(); List<String> existingNames = VmInstances.lookupPersistentDeviceNames(instanceId); for(final BlockDeviceMappingItemType device : blockDevices){ if(rootDeviceName!=null && rootDeviceName.equals(device.getDeviceName())) throw new ClientComputeException("InvalidBlockDeviceMapping", "The device names should not contain root device"); if(device.getNoDevice() != null) suppressedDevice.add(device.getDeviceName()); if(device.getEbs() != null) { if ( existingNames.contains(device.getDeviceName()) ) throw new ClientComputeException("InvalidBlockDeviceMapping", "Can't add new block device mapping with a device name that is already in use"); else { creteImageDevices.add(device); // add name to the list of "existing names" so later ephemeral devices can be checked existingNames.add(device.getDeviceName()); } } if(device.getVirtualName() != null) { existingNames.add(device.getDeviceName()); creteImageDevices.add(device); } } if(! bdmCreateImageVerifier().apply(request)){ throw new ClientComputeException("InvalidBlockDeviceMapping", "A block device mapping parameter is not valid"); } // add ephemeral devices unless they need to be suppressed try{ for(BlockDeviceMappingItemType device: Lists.transform(VmInstances.lookupEphemeralDevices(instanceId), VmInstances.EphemeralAttachmentToDevice)){ String dName = device.getDeviceName(); if ( dName != null && existingNames.contains(dName) ) throw new ClientComputeException("InvalidBlockDeviceMapping", "Can't add new block device mapping with a device name that is already in use by an ephemeral device"); if ( dName != null && !suppressedDevice.contains(dName) ){ creteImageDevices.add(device); } else { blockDevices.add(device); } } } catch (ClientComputeException e) { throw e; } catch(final Exception ex){ LOG.warn("Failed to retrieve ephemeral device information", ex); } try { Images.validateBlockDeviceMappings( creteImageDevices, EnumSet.of( AllowEbsMapping ) ); } catch (MetadataException e) { throw new ClientComputeException("InvalidBlockDeviceMapping", e.getMessage()); } final List<BlockDeviceMappingItemType> blockDeviceMapping = creteImageDevices; Supplier<ImageInfo> allocator = new Supplier<ImageInfo>( ) { @Override public ImageInfo get( ) { try{ return Images.createPendingFromDeviceMapping(ctx.getUserFullName(), name, desc, imageArch, imagePlatform, blockDeviceMapping); }catch ( final Exception ex) { throw new RuntimeException( ex ); } } }; Predicate<ImageInfo> deallocator = new Predicate<ImageInfo>() { @Override public boolean apply(@Nullable ImageInfo input) { try{ Images.setImageState(input.getDisplayName(), ImageMetadata.State.failed); Images.deregisterImage(input.getDisplayName()); }catch(final Exception ex){ LOG.error("failed to delete image from unsucccessful create-image request", ex); return false; } return true; } }; ImageInfo imageInfo = null; try{ imageInfo = RestrictedTypes.allocateUnitlessResource( allocator ); reply.setImageId(imageInfo.getDisplayName()); }catch (final AuthException ex){ throw new ClientComputeException( "AuthFailure", "Not authorized to create an image" ); }catch(final Exception ex){ LOG.error("Unable to register the image", ex); throw new EucalyptusCloudException( "Unable to register the image", ex); } final CreateImageTask task = new CreateImageTask(ctx.getAccountNumber(), instanceId, imageInfo.getDisplayName(), noReboot, blockDevices); try{ task.create(imageInfo.getDisplayName()); }catch(final Exception ex){ deallocator.apply(imageInfo); LOG.error("CreateImage task failed", ex); if(ex instanceof EucalyptusCloudException) throw (EucalyptusCloudException) ex; else throw new EucalyptusCloudException("Create-image has failed", ex); } return reply; } private static String imageIdentifier( final String identifier ) throws EucalyptusCloudException { if( !CloudMetadatas.isImageIdentifier( identifier ) ) throw new EucalyptusCloudException( "Invalid id: " + "\"" + identifier + "\"" ); return normalizeImageIdentifier( identifier ); } /** * <p>Predicate to validate the block device mappings in instance store image registration request. * Suppressing a device mapping is allowed and ebs mappings are considered invalid.</p> */ private static Predicate<RegisterImageType> bdmInstanceStoreImageVerifier () { return new Predicate<RegisterImageType>( ) { @Override public boolean apply(RegisterImageType arg0) { checkParam( arg0, notNullValue( ) ); try { Images.validateBlockDeviceMappings( arg0.getBlockDeviceMappings(), EnumSet.of( AllowSuppressMapping ) ); return true; } catch ( MetadataException e ) { throw Exceptions.toUndeclared( e ); } } }; } /** * <p>Predicate to validate the block device mappings in boot from ebs image registration request. * Suppressing a device mapping is not allowed and ebs mappings are considered valid</p> */ private static Predicate<RegisterImageType> bdmBfebsImageVerifier () { return new Predicate<RegisterImageType>( ) { @Override public boolean apply(RegisterImageType arg0) { checkParam( arg0, notNullValue( ) ); try { Images.validateBlockDeviceMappings( arg0.getBlockDeviceMappings(), EnumSet.of( AllowEbsMapping, AllowDevSda1 ) ); return true; } catch ( MetadataException e ) { throw Exceptions.toUndeclared( e ); } } }; } /* * <p>Predicate to validate the block device mappings in create image request.</p> */ private static Predicate<CreateImageType> bdmCreateImageVerifier ( ) { return new Predicate<CreateImageType> ( ) { @Override public boolean apply(CreateImageType arg0) { checkParam( arg0, notNullValue( ) ); try { Images.validateBlockDeviceMappings( arg0.getBlockDeviceMappings(), EnumSet.of( AllowEbsMapping, AllowSuppressMapping ) ); return true; } catch ( MetadataException e ) { throw Exceptions.toUndeclared( e ); } } }; } private static void verifyImageNameAndDescription( final String name, final String description ) throws ComputeException { final Context ctx = Contexts.lookup( ); if ( name != null ) { if( !Images.isImageNameValid( name ) ){ throw new ClientComputeException("InvalidAMIName.Malformed", "AMI names must be between 3 and 128 characters long, and may contain letters, numbers, '(', ')', '.', '-', '/' and '_'"); } final EntityTransaction db = Entities.get( ImageInfo.class ); try { final List<ImageInfo> images = Lists.newArrayList( Iterables.filter( Entities.query( Images.exampleWithName( ctx.getUserFullName( ).asAccountFullName( ), name ), Entities.queryOptions( ).withReadonly( true ).build( ) ), new Predicate<ImageInfo>(){ @Override public boolean apply(ImageInfo arg0) { return ImageMetadata.State.available.name().equals(arg0.getState().getExternalStateName()) || ImageMetadata.State.pending.name().equals(arg0.getState().getExternalStateName()); } } ) ); if( images.size( ) > 0 ) throw new ClientComputeException( "InvalidAMIName.Duplicate", String.format("AMI name %s is already in use by EMI %s", name, images.get(0).getDisplayName( ) ) ); } catch ( final ComputeException e ) { throw e; } catch( final Exception ex ) { LOG.error( "Error checking for duplicate image name", ex ); throw new ComputeException( "InternalError", "Error processing request." ); } finally { db.rollback( ); } }else { throw new ClientComputeException("InvalidAMIName.Malformed", "AMI names must be between 3 and 128 characters long, and may contain letters, numbers, '(', ')', '.', '-', '/' and '_'"); } if( description!=null && !Images.isImageDescriptionValid( description ) ){ throw new ClientComputeException("InvalidParameter", "AMI descriptions must be less than 256 characters long"); } } private static boolean canModifyImage( final ImageInfo imgInfo ) { final Context ctx = Contexts.lookup( ); final String requestAccountId = ctx.getUserFullName( ).getAccountNumber( ); return ( ctx.isAdministrator( ) || imgInfo.getOwnerAccountNumber( ).equals( requestAccountId ) ) && RestrictedTypes.filterPrivileged( ).apply( imgInfo ); } private static String normalizeIdentifier( final String identifier, final String prefix, final boolean required, final String message ) throws ClientComputeException { try { return Strings.emptyToNull( identifier ) == null && !required ? null : ResourceIdentifiers.parse( prefix, identifier ).getIdentifier( ); } catch ( final InvalidResourceIdentifier e ) { throw new ClientComputeException( "InvalidParameterValue", String.format( message, e.getIdentifier( ) ) ); } } private static String normalizeInstanceIdentifier( final String identifier ) throws EucalyptusCloudException { return normalizeIdentifier( identifier, VmInstance.ID_PREFIX, true, "Value (%s) for parameter instanceId is invalid. Expected: 'i-...'." ); } private static String normalizeImageIdentifier( final String identifier ) throws EucalyptusCloudException { return normalizeIdentifier( identifier, null, true, "Value (%s) for parameter image is invalid." ); } @Nullable private static String normalizeOptionalImageIdentifier( final String identifier ) throws EucalyptusCloudException { return normalizeIdentifier( identifier, null, false, "Value (%s) for parameter image is invalid." ); } }