/***************************************************************************** * Copyright (c) 2008 g-Eclipse Consortium * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Initial development of the original code was made for the * g-Eclipse project founded by European Union * project number: FP6-IST-034327 http://www.geclipse.eu/ * * Contributors: * Moritz Post - initial API and implementation *****************************************************************************/ package eu.geclipse.aws.ec2; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; import org.eclipse.core.net.proxy.IProxyChangeEvent; import org.eclipse.core.net.proxy.IProxyChangeListener; import org.eclipse.core.net.proxy.IProxyData; import org.eclipse.core.net.proxy.IProxyService; import org.eclipse.core.runtime.NullProgressMonitor; import org.osgi.framework.Bundle; import org.osgi.util.tracker.ServiceTracker; import com.xerox.amazonws.ec2.AddressInfo; import com.xerox.amazonws.ec2.AvailabilityZone; import com.xerox.amazonws.ec2.DescribeImageAttributeResult; import com.xerox.amazonws.ec2.EC2Exception; import com.xerox.amazonws.ec2.GroupDescription; import com.xerox.amazonws.ec2.ImageDescription; import com.xerox.amazonws.ec2.ImageListAttribute; import com.xerox.amazonws.ec2.Jec2; import com.xerox.amazonws.ec2.KeyPairInfo; import com.xerox.amazonws.ec2.LaunchConfiguration; import com.xerox.amazonws.ec2.ReservationDescription; import com.xerox.amazonws.ec2.TerminatingInstanceDescription; import com.xerox.amazonws.ec2.ImageAttribute.ImageAttributeType; import com.xerox.amazonws.ec2.Jec2.ImageListAttributeOperationType; import eu.geclipse.aws.auth.AWSAuthTokenDescription; import eu.geclipse.aws.ec2.internal.Activator; import eu.geclipse.aws.ec2.internal.Messages; import eu.geclipse.aws.ec2.op.AMILaunchConfiguration; import eu.geclipse.aws.ec2.service.EC2Service; import eu.geclipse.aws.vo.AWSVirtualOrganization; import eu.geclipse.core.auth.AbstractAuthTokenProvider; import eu.geclipse.core.auth.AuthTokenRequest; import eu.geclipse.core.auth.IAuthenticationToken; import eu.geclipse.core.model.IVirtualOrganization; import eu.geclipse.core.reporting.ProblemException; /** * This class wraps the amazon ec2 webservice functionality provided via the * plugin org.xerox.amazonws and the typica library. * <p> * typica: <a * href="http://code.google.com/p/typica/">http://code.google.com/p/typica/</a> * * @author Moritz Post */ public class EC2 implements IEC2, IProxyChangeListener { /** The HTTPS protocol: "https" */ private static final String HTTPS_PROTOCOL = "https"; //$NON-NLS-1$ /** The instance of the AWS EC2 library. */ private Jec2 jec2; /** The URL to connect to the EC2 webservice */ private String ec2Url; /** The Service tracker used to track the {@link IProxyService}. */ private ServiceTracker tracker; /** The aws access id to bind this EC2 instance to. */ private String awsAccessId; /** The initial VO to be used in the auth token wizard. */ private AWSVirtualOrganization awsVo; /** * Create a new Instance of the EC2 class, bound to the passed aws access id. * * @param awsAccessId the aws access id to bind to */ EC2( final String awsAccessId ) { this.awsAccessId = awsAccessId; Bundle bundle = Activator.getDefault().getBundle(); this.tracker = new ServiceTracker( bundle.getBundleContext(), IProxyService.class.getName(), null ); this.tracker.open(); getProxyService().addProxyChangeListener( this ); updateProxySettings(); } /** * Creates a new EC2 instance with the {@link AWSVirtualOrganization} as the * initial {@link IVirtualOrganization}. The constructor extracts the aws * access id from the aws vo and invokes the {@link #EC2(String)} constructor. * <p> * Additionally the aws vo is stored to be used as the initial VO when trying * to create a new {@link AWSAuthTokenDescription}. Thereby there is no need * to explicitly select a VO in the auth token wizard. * * @param awsVo the vo to extract the aws access id from and to use as the * initial vo when creating an {@link AWSAuthTokenDescription}. * @throws ProblemException a problem which might arise when accessing the AWS * VO. */ public EC2( final AWSVirtualOrganization awsVo ) throws ProblemException { this( awsVo.getProperties().getAwsAccessId() ); this.awsVo = awsVo; this.awsAccessId = awsVo.getProperties().getAwsAccessId(); } public boolean initEc2( final String ec2Url, final String awsAccessId, final String awsSecretId ) throws EC2ServiceException { if( ec2Url == null || awsAccessId == null || awsSecretId == null ) { this.ec2Url = null; this.jec2 = null; throw new EC2ServiceException( "Missing required initialization parameter" ); //$NON-NLS-1$ } this.ec2Url = ec2Url; URL url; try { url = new URL( ec2Url ); } catch( MalformedURLException e ) { throw new EC2ServiceException( "Could not validate ec2 url", e ); //$NON-NLS-1$ } String host = url.getHost(); boolean secure = false; if( url.getProtocol().equals( EC2.HTTPS_PROTOCOL ) ) { secure = true; } int port = url.getPort(); if( port == -1 ) { port = secure ? 443 : 80; } this.jec2 = new Jec2( awsAccessId, awsSecretId, secure, host, port ); updateProxySettings(); return true; } /** * Return the {@link IProxyService} or <code>null</code> if the service is not * available. * * @return the {@link IProxyService} or <code>null</code> */ private IProxyService getProxyService() { return ( IProxyService )this.tracker.getService(); } public void proxyInfoChanged( final IProxyChangeEvent event ) { updateProxySettings(); } /** * Update the proxy settings of the {@link Jec2} class. */ private void updateProxySettings() { if( this.jec2 == null ) { return; } IProxyService proxyService = ( IProxyService )this.tracker.getService(); boolean enabled = proxyService.isProxiesEnabled(); if( enabled ) { IProxyData proxyData = proxyService.getProxyData( IProxyData.HTTP_PROXY_TYPE ); String host = proxyData.getHost(); int port = proxyData.getPort(); String userName = proxyData.getUserId(); String userPassword = proxyData.getPassword(); this.jec2.setProxyValues( host, port, userName, userPassword ); } else { this.jec2.setProxyValues( null, 0, null, null, null ); } } public boolean isInitialized() { if( this.jec2 != null ) { return true; } return false; } /** * This method tries to obtain an {@link AWSAuthTokenDescription} to extract * the AWS credentials. If no such token exists, the user is asked whether he * wants to create one. In the case that no token could be found or created * the method throws an {@link EC2ServiceException}. * <p> * Additionally, in case of a valid token this {@link IEC2} instance is * initiated with the obtained AWS credentials by invoking * {@link #initEc2(String, String, String)}. * * @return if the {@link EC2} could be initialized successfully * @throws EC2ServiceException the exception is thrown when the AWS * credentials could not be obtained */ public boolean ensureAuthentication() throws EC2ServiceException { // check for an existing token or create a new one AWSAuthTokenDescription authTokenDescription = new AWSAuthTokenDescription( this.awsAccessId ); authTokenDescription.setAwsVo( this.awsVo ); AuthTokenRequest request = new AuthTokenRequest( authTokenDescription, Messages.getString( "EC2.dialog_auth_confirmation_title" ), //$NON-NLS-1$ Messages.getString( "EC2.dialog_auth_confirmation_description" ) ); //$NON-NLS-1$ IAuthenticationToken authToken; try { authToken = AbstractAuthTokenProvider.staticRequestToken( request ); } catch( ProblemException pExc ) { throw new EC2ServiceException( "No valid authentication token could be created (might be user canceled).", pExc ); //$NON-NLS-1$ } // no valid connection credentials existing if( authToken == null ) { throw new EC2ServiceException( "No valid authentication token could be created (might be user canceled)." ); //$NON-NLS-1$ } // fetch connection information AWSAuthTokenDescription awsDesc = ( AWSAuthTokenDescription )authToken.getDescription(); String ec2Url = null; List<EC2Service> ec2ServiceList; try { ec2ServiceList = awsDesc.getAwsVo() .getChildren( new NullProgressMonitor(), EC2Service.class ); EC2Service ec2Service = null; if( ec2ServiceList != null ) { ec2Service = ec2ServiceList.get( 0 ); } if( ec2Service != null ) { ec2Url = ec2Service.getProperties().getEc2Url(); } } catch( ProblemException problemEx ) { throw new EC2ServiceException( "Could not get ec2url from awsVo via ec2Service", //$NON-NLS-1$ problemEx ); } return initEc2( ec2Url, awsDesc.getAwsAccessId(), awsDesc.getAwsSecretId() ); } /** * Checks to see if the provided list is <code>null</code>. If it is * <code>null</code> a new {@link ArrayList} with the provided type is created * and returned. If the list is not <code>null</code> the current list is * returned. * * @param <T> the type of the list elements * @param list the list to check for the <code>null</code> reference * @return the inputed list or a new empty {@link ArrayList} */ private <T> List<T> nullCheck( final List<T> list ) { return list == null ? new ArrayList<T>() : list; } public List<ImageDescription> describeImagesByOwner( List<String> ownerList ) throws EC2ServiceException { ensureAuthentication(); ownerList = nullCheck( ownerList ); try { return this.jec2.describeImagesByOwner( ownerList ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "describeImagesByOwner", ec2Ex ); //$NON-NLS-1$ } } public List<ImageDescription> describeImagesByExec( List<String> execList ) throws EC2ServiceException { ensureAuthentication(); execList = nullCheck( execList ); try { return this.jec2.describeImagesByExecutability( execList ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "describeImagesByExecutability", ec2Ex ); //$NON-NLS-1$ } } public ReservationDescription runInstances( final AMILaunchConfiguration launchConfig ) throws EC2ServiceException { ensureAuthentication(); LaunchConfiguration lc = new LaunchConfiguration( launchConfig.getAmiId() ); lc.setMinCount( launchConfig.getMinCount() ); lc.setMaxCount( launchConfig.getMaxCount() ); lc.setSecurityGroup( launchConfig.getSecurityGroup() ); lc.setUserData( launchConfig.getUserData() ); lc.setKeyName( launchConfig.getKeyName() ); lc.setInstanceType( launchConfig.getInstanceType() ); lc.setAvailabilityZone( launchConfig.getZone() ); lc.setRamdiskId( launchConfig.getRamdiskId() ); lc.setKernelId( launchConfig.getKernelId() ); lc.setBlockDevicemappings( launchConfig.getBlockDeviceMappings() ); try { return this.jec2.runInstances( lc ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "runInstances", ec2Ex ); //$NON-NLS-1$ } } public List<GroupDescription> describeSecurityGroups( List<String> securityGroups ) throws EC2ServiceException { ensureAuthentication(); securityGroups = nullCheck( securityGroups ); try { return this.jec2.describeSecurityGroups( securityGroups ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "describeSecurityGroups", ec2Ex ); //$NON-NLS-1$ } } public List<AvailabilityZone> describeAvailabilityZones( List<String> zones ) throws EC2ServiceException { ensureAuthentication(); zones = nullCheck( zones ); try { return this.jec2.describeAvailabilityZones( zones ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "describeAvailabilityZones", ec2Ex ); //$NON-NLS-1$ } } public List<KeyPairInfo> describeKeypairs( List<String> keypairs ) throws EC2ServiceException { ensureAuthentication(); keypairs = nullCheck( keypairs ); try { return this.jec2.describeKeyPairs( keypairs ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "describeKeyPairs", ec2Ex ); //$NON-NLS-1$ } } public List<ReservationDescription> describeInstances( List<String> instanceList ) throws EC2ServiceException { ensureAuthentication(); instanceList = nullCheck( instanceList ); try { return this.jec2.describeInstances( instanceList ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "describeInstances", ec2Ex ); //$NON-NLS-1$ } } public List<TerminatingInstanceDescription> terminateInstances( List<String> instanceList ) throws EC2ServiceException { ensureAuthentication(); instanceList = nullCheck( instanceList ); try { return this.jec2.terminateInstances( instanceList ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "terminateInstances", ec2Ex ); //$NON-NLS-1$ } } public void authorizeSecurityGroup( final String groupName, final String cidrIp, final String ipProtocol, final int fromPort, final int toPort ) throws EC2ServiceException { ensureAuthentication(); try { this.jec2.authorizeSecurityGroupIngress( groupName, ipProtocol, fromPort, toPort, cidrIp ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "authorizeSecurityGroupIngress", ec2Ex ); //$NON-NLS-1$ } } public void authorizeSecurityGroup( final String groupName, final String secGroupName, final String secGroupOwnerId ) throws EC2ServiceException { ensureAuthentication(); try { this.jec2.authorizeSecurityGroupIngress( groupName, secGroupName, secGroupOwnerId ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "authorizeSecurityGroupIngress", ec2Ex ); //$NON-NLS-1$ } } public void revokeSecurityGroup( final String groupName, final String cidrIp, final String ipProtocol, final int fromPort, final int toPort ) throws EC2ServiceException { ensureAuthentication(); try { this.jec2.revokeSecurityGroupIngress( groupName, ipProtocol, fromPort, toPort, cidrIp ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "revokeSecurityGroupIngress", ec2Ex ); //$NON-NLS-1$ } } public void revokeSecurityGroup( final String groupName, final String secGroupName, final String secGroupOwnerId ) throws EC2ServiceException { ensureAuthentication(); try { this.jec2.revokeSecurityGroupIngress( groupName, secGroupName, secGroupOwnerId ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "revokeSecurityGroupIngress", ec2Ex ); //$NON-NLS-1$ } } public void deleteSecurityGroup( final String securityGroup ) throws EC2ServiceException { ensureAuthentication(); try { this.jec2.deleteSecurityGroup( securityGroup ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "deleteSecurityGroup", ec2Ex ); //$NON-NLS-1$ } } @Override public boolean equals( final Object obj ) { if( obj instanceof EC2 ) { EC2 ec2 = ( EC2 )obj; if( ec2.awsAccessId.equals( this.awsAccessId ) ) { return true; } } return false; } /** * Creates an {@link EC2ServiceException} with a meaningful error message. * Also logs the message + exception. * * @param origine the source of the exception * @param ec2Ex the exception to wrap * @return the newly created exception */ private EC2ServiceException getEC2ServiceException( final String origine, final EC2Exception ec2Ex ) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append( "Could not '" ); //$NON-NLS-1$ stringBuilder.append( origine ); stringBuilder.append( "' for " ); //$NON-NLS-1$ stringBuilder.append( this.awsAccessId ); stringBuilder.append( " @ " ); //$NON-NLS-1$ stringBuilder.append( this.ec2Url ); Activator.log( stringBuilder.toString(), ec2Ex ); return new EC2ServiceException( stringBuilder.toString(), ec2Ex ); } public void createSecurityGroup( final String securityGroupName, final String securityGroupDescription ) throws EC2ServiceException { ensureAuthentication(); try { this.jec2.createSecurityGroup( securityGroupName, securityGroupDescription ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "createSecurityGroup", ec2Ex ); //$NON-NLS-1$ } } public KeyPairInfo createKeypair( final String keypairName ) throws EC2ServiceException { ensureAuthentication(); try { return this.jec2.createKeyPair( keypairName ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "createKeypair", ec2Ex ); //$NON-NLS-1$ } } public String allocateAddress() throws EC2ServiceException { ensureAuthentication(); try { return this.jec2.allocateAddress(); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "allocateAddress", ec2Ex ); //$NON-NLS-1$ } } public List<AddressInfo> describeAddresses( final List<String> addresses ) throws EC2ServiceException { ensureAuthentication(); try { return this.jec2.describeAddresses( addresses ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "describeAddresses", ec2Ex ); //$NON-NLS-1$ } } public void associateAddress( final String instanceId, final String elasticIP ) throws EC2ServiceException { ensureAuthentication(); try { this.jec2.associateAddress( instanceId, elasticIP ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "associateAddress", ec2Ex ); //$NON-NLS-1$ } } public void disassociateAddress( final String elasticIP ) throws EC2ServiceException { ensureAuthentication(); try { this.jec2.disassociateAddress( elasticIP ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "disassociateAddress", ec2Ex ); //$NON-NLS-1$ } } public void releaseAddress( final String elasticIP ) throws EC2ServiceException { ensureAuthentication(); try { this.jec2.releaseAddress( elasticIP ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "releaseAddress", ec2Ex ); //$NON-NLS-1$ } } public void rebootInstances( final List<String> instances ) throws EC2ServiceException { ensureAuthentication(); try { this.jec2.rebootInstances( instances ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "rebootInstances", ec2Ex ); //$NON-NLS-1$ } } public String registerImage( final String bucketPath ) throws EC2ServiceException { ensureAuthentication(); try { return this.jec2.registerImage( bucketPath ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "registerImage", ec2Ex ); //$NON-NLS-1$ } } public DescribeImageAttributeResult describeImageAttributes( final String imageId, final ImageAttributeType imageAttribute ) throws EC2ServiceException { ensureAuthentication(); try { return this.jec2.describeImageAttribute( imageId, imageAttribute ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "describeImageAttribute", ec2Ex ); //$NON-NLS-1$ } } public void modifyImageAttribute( final String imageId, final ImageListAttribute attribute, final ImageListAttributeOperationType operationType ) throws EC2ServiceException { ensureAuthentication(); try { this.jec2.modifyImageAttribute( imageId, attribute, operationType ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "modifyImageAttribute", ec2Ex ); //$NON-NLS-1$ } } public void deleteKeypair( final String keypair ) throws EC2ServiceException { ensureAuthentication(); try { this.jec2.deleteKeyPair( keypair ); } catch( EC2Exception ec2Ex ) { throw getEC2ServiceException( "deleteKeypair", ec2Ex ); //$NON-NLS-1$ } } }