| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you under the Apache License, Version 2.0 (the | |
| // "License"); you may not use this file except in compliance | |
| // with the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, | |
| // software distributed under the License is distributed on an | |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| // KIND, either express or implied. See the License for the | |
| // specific language governing permissions and limitations | |
| // under the License. | |
| package com.cloud.bridge.service; | |
| import java.io.ByteArrayInputStream; | |
| import java.io.File; | |
| import java.io.FileInputStream; | |
| import java.io.FileNotFoundException; | |
| import java.io.FileOutputStream; | |
| import java.io.IOException; | |
| import java.io.OutputStream; | |
| import java.io.OutputStreamWriter; | |
| import java.net.URLEncoder; | |
| import java.security.KeyStore; | |
| import java.security.SignatureException; | |
| import java.security.cert.Certificate; | |
| import java.security.cert.CertificateFactory; | |
| import java.sql.SQLException; | |
| import java.text.ParseException; | |
| import java.util.ArrayList; | |
| import java.util.Calendar; | |
| import java.util.Date; | |
| import java.util.Enumeration; | |
| import java.util.List; | |
| import java.util.Properties; | |
| import java.util.UUID; | |
| import javax.servlet.ServletConfig; | |
| import javax.servlet.ServletException; | |
| import javax.servlet.http.HttpServlet; | |
| import javax.servlet.http.HttpServletRequest; | |
| import javax.servlet.http.HttpServletResponse; | |
| import javax.xml.stream.XMLOutputFactory; | |
| import javax.xml.stream.XMLStreamException; | |
| import javax.xml.stream.XMLStreamWriter; | |
| import org.apache.axiom.om.OMAbstractFactory; | |
| import org.apache.axiom.om.OMFactory; | |
| import org.apache.axis2.AxisFault; | |
| import org.apache.axis2.databinding.ADBBean; | |
| import org.apache.axis2.databinding.ADBException; | |
| import org.apache.axis2.databinding.utils.writer.MTOMAwareXMLSerializer; | |
| import org.apache.commons.codec.binary.Base64; | |
| import org.apache.log4j.Logger; | |
| import com.amazon.ec2.AllocateAddressResponse; | |
| import com.amazon.ec2.AssociateAddressResponse; | |
| import com.amazon.ec2.AttachVolumeResponse; | |
| import com.amazon.ec2.AuthorizeSecurityGroupIngressResponse; | |
| import com.amazon.ec2.CreateImageResponse; | |
| import com.amazon.ec2.CreateKeyPairResponse; | |
| import com.amazon.ec2.CreateSecurityGroupResponse; | |
| import com.amazon.ec2.CreateSnapshotResponse; | |
| import com.amazon.ec2.CreateVolumeResponse; | |
| import com.amazon.ec2.DeleteKeyPairResponse; | |
| import com.amazon.ec2.DeleteSecurityGroupResponse; | |
| import com.amazon.ec2.DeleteSnapshotResponse; | |
| import com.amazon.ec2.DeleteVolumeResponse; | |
| import com.amazon.ec2.DeregisterImageResponse; | |
| import com.amazon.ec2.DescribeAvailabilityZonesResponse; | |
| import com.amazon.ec2.DescribeImageAttributeResponse; | |
| import com.amazon.ec2.DescribeImagesResponse; | |
| import com.amazon.ec2.DescribeInstanceAttributeResponse; | |
| import com.amazon.ec2.DescribeInstancesResponse; | |
| import com.amazon.ec2.DescribeKeyPairsResponse; | |
| import com.amazon.ec2.DescribeSecurityGroupsResponse; | |
| import com.amazon.ec2.DescribeSnapshotsResponse; | |
| import com.amazon.ec2.DescribeVolumesResponse; | |
| import com.amazon.ec2.DetachVolumeResponse; | |
| import com.amazon.ec2.DisassociateAddressResponse; | |
| import com.amazon.ec2.GetPasswordDataResponse; | |
| import com.amazon.ec2.ImportKeyPairResponse; | |
| import com.amazon.ec2.ModifyImageAttributeResponse; | |
| import com.amazon.ec2.RebootInstancesResponse; | |
| import com.amazon.ec2.RegisterImageResponse; | |
| import com.amazon.ec2.ReleaseAddressResponse; | |
| import com.amazon.ec2.ResetImageAttributeResponse; | |
| import com.amazon.ec2.RevokeSecurityGroupIngressResponse; | |
| import com.amazon.ec2.RunInstancesResponse; | |
| import com.amazon.ec2.StartInstancesResponse; | |
| import com.amazon.ec2.StopInstancesResponse; | |
| import com.amazon.ec2.TerminateInstancesResponse; | |
| import com.cloud.bridge.model.UserCredentialsVO; | |
| import com.cloud.bridge.persist.dao.OfferingDaoImpl; | |
| import com.cloud.bridge.persist.dao.UserCredentialsDaoImpl; | |
| import com.cloud.bridge.service.controller.s3.ServiceProvider; | |
| import com.cloud.bridge.service.core.ec2.EC2AssociateAddress; | |
| import com.cloud.bridge.service.core.ec2.EC2AuthorizeRevokeSecurityGroup; | |
| import com.cloud.bridge.service.core.ec2.EC2CreateImage; | |
| import com.cloud.bridge.service.core.ec2.EC2CreateKeyPair; | |
| import com.cloud.bridge.service.core.ec2.EC2CreateVolume; | |
| import com.cloud.bridge.service.core.ec2.EC2DeleteKeyPair; | |
| import com.cloud.bridge.service.core.ec2.EC2DescribeAddresses; | |
| import com.cloud.bridge.service.core.ec2.EC2DescribeAvailabilityZones; | |
| import com.cloud.bridge.service.core.ec2.EC2DescribeImages; | |
| import com.cloud.bridge.service.core.ec2.EC2DescribeInstances; | |
| import com.cloud.bridge.service.core.ec2.EC2DescribeKeyPairs; | |
| import com.cloud.bridge.service.core.ec2.EC2DescribeSecurityGroups; | |
| import com.cloud.bridge.service.core.ec2.EC2DescribeSnapshots; | |
| import com.cloud.bridge.service.core.ec2.EC2DescribeVolumes; | |
| import com.cloud.bridge.service.core.ec2.EC2DisassociateAddress; | |
| import com.cloud.bridge.service.core.ec2.EC2Engine; | |
| import com.cloud.bridge.service.core.ec2.EC2Filter; | |
| import com.cloud.bridge.service.core.ec2.EC2GroupFilterSet; | |
| import com.cloud.bridge.service.core.ec2.EC2Image; | |
| import com.cloud.bridge.service.core.ec2.EC2ImportKeyPair; | |
| import com.cloud.bridge.service.core.ec2.EC2InstanceFilterSet; | |
| import com.cloud.bridge.service.core.ec2.EC2IpPermission; | |
| import com.cloud.bridge.service.core.ec2.EC2KeyPairFilterSet; | |
| import com.cloud.bridge.service.core.ec2.EC2RebootInstances; | |
| import com.cloud.bridge.service.core.ec2.EC2RegisterImage; | |
| import com.cloud.bridge.service.core.ec2.EC2ReleaseAddress; | |
| import com.cloud.bridge.service.core.ec2.EC2RunInstances; | |
| import com.cloud.bridge.service.core.ec2.EC2SecurityGroup; | |
| import com.cloud.bridge.service.core.ec2.EC2SnapshotFilterSet; | |
| import com.cloud.bridge.service.core.ec2.EC2StartInstances; | |
| import com.cloud.bridge.service.core.ec2.EC2StopInstances; | |
| import com.cloud.bridge.service.core.ec2.EC2Volume; | |
| import com.cloud.bridge.service.core.ec2.EC2VolumeFilterSet; | |
| import com.cloud.bridge.service.exception.EC2ServiceException; | |
| import com.cloud.bridge.service.exception.NoSuchObjectException; | |
| import com.cloud.bridge.service.exception.PermissionDeniedException; | |
| import com.cloud.bridge.service.exception.EC2ServiceException.ClientError; | |
| import com.cloud.bridge.util.AuthenticationUtils; | |
| import com.cloud.bridge.util.ConfigurationHelper; | |
| import com.cloud.bridge.util.EC2RestAuth; | |
| import com.cloud.stack.models.CloudStackAccount; | |
| import com.cloud.utils.component.ComponentLocator; | |
| import com.cloud.utils.db.Transaction; | |
| public class EC2RestServlet extends HttpServlet { | |
| private static final long serialVersionUID = -6168996266762804888L; | |
| protected final UserCredentialsDaoImpl ucDao = ComponentLocator.inject(UserCredentialsDaoImpl.class); | |
| protected final OfferingDaoImpl ofDao = ComponentLocator.inject(OfferingDaoImpl.class); | |
| public static final Logger logger = Logger.getLogger(EC2RestServlet.class); | |
| private OMFactory factory = OMAbstractFactory.getOMFactory(); | |
| private XMLOutputFactory xmlOutFactory = XMLOutputFactory.newInstance(); | |
| private String pathToKeystore = null; | |
| private String keystorePassword = null; | |
| private String wsdlVersion = null; | |
| private String version = null; | |
| boolean debug=true; | |
| /** | |
| * We build the path to where the keystore holding the WS-Security X509 certificates | |
| * are stored. | |
| */ | |
| @Override | |
| public void init( ServletConfig config ) throws ServletException { | |
| File propertiesFile = ConfigurationHelper.findConfigurationFile("ec2-service.properties"); | |
| Properties EC2Prop = null; | |
| if (null != propertiesFile) { | |
| logger.info("Use EC2 properties file: " + propertiesFile.getAbsolutePath()); | |
| EC2Prop = new Properties(); | |
| try { | |
| EC2Prop.load( new FileInputStream( propertiesFile )); | |
| } catch (FileNotFoundException e) { | |
| logger.warn("Unable to open properties file: " + propertiesFile.getAbsolutePath(), e); | |
| } catch (IOException e) { | |
| logger.warn("Unable to read properties file: " + propertiesFile.getAbsolutePath(), e); | |
| } | |
| String keystore = EC2Prop.getProperty( "keystore" ); | |
| keystorePassword = EC2Prop.getProperty( "keystorePass" ); | |
| wsdlVersion = EC2Prop.getProperty( "WSDLVersion", "2010-11-15" ); | |
| version = EC2Prop.getProperty( "cloudbridgeVersion", "UNKNOWN VERSION" ); | |
| String installedPath = System.getenv("CATALINA_HOME"); | |
| if (installedPath == null) installedPath = System.getenv("CATALINA_BASE"); | |
| if (installedPath == null) installedPath = System.getProperty("catalina.home"); | |
| String webappPath = config.getServletContext().getRealPath("/"); | |
| //pathToKeystore = new String( installedPath + File.separator + "webapps" + File.separator + webappName + File.separator + "WEB-INF" + File.separator + "classes" + File.separator + keystore ); | |
| pathToKeystore = new String( webappPath + "WEB-INF" + File.separator + "classes" + File.separator + keystore ); | |
| } | |
| } | |
| @Override | |
| protected void doGet(HttpServletRequest req, HttpServletResponse resp) { | |
| doGetOrPost(req, resp); | |
| } | |
| @Override | |
| protected void doPost(HttpServletRequest req, HttpServletResponse resp) { | |
| doGetOrPost(req, resp); | |
| } | |
| protected void doGetOrPost(HttpServletRequest request, HttpServletResponse response) { | |
| if(debug){ | |
| System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.request_uri: "+request.getAttribute("javax.servlet.forward.request_uri")); | |
| System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.context_path: "+request.getAttribute("javax.servlet.forward.context_path")); | |
| System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.servlet_path: "+request.getAttribute("javax.servlet.forward.servlet_path")); | |
| System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.path_info: "+request.getAttribute("javax.servlet.forward.path_info")); | |
| System.out.println("EC2RestServlet.doGetOrPost: javax.servlet.forward.query_string: "+request.getAttribute("javax.servlet.forward.query_string")); | |
| } | |
| String action = request.getParameter( "Action" ); | |
| logRequest(request); | |
| // -> unauthenticated calls, should still be done over HTTPS | |
| if (action.equalsIgnoreCase( "SetUserKeys" )) { | |
| setUserKeys(request, response); | |
| return; | |
| } | |
| if (action.equalsIgnoreCase( "CloudEC2Version" )) { | |
| cloudEC2Version(request, response); | |
| return; | |
| } | |
| // -> authenticated calls | |
| try { | |
| if (!authenticateRequest( request, response )) return; | |
| if (action.equalsIgnoreCase( "AllocateAddress" )) allocateAddress(request, response); | |
| else if (action.equalsIgnoreCase( "AssociateAddress" )) associateAddress(request, response); | |
| else if (action.equalsIgnoreCase( "AttachVolume" )) attachVolume(request, response ); | |
| else if (action.equalsIgnoreCase( "AuthorizeSecurityGroupIngress" )) authorizeSecurityGroupIngress(request, response); | |
| else if (action.equalsIgnoreCase( "CreateImage" )) createImage(request, response); | |
| else if (action.equalsIgnoreCase( "CreateSecurityGroup" )) createSecurityGroup(request, response); | |
| else if (action.equalsIgnoreCase( "CreateSnapshot" )) createSnapshot(request, response); | |
| else if (action.equalsIgnoreCase( "CreateVolume" )) createVolume(request, response); | |
| else if (action.equalsIgnoreCase( "DeleteSecurityGroup" )) deleteSecurityGroup(request, response); | |
| else if (action.equalsIgnoreCase( "DeleteSnapshot" )) deleteSnapshot(request, response); | |
| else if (action.equalsIgnoreCase( "DeleteVolume" )) deleteVolume(request, response); | |
| else if (action.equalsIgnoreCase( "DeregisterImage" )) deregisterImage(request, response); | |
| else if (action.equalsIgnoreCase( "DescribeAddresses" )) describeAddresses(request, response); | |
| else if (action.equalsIgnoreCase( "DescribeAvailabilityZones" )) describeAvailabilityZones(request, response); | |
| else if (action.equalsIgnoreCase( "DescribeImageAttribute" )) describeImageAttribute(request, response); | |
| else if (action.equalsIgnoreCase( "DescribeImages" )) describeImages(request, response); | |
| else if (action.equalsIgnoreCase( "DescribeInstanceAttribute" )) describeInstanceAttribute(request, response); | |
| else if (action.equalsIgnoreCase( "DescribeInstances" )) describeInstances(request, response); | |
| else if (action.equalsIgnoreCase( "DescribeSecurityGroups" )) describeSecurityGroups(request, response); | |
| else if (action.equalsIgnoreCase( "DescribeSnapshots" )) describeSnapshots(request, response); | |
| else if (action.equalsIgnoreCase( "DescribeVolumes" )) describeVolumes(request, response); | |
| else if (action.equalsIgnoreCase( "DetachVolume" )) detachVolume(request, response); | |
| else if (action.equalsIgnoreCase( "DisassociateAddress" )) disassociateAddress(request, response); | |
| else if (action.equalsIgnoreCase( "ModifyImageAttribute" )) modifyImageAttribute(request, response); | |
| else if (action.equalsIgnoreCase( "RebootInstances" )) rebootInstances(request, response); | |
| else if (action.equalsIgnoreCase( "RegisterImage" )) registerImage(request, response); | |
| else if (action.equalsIgnoreCase( "ReleaseAddress" )) releaseAddress(request, response); | |
| else if (action.equalsIgnoreCase( "ResetImageAttribute" )) resetImageAttribute(request, response); | |
| else if (action.equalsIgnoreCase( "RevokeSecurityGroupIngress")) revokeSecurityGroupIngress(request, response); | |
| else if (action.equalsIgnoreCase( "RunInstances" )) runInstances(request, response); | |
| else if (action.equalsIgnoreCase( "StartInstances" )) startInstances(request, response); | |
| else if (action.equalsIgnoreCase( "StopInstances" )) stopInstances(request, response); | |
| else if (action.equalsIgnoreCase( "TerminateInstances" )) terminateInstances(request, response); | |
| else if (action.equalsIgnoreCase( "SetCertificate" )) setCertificate(request, response); | |
| else if (action.equalsIgnoreCase( "DeleteCertificate" )) deleteCertificate(request, response); | |
| else if (action.equalsIgnoreCase( "SetOfferMapping" )) setOfferMapping(request, response); | |
| else if (action.equalsIgnoreCase( "DeleteOfferMapping" )) deleteOfferMapping(request, response); | |
| else if (action.equalsIgnoreCase( "CreateKeyPair" )) createKeyPair(request, response); | |
| else if (action.equalsIgnoreCase( "ImportKeyPair" )) importKeyPair(request, response); | |
| else if (action.equalsIgnoreCase( "DeleteKeyPair" )) deleteKeyPair(request, response); | |
| else if (action.equalsIgnoreCase( "DescribeKeyPairs" )) describeKeyPairs(request, response); | |
| else if (action.equalsIgnoreCase( "GetPasswordData" )) getPasswordData(request, response); | |
| else { | |
| logger.error("Unsupported action " + action); | |
| throw new EC2ServiceException(ClientError.Unsupported, "This operation is not available"); | |
| } | |
| } catch( EC2ServiceException e ) { | |
| response.setStatus(e.getErrorCode()); | |
| if (e.getCause() != null && e.getCause() instanceof AxisFault) | |
| faultResponse(response, ((AxisFault)e.getCause()).getFaultCode().getLocalPart(), e.getMessage()); | |
| else { | |
| logger.error("EC2ServiceException: " + e.getMessage(), e); | |
| endResponse(response, e.toString()); | |
| } | |
| } catch( PermissionDeniedException e ) { | |
| logger.error("Unexpected exception: " + e.getMessage(), e); | |
| response.setStatus(403); | |
| endResponse(response, "Access denied"); | |
| } catch( Exception e ) { | |
| logger.error("Unexpected exception: " + e.getMessage(), e); | |
| response.setStatus(500); | |
| endResponse(response, e.toString()); | |
| } finally { | |
| try { | |
| response.flushBuffer(); | |
| } catch (IOException e) { | |
| logger.error("Unexpected exception " + e.getMessage(), e); | |
| } | |
| } | |
| } | |
| /** | |
| * Provide an easy way to determine the version of the implementation running. | |
| * | |
| * This is an unauthenticated REST call. | |
| */ | |
| private void cloudEC2Version( HttpServletRequest request, HttpServletResponse response ) { | |
| String version_response = new String( "<?xml version=\"1.0\" encoding=\"utf-8\"?><CloudEC2Version>" + version + "</CloudEC2Version>" ); | |
| response.setStatus(200); | |
| endResponse(response, version_response); | |
| } | |
| /** | |
| * This request registers the Cloud.com account holder to the EC2 service. The Cloud.com | |
| * account holder saves his API access and secret keys with the EC2 service so that | |
| * the EC2 service can make Cloud.com API calls on his behalf. The given API access | |
| * and secret key are saved into the "usercredentials" database table. | |
| * | |
| * This is an unauthenticated REST call. The only required parameters are 'accesskey' and | |
| * 'secretkey'. | |
| * | |
| * To verify that the given keys represent an existing account they are used to execute the | |
| * Cloud.com's listAccounts API function. If the keys do not represent a valid account the | |
| * listAccounts function will fail. | |
| * | |
| * A user can call this REST function any number of times, on each call the Cloud.com secret | |
| * key is simply over writes any previously stored value. | |
| * | |
| * As with all REST calls HTTPS should be used to ensure their security. | |
| */ | |
| private void setUserKeys( HttpServletRequest request, HttpServletResponse response ) { | |
| String[] accessKey = null; | |
| String[] secretKey = null; | |
| Transaction txn = null; | |
| try { | |
| // -> all these parameters are required | |
| accessKey = request.getParameterValues( "accesskey" ); | |
| if ( null == accessKey || 0 == accessKey.length ) { | |
| response.sendError(530, "Missing accesskey parameter" ); | |
| return; | |
| } | |
| secretKey = request.getParameterValues( "secretkey" ); | |
| if ( null == secretKey || 0 == secretKey.length ) { | |
| response.sendError(530, "Missing secretkey parameter" ); | |
| return; | |
| } | |
| } catch( Exception e ) { | |
| logger.error("SetUserKeys exception " + e.getMessage(), e); | |
| response.setStatus(500); | |
| endResponse(response, "SetUserKeys exception " + e.getMessage()); | |
| return; | |
| } | |
| // prime UserContext here | |
| // logger.debug("initializing context"); | |
| UserContext context = UserContext.current(); | |
| try { | |
| txn = Transaction.open(Transaction.AWSAPI_DB); | |
| // -> use the keys to see if the account actually exists | |
| ServiceProvider.getInstance().getEC2Engine().validateAccount( accessKey[0], secretKey[0] ); | |
| /* UserCredentialsDao credentialDao = new UserCredentialsDao(); | |
| credentialDao.setUserKeys( ); | |
| */ UserCredentialsVO user = new UserCredentialsVO(accessKey[0], secretKey[0]); | |
| ucDao.persist(user); | |
| txn.commit(); | |
| } catch( Exception e ) { | |
| logger.error("SetUserKeys " + e.getMessage(), e); | |
| response.setStatus(401); | |
| endResponse(response, e.toString()); | |
| txn.close(); | |
| return; | |
| } | |
| response.setStatus(200); | |
| endResponse(response, "User keys set successfully"); | |
| } | |
| /** | |
| * The SOAP API for EC2 uses WS-Security to sign all client requests. This requires that | |
| * the client have a public/private key pair and the public key defined by a X509 certificate. | |
| * Thus in order for a Cloud.com account holder to use the EC2's SOAP API he must register | |
| * his X509 certificate with the EC2 service. This function allows the Cloud.com account | |
| * holder to "load" his X509 certificate into the service. Note, that the SetUserKeys REST | |
| * function must be called before this call. | |
| * | |
| * This is an authenticated REST call and as such must contain all the required REST parameters | |
| * including: Signature, Timestamp, Expires, etc. The signature is calculated using the | |
| * Cloud.com account holder's API access and secret keys and the Amazon defined EC2 signature | |
| * algorithm. | |
| * | |
| * A user can call this REST function any number of times, on each call the X509 certificate | |
| * simply over writes any previously stored value. | |
| */ | |
| private void setCertificate( HttpServletRequest request, HttpServletResponse response ) | |
| throws Exception { | |
| Transaction txn = null; | |
| try { | |
| // [A] Pull the cert and cloud AccessKey from the request | |
| String[] certificate = request.getParameterValues( "cert" ); | |
| if (null == certificate || 0 == certificate.length) { | |
| response.sendError(530, "Missing cert parameter" ); | |
| return; | |
| } | |
| // logger.debug( "SetCertificate cert: [" + certificate[0] + "]" ); | |
| String [] accessKey = request.getParameterValues( "AWSAccessKeyId" ); | |
| if ( null == accessKey || 0 == accessKey.length ) { | |
| response.sendError(530, "Missing AWSAccessKeyId parameter" ); | |
| return; | |
| } | |
| // [B] Open our keystore | |
| FileInputStream fsIn = new FileInputStream( pathToKeystore ); | |
| KeyStore certStore = KeyStore.getInstance( "JKS" ); | |
| certStore.load( fsIn, keystorePassword.toCharArray()); | |
| // -> use the Cloud API key to save the cert in the keystore | |
| // -> write the cert into the keystore on disk | |
| Certificate userCert = null; | |
| CertificateFactory cf = CertificateFactory.getInstance( "X.509" ); | |
| ByteArrayInputStream bs = new ByteArrayInputStream( certificate[0].getBytes()); | |
| while (bs.available() > 0) userCert = cf.generateCertificate(bs); | |
| certStore.setCertificateEntry( accessKey[0], userCert ); | |
| FileOutputStream fsOut = new FileOutputStream( pathToKeystore ); | |
| certStore.store( fsOut, keystorePassword.toCharArray()); | |
| // [C] Associate the cert's uniqueId with the Cloud API keys | |
| String uniqueId = AuthenticationUtils.X509CertUniqueId( userCert ); | |
| logger.debug( "SetCertificate, uniqueId: " + uniqueId ); | |
| /* UserCredentialsDao credentialDao = new UserCredentialsDao(); | |
| credentialDao.setCertificateId( accessKey[0], uniqueId ); | |
| */ | |
| txn = Transaction.open(Transaction.AWSAPI_DB); | |
| UserCredentialsVO user = ucDao.getByAccessKey(accessKey[0]); | |
| user.setCertUniqueId(uniqueId); | |
| ucDao.update(user.getId(), user); | |
| response.setStatus(200); | |
| endResponse(response, "User certificate set successfully"); | |
| txn.commit(); | |
| } catch( NoSuchObjectException e ) { | |
| logger.error("SetCertificate exception " + e.getMessage(), e); | |
| response.sendError(404, "SetCertificate exception " + e.getMessage()); | |
| } catch( Exception e ) { | |
| logger.error("SetCertificate exception " + e.getMessage(), e); | |
| response.sendError(500, "SetCertificate exception " + e.getMessage()); | |
| } finally { | |
| txn.close(); | |
| } | |
| } | |
| /** | |
| * The SOAP API for EC2 uses WS-Security to sign all client requests. This requires that | |
| * the client have a public/private key pair and the public key defined by a X509 certificate. | |
| * This REST call allows a Cloud.com account holder to remove a previouly "loaded" X509 | |
| * certificate out of the EC2 service. | |
| * | |
| * This is an unauthenticated REST call and as such must contain all the required REST parameters | |
| * including: Signature, Timestamp, Expires, etc. The signature is calculated using the | |
| * Cloud.com account holder's API access and secret keys and the Amazon defined EC2 signature | |
| * algorithm. | |
| */ | |
| private void deleteCertificate( HttpServletRequest request, HttpServletResponse response ) | |
| throws Exception { | |
| Transaction txn = null; | |
| try { | |
| String [] accessKey = request.getParameterValues( "AWSAccessKeyId" ); | |
| if ( null == accessKey || 0 == accessKey.length ) { | |
| response.sendError(530, "Missing AWSAccessKeyId parameter" ); | |
| return; | |
| } | |
| // -> delete the specified entry and save back to disk | |
| FileInputStream fsIn = new FileInputStream( pathToKeystore ); | |
| KeyStore certStore = KeyStore.getInstance( "JKS" ); | |
| certStore.load( fsIn, keystorePassword.toCharArray()); | |
| if ( certStore.containsAlias( accessKey[0] )) { | |
| certStore.deleteEntry( accessKey[0] ); | |
| FileOutputStream fsOut = new FileOutputStream( pathToKeystore ); | |
| certStore.store( fsOut, keystorePassword.toCharArray()); | |
| // -> dis-associate the cert's uniqueId with the Cloud API keys | |
| /* UserCredentialsDao credentialDao = new UserCredentialsDao(); | |
| credentialDao.setCertificateId( accessKey[0], null ); | |
| */ txn = Transaction.open(Transaction.AWSAPI_DB); | |
| UserCredentialsVO user = ucDao.getByAccessKey(accessKey[0]); | |
| user.setCertUniqueId(null); | |
| ucDao.update(user.getId(), user); | |
| response.setStatus(200); | |
| endResponse(response, "User certificate deleted successfully"); | |
| txn.commit(); | |
| } | |
| else response.setStatus(404); | |
| } catch( NoSuchObjectException e ) { | |
| logger.error("SetCertificate exception " + e.getMessage(), e); | |
| response.sendError(404, "SetCertificate exception " + e.getMessage()); | |
| } catch( Exception e ) { | |
| logger.error("DeleteCertificate exception " + e.getMessage(), e); | |
| response.sendError(500, "DeleteCertificate exception " + e.getMessage()); | |
| } finally { | |
| txn.close(); | |
| } | |
| } | |
| /** | |
| * Allow the caller to define the mapping between the Amazon instance type strings | |
| * (e.g., m1.small, cc1.4xlarge) and the cloudstack service offering ids. Setting | |
| * an existing mapping just over writes the prevous values. | |
| */ | |
| private void setOfferMapping( HttpServletRequest request, HttpServletResponse response ) { | |
| String amazonOffer = null; | |
| String cloudOffer = null; | |
| try { | |
| // -> all these parameters are required | |
| amazonOffer = request.getParameter( "amazonoffer" ); | |
| if ( null == amazonOffer ) { | |
| response.sendError(530, "Missing amazonoffer parameter" ); | |
| return; | |
| } | |
| cloudOffer = request.getParameter( "cloudoffer" ); | |
| if ( null == cloudOffer ) { | |
| response.sendError(530, "Missing cloudoffer parameter" ); | |
| return; | |
| } | |
| } catch( Exception e ) { | |
| logger.error("SetOfferMapping exception " + e.getMessage(), e); | |
| response.setStatus(500); | |
| endResponse(response, "SetOfferMapping exception " + e.getMessage()); | |
| return; | |
| } | |
| // validate account is admin level | |
| try { | |
| CloudStackAccount currentAccount = ServiceProvider.getInstance().getEC2Engine().getCurrentAccount(); | |
| if (currentAccount.getAccountType() != 1) { | |
| logger.debug("SetOfferMapping called by non-admin user!"); | |
| response.setStatus(500); | |
| endResponse(response, "Permission denied for non-admin user to setOfferMapping!"); | |
| return; | |
| } | |
| } catch (Exception e) { | |
| logger.error("SetOfferMapping " + e.getMessage(), e); | |
| response.setStatus(401); | |
| endResponse(response, e.toString()); | |
| return; | |
| } | |
| try { | |
| ofDao.setOfferMapping( amazonOffer, cloudOffer ); | |
| } catch( Exception e ) { | |
| logger.error("SetOfferMapping " + e.getMessage(), e); | |
| response.setStatus(401); | |
| endResponse(response, e.toString()); | |
| return; | |
| } | |
| response.setStatus(200); | |
| endResponse(response, "offering mapping set successfully"); | |
| } | |
| private void deleteOfferMapping( HttpServletRequest request, HttpServletResponse response ) { | |
| String amazonOffer = null; | |
| try { | |
| // -> all these parameters are required | |
| amazonOffer = request.getParameter( "amazonoffer" ); | |
| if ( null == amazonOffer ) { | |
| response.sendError(530, "Missing amazonoffer parameter" ); | |
| return; | |
| } | |
| } catch( Exception e ) { | |
| logger.error("DeleteOfferMapping exception " + e.getMessage(), e); | |
| response.setStatus(500); | |
| endResponse(response, "DeleteOfferMapping exception " + e.getMessage()); | |
| return; | |
| } | |
| // validate account is admin level | |
| try { | |
| CloudStackAccount currentAccount = ServiceProvider.getInstance().getEC2Engine().getCurrentAccount(); | |
| if (currentAccount.getAccountType() != 1) { | |
| logger.debug("deleteOfferMapping called by non-admin user!"); | |
| response.setStatus(500); | |
| endResponse(response, "Permission denied for non-admin user to deleteOfferMapping!"); | |
| return; | |
| } | |
| } catch (Exception e) { | |
| logger.error("deleteOfferMapping " + e.getMessage(), e); | |
| response.setStatus(401); | |
| endResponse(response, e.toString()); | |
| return; | |
| } | |
| try { | |
| ofDao.deleteOfferMapping( amazonOffer ); | |
| } catch( Exception e ) { | |
| logger.error("DeleteOfferMapping " + e.getMessage(), e); | |
| response.setStatus(401); | |
| endResponse(response, e.toString()); | |
| return; | |
| } | |
| response.setStatus(200); | |
| endResponse(response, "offering mapping deleted successfully"); | |
| } | |
| /** | |
| * The approach taken here is to map these REST calls into the same objects used | |
| * to implement the matching SOAP requests (e.g., AttachVolume). This is done by parsing | |
| * out the URL parameters and loading them into the relevant EC2XXX object(s). Once | |
| * the parameters are loaded the appropriate EC2Engine function is called to perform | |
| * the requested action. The result of the EC2Engine function is a standard | |
| * Amazon WSDL defined object (e.g., AttachVolumeResponse Java object). Finally the | |
| * serialize method is called on the returned response object to obtain the extected | |
| * response XML. | |
| */ | |
| private void attachVolume( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2Volume EC2request = new EC2Volume(); | |
| // -> all these parameters are required | |
| String[] volumeId = request.getParameterValues( "VolumeId" ); | |
| if ( null != volumeId && 0 < volumeId.length ) | |
| EC2request.setId( volumeId[0] ); | |
| else { response.sendError(530, "Missing VolumeId parameter" ); return; } | |
| String[] instanceId = request.getParameterValues( "InstanceId" ); | |
| if ( null != instanceId && 0 < instanceId.length ) | |
| EC2request.setInstanceId( instanceId[0] ); | |
| else { response.sendError(530, "Missing InstanceId parameter" ); return; } | |
| String[] device = request.getParameterValues( "Device" ); | |
| if ( null != device && 0 < device.length ) | |
| EC2request.setDevice( device[0] ); | |
| else { response.sendError(530, "Missing Device parameter" ); return; } | |
| // -> execute the request | |
| AttachVolumeResponse EC2response = EC2SoapServiceImpl.toAttachVolumeResponse( ServiceProvider.getInstance().getEC2Engine().attachVolume( EC2request )); | |
| serializeResponse(response, EC2response); | |
| } | |
| /** | |
| * The SOAP equivalent of this function appears to allow multiple permissions per request, yet | |
| * in the REST API documentation only one permission is allowed. | |
| */ | |
| private void revokeSecurityGroupIngress( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2AuthorizeRevokeSecurityGroup EC2request = new EC2AuthorizeRevokeSecurityGroup(); | |
| String[] groupName = request.getParameterValues( "GroupName" ); | |
| if ( null != groupName && 0 < groupName.length ) | |
| EC2request.setName( groupName[0] ); | |
| else { response.sendError(530, "Missing GroupName parameter" ); return; } | |
| EC2IpPermission perm = new EC2IpPermission(); | |
| String[] protocol = request.getParameterValues( "IpProtocol" ); | |
| if ( null != protocol && 0 < protocol.length ) | |
| perm.setProtocol( protocol[0] ); | |
| else { response.sendError(530, "Missing IpProtocol parameter" ); return; } | |
| String[] fromPort = request.getParameterValues( "FromPort" ); | |
| if ( null != fromPort && 0 < fromPort.length ) | |
| perm.setProtocol( fromPort[0] ); | |
| else { response.sendError(530, "Missing FromPort parameter" ); return; } | |
| String[] toPort = request.getParameterValues( "ToPort" ); | |
| if ( null != toPort && 0 < toPort.length ) | |
| perm.setProtocol( toPort[0] ); | |
| else { response.sendError(530, "Missing ToPort parameter" ); return; } | |
| String[] ranges = request.getParameterValues( "CidrIp" ); | |
| if ( null != ranges && 0 < ranges.length) | |
| perm.addIpRange( ranges[0] ); | |
| else { response.sendError(530, "Missing CidrIp parameter" ); return; } | |
| String[] user = request.getParameterValues( "SourceSecurityGroupOwnerId" ); | |
| if ( null == user || 0 == user.length) { | |
| response.sendError(530, "Missing SourceSecurityGroupOwnerId parameter" ); | |
| return; | |
| } | |
| String[] name = request.getParameterValues( "SourceSecurityGroupName" ); | |
| if ( null == name || 0 == name.length) { | |
| response.sendError(530, "Missing SourceSecurityGroupName parameter" ); | |
| return; | |
| } | |
| EC2SecurityGroup group = new EC2SecurityGroup(); | |
| group.setAccount( user[0] ); | |
| group.setName( name[0] ); | |
| perm.addUser( group ); | |
| EC2request.addIpPermission( perm ); | |
| // -> execute the request | |
| RevokeSecurityGroupIngressResponse EC2response = EC2SoapServiceImpl.toRevokeSecurityGroupIngressResponse( | |
| ServiceProvider.getInstance().getEC2Engine().revokeSecurityGroup( EC2request )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void authorizeSecurityGroupIngress( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| // -> parse the complicated paramters into our standard object | |
| EC2AuthorizeRevokeSecurityGroup EC2request = new EC2AuthorizeRevokeSecurityGroup(); | |
| String[] groupName = request.getParameterValues( "GroupName" ); | |
| if ( null != groupName && 0 < groupName.length ) | |
| EC2request.setName( groupName[0] ); | |
| else { response.sendError(530, "Missing GroupName parameter" ); return; } | |
| // -> not clear how many parameters there are until we fail to get IpPermissions.n.IpProtocol | |
| int nCount = 1; | |
| do | |
| { EC2IpPermission perm = new EC2IpPermission(); | |
| String[] protocol = request.getParameterValues( "IpPermissions." + nCount + ".IpProtocol" ); | |
| if ( null != protocol && 0 < protocol.length ) | |
| perm.setProtocol( protocol[0] ); | |
| else break; | |
| String[] fromPort = request.getParameterValues( "IpPermissions." + nCount + ".FromPort" ); | |
| if (null != fromPort && 0 < fromPort.length) perm.setProtocol( fromPort[0] ); | |
| String[] toPort = request.getParameterValues( "IpPermissions." + nCount + ".ToPort" ); | |
| if (null != toPort && 0 < toPort.length) perm.setProtocol( toPort[0] ); | |
| // -> list: IpPermissions.n.IpRanges.m.CidrIp | |
| int mCount = 1; | |
| do | |
| { String[] ranges = request.getParameterValues( "IpPermissions." + nCount + ".IpRanges." + mCount + ".CidrIp" ); | |
| if ( null != ranges && 0 < ranges.length) | |
| perm.addIpRange( ranges[0] ); | |
| else break; | |
| mCount++; | |
| } while( true ); | |
| // -> list: IpPermissions.n.Groups.m.UserId and IpPermissions.n.Groups.m.GroupName | |
| mCount = 1; | |
| do | |
| { String[] user = request.getParameterValues( "IpPermissions." + nCount + ".Groups." + mCount + ".UserId" ); | |
| if ( null == user || 0 == user.length) break; | |
| String[] name = request.getParameterValues( "IpPermissions." + nCount + ".Groups." + mCount + ".GroupName" ); | |
| if ( null == name || 0 == name.length) break; | |
| EC2SecurityGroup group = new EC2SecurityGroup(); | |
| group.setAccount( user[0] ); | |
| group.setName( name[0] ); | |
| perm.addUser( group ); | |
| mCount++; | |
| } while( true ); | |
| // -> multiple IP permissions can be specified per group name | |
| EC2request.addIpPermission( perm ); | |
| nCount++; | |
| } while( true ); | |
| if (1 == nCount) { response.sendError(530, "At least one IpPermissions required" ); return; } | |
| // -> execute the request | |
| AuthorizeSecurityGroupIngressResponse EC2response = EC2SoapServiceImpl.toAuthorizeSecurityGroupIngressResponse( | |
| ServiceProvider.getInstance().getEC2Engine().authorizeSecurityGroup( EC2request )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void detachVolume( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2Volume EC2request = new EC2Volume(); | |
| String[] volumeId = request.getParameterValues( "VolumeId" ); | |
| if ( null != volumeId && 0 < volumeId.length ) | |
| EC2request.setId(volumeId[0]); | |
| else { response.sendError(530, "Missing VolumeId parameter" ); return; } | |
| String[] instanceId = request.getParameterValues( "InstanceId" ); | |
| if ( null != instanceId && 0 < instanceId.length ) | |
| EC2request.setInstanceId(instanceId[0]); | |
| String[] device = request.getParameterValues( "Device" ); | |
| if ( null != device && 0 < device.length ) | |
| EC2request.setDevice( device[0] ); | |
| // -> execute the request | |
| DetachVolumeResponse EC2response = EC2SoapServiceImpl.toDetachVolumeResponse( ServiceProvider.getInstance().getEC2Engine().detachVolume( EC2request )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void deleteVolume( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2Volume EC2request = new EC2Volume(); | |
| String[] volumeId = request.getParameterValues( "VolumeId" ); | |
| if ( null != volumeId && 0 < volumeId.length ) | |
| EC2request.setId(volumeId[0]); | |
| else { response.sendError(530, "Missing VolumeId parameter" ); return; } | |
| // -> execute the request | |
| DeleteVolumeResponse EC2response = EC2SoapServiceImpl.toDeleteVolumeResponse( ServiceProvider.getInstance().getEC2Engine().deleteVolume( EC2request )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void createVolume( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2CreateVolume EC2request = new EC2CreateVolume(); | |
| String[] zoneName = request.getParameterValues( "AvailabilityZone" ); | |
| if ( null != zoneName && 0 < zoneName.length ) | |
| EC2request.setZoneName( zoneName[0] ); | |
| else { response.sendError(530, "Missing AvailabilityZone parameter" ); return; } | |
| String[] size = request.getParameterValues( "Size" ); | |
| String[] snapshotId = request.getParameterValues("SnapshotId"); | |
| boolean useSnapshot = false; | |
| boolean useSize = false; | |
| if (null != size && 0 < size.length) | |
| useSize = true; | |
| if (snapshotId != null && snapshotId.length != 0) | |
| useSnapshot = true; | |
| if (useSize && !useSnapshot) { | |
| EC2request.setSize( size[0] ); | |
| } else if (useSnapshot && !useSize) { | |
| EC2request.setSnapshotId(snapshotId[0]); | |
| } else if (useSize && useSnapshot) { | |
| response.sendError(530, "Size and SnapshotId parameters are mutually exclusive" ); return; | |
| } else { | |
| response.sendError(530, "Size or SnapshotId has to be specified" ); return; | |
| } | |
| // -> execute the request | |
| CreateVolumeResponse EC2response = EC2SoapServiceImpl.toCreateVolumeResponse( ServiceProvider.getInstance().getEC2Engine().createVolume( EC2request )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void createSecurityGroup( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| String groupName, groupDescription = null; | |
| String[] name = request.getParameterValues( "GroupName" ); | |
| if ( null != name && 0 < name.length ) | |
| groupName = name[0]; | |
| else { response.sendError(530, "Missing GroupName parameter" ); return; } | |
| String[] desc = request.getParameterValues( "GroupDescription" ); | |
| if ( null != desc && 0 < desc.length ) | |
| groupDescription = desc[0]; | |
| else { response.sendError(530, "Missing GroupDescription parameter" ); return; } | |
| // -> execute the request | |
| CreateSecurityGroupResponse EC2response = EC2SoapServiceImpl.toCreateSecurityGroupResponse( ServiceProvider.getInstance().getEC2Engine().createSecurityGroup( groupName, groupDescription )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void deleteSecurityGroup( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| String groupName = null; | |
| String[] name = request.getParameterValues( "GroupName" ); | |
| if ( null != name && 0 < name.length ) | |
| groupName = name[0]; | |
| else { response.sendError(530, "Missing GroupName parameter" ); return; } | |
| // -> execute the request | |
| DeleteSecurityGroupResponse EC2response = EC2SoapServiceImpl.toDeleteSecurityGroupResponse( ServiceProvider.getInstance().getEC2Engine().deleteSecurityGroup( groupName )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void deleteSnapshot( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| String snapshotId = null; | |
| String[] snapSet = request.getParameterValues( "SnapshotId" ); | |
| if ( null != snapSet && 0 < snapSet.length ) | |
| snapshotId = snapSet[0]; | |
| else { response.sendError(530, "Missing SnapshotId parameter" ); return; } | |
| // -> execute the request | |
| DeleteSnapshotResponse EC2response = EC2SoapServiceImpl.toDeleteSnapshotResponse( ServiceProvider.getInstance().getEC2Engine().deleteSnapshot( snapshotId )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void createSnapshot( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| String volumeId = null; | |
| String[] volSet = request.getParameterValues( "VolumeId" ); | |
| if ( null != volSet && 0 < volSet.length ) | |
| volumeId = volSet[0]; | |
| else { response.sendError(530, "Missing VolumeId parameter" ); return; } | |
| // -> execute the request | |
| EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); | |
| CreateSnapshotResponse EC2response = EC2SoapServiceImpl.toCreateSnapshotResponse( engine.createSnapshot( volumeId ), engine); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void deregisterImage( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2Image image = new EC2Image(); | |
| String[] imageId = request.getParameterValues( "ImageId" ); | |
| if ( null != imageId && 0 < imageId.length ) | |
| image.setId( imageId[0] ); | |
| else { response.sendError(530, "Missing ImageId parameter" ); return; } | |
| // -> execute the request | |
| DeregisterImageResponse EC2response = EC2SoapServiceImpl.toDeregisterImageResponse( ServiceProvider.getInstance().getEC2Engine().deregisterImage( image )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void createImage( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2CreateImage EC2request = new EC2CreateImage(); | |
| String[] instanceId = request.getParameterValues( "InstanceId" ); | |
| if ( null != instanceId && 0 < instanceId.length ) | |
| EC2request.setInstanceId( instanceId[0] ); | |
| else { response.sendError(530, "Missing InstanceId parameter" ); return; } | |
| String[] name = request.getParameterValues( "Name" ); | |
| if ( null != name && 0 < name.length ) | |
| EC2request.setName( name[0] ); | |
| else { response.sendError(530, "Missing Name parameter" ); return; } | |
| String[] description = request.getParameterValues( "Description" ); | |
| if ( null != description && 0 < description.length ) | |
| EC2request.setDescription( description[0] ); | |
| // -> execute the request | |
| CreateImageResponse EC2response = EC2SoapServiceImpl.toCreateImageResponse( ServiceProvider.getInstance().getEC2Engine().createImage( EC2request )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void registerImage( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2RegisterImage EC2request = new EC2RegisterImage(); | |
| String[] location = request.getParameterValues( "ImageLocation" ); | |
| if ( null != location && 0 < location.length ) | |
| EC2request.setLocation( location[0] ); | |
| else { response.sendError(530, "Missing ImageLocation parameter" ); return; } | |
| String[] cloudRedfined = request.getParameterValues( "Architecture" ); | |
| if ( null != cloudRedfined && 0 < cloudRedfined.length ) | |
| EC2request.setArchitecture( cloudRedfined[0] ); | |
| else { response.sendError(530, "Missing Architecture parameter" ); return; } | |
| String[] name = request.getParameterValues( "Name" ); | |
| if ( null != name && 0 < name.length ) | |
| EC2request.setName( name[0] ); | |
| String[] description = request.getParameterValues( "Description" ); | |
| if ( null != description && 0 < description.length ) | |
| EC2request.setDescription( description[0] ); | |
| // -> execute the request | |
| RegisterImageResponse EC2response = EC2SoapServiceImpl.toRegisterImageResponse( ServiceProvider.getInstance().getEC2Engine().registerImage( EC2request )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void modifyImageAttribute( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2Image image = new EC2Image(); | |
| // -> its interesting to note that the SOAP API docs has description but the REST API docs do not | |
| String[] imageId = request.getParameterValues( "ImageId" ); | |
| if ( null != imageId && 0 < imageId.length ) | |
| image.setId( imageId[0] ); | |
| else { response.sendError(530, "Missing ImageId parameter" ); return; } | |
| String[] description = request.getParameterValues( "Description" ); | |
| if ( null != description && 0 < description.length ) | |
| image.setDescription( description[0] ); | |
| else { response.sendError(530, "Missing Description parameter" ); return; } | |
| // -> execute the request | |
| ModifyImageAttributeResponse EC2response = EC2SoapServiceImpl.toModifyImageAttributeResponse( ServiceProvider.getInstance().getEC2Engine().modifyImageAttribute( image )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void resetImageAttribute( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2Image image = new EC2Image(); | |
| String[] imageId = request.getParameterValues( "ImageId" ); | |
| if ( null != imageId && 0 < imageId.length ) | |
| image.setId( imageId[0] ); | |
| else { response.sendError(530, "Missing ImageId parameter" ); return; } | |
| // -> execute the request | |
| image.setDescription( "" ); | |
| ResetImageAttributeResponse EC2response = EC2SoapServiceImpl.toResetImageAttributeResponse( ServiceProvider.getInstance().getEC2Engine().modifyImageAttribute( image )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void runInstances( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2RunInstances EC2request = new EC2RunInstances(); | |
| // -> so in the Amazon docs for this REST call there is no userData even though there is in the SOAP docs | |
| String[] imageId = request.getParameterValues( "ImageId" ); | |
| if ( null != imageId && 0 < imageId.length ) | |
| EC2request.setTemplateId( imageId[0] ); | |
| else { response.sendError(530, "Missing ImageId parameter" ); return; } | |
| String[] minCount = request.getParameterValues( "MinCount" ); | |
| if ( null != minCount && 0 < minCount.length ) | |
| EC2request.setMinCount( Integer.parseInt( minCount[0] )); | |
| else { response.sendError(530, "Missing MinCount parameter" ); return; } | |
| String[] maxCount = request.getParameterValues( "MaxCount" ); | |
| if ( null != maxCount && 0 < maxCount.length ) | |
| EC2request.setMaxCount( Integer.parseInt( maxCount[0] )); | |
| else { response.sendError(530, "Missing MaxCount parameter" ); return; } | |
| String[] instanceType = request.getParameterValues( "InstanceType" ); | |
| if ( null != instanceType && 0 < instanceType.length ) | |
| EC2request.setInstanceType( instanceType[0] ); | |
| String[] zoneName = request.getParameterValues( "Placement.AvailabilityZone" ); | |
| if ( null != zoneName && 0 < zoneName.length ) | |
| EC2request.setZoneName( zoneName[0] ); | |
| String[] size = request.getParameterValues("size"); | |
| if (size != null) { | |
| EC2request.setSize(Integer.valueOf(size[0])); | |
| } | |
| String[] keyName = request.getParameterValues("KeyName"); | |
| if (keyName != null) { | |
| EC2request.setKeyName(keyName[0]); | |
| } | |
| // -> execute the request | |
| EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); | |
| RunInstancesResponse EC2response = EC2SoapServiceImpl.toRunInstancesResponse( engine.runInstances( EC2request ), engine); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void rebootInstances( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2RebootInstances EC2request = new EC2RebootInstances(); | |
| int count = 0; | |
| // -> load in all the "InstanceId.n" parameters if any | |
| Enumeration<?> names = request.getParameterNames(); | |
| while( names.hasMoreElements()) { | |
| String key = (String)names.nextElement(); | |
| if (key.startsWith("InstanceId")) { | |
| String[] value = request.getParameterValues( key ); | |
| if (null != value && 0 < value.length) { | |
| EC2request.addInstanceId( value[0] ); | |
| count++; | |
| } | |
| } | |
| } | |
| if (0 == count) { response.sendError(530, "Missing InstanceId parameter" ); return; } | |
| // -> execute the request | |
| RebootInstancesResponse EC2response = EC2SoapServiceImpl.toRebootInstancesResponse( ServiceProvider.getInstance().getEC2Engine().rebootInstances(EC2request)); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void startInstances( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2StartInstances EC2request = new EC2StartInstances(); | |
| int count = 0; | |
| // -> load in all the "InstanceId.n" parameters if any | |
| Enumeration<?> names = request.getParameterNames(); | |
| while( names.hasMoreElements()) { | |
| String key = (String)names.nextElement(); | |
| if (key.startsWith("InstanceId")) { | |
| String[] value = request.getParameterValues( key ); | |
| if (null != value && 0 < value.length) { | |
| EC2request.addInstanceId( value[0] ); | |
| count++; | |
| } | |
| } | |
| } | |
| if (0 == count) { response.sendError(530, "Missing InstanceId parameter" ); return; } | |
| // -> execute the request | |
| StartInstancesResponse EC2response = EC2SoapServiceImpl.toStartInstancesResponse( ServiceProvider.getInstance().getEC2Engine().startInstances(EC2request)); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void stopInstances( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2StopInstances EC2request = new EC2StopInstances(); | |
| int count = 0; | |
| // -> load in all the "InstanceId.n" parameters if any | |
| Enumeration<?> names = request.getParameterNames(); | |
| while( names.hasMoreElements()) { | |
| String key = (String)names.nextElement(); | |
| if (key.startsWith("InstanceId")) { | |
| String[] value = request.getParameterValues( key ); | |
| if (null != value && 0 < value.length) { | |
| EC2request.addInstanceId( value[0] ); | |
| count++; | |
| } | |
| } | |
| } | |
| if (0 == count) { response.sendError(530, "Missing InstanceId parameter" ); return; } | |
| // -> execute the request | |
| StopInstancesResponse EC2response = EC2SoapServiceImpl.toStopInstancesResponse( ServiceProvider.getInstance().getEC2Engine().stopInstances( EC2request )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void terminateInstances( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2StopInstances EC2request = new EC2StopInstances(); | |
| int count = 0; | |
| // -> load in all the "InstanceId.n" parameters if any | |
| Enumeration<?> names = request.getParameterNames(); | |
| while( names.hasMoreElements()) { | |
| String key = (String)names.nextElement(); | |
| if (key.startsWith("InstanceId")) { | |
| String[] value = request.getParameterValues( key ); | |
| if (null != value && 0 < value.length) { | |
| EC2request.addInstanceId( value[0] ); | |
| count++; | |
| } | |
| } | |
| } | |
| if (0 == count) { response.sendError(530, "Missing InstanceId parameter" ); return; } | |
| // -> execute the request | |
| EC2request.setDestroyInstances( true ); | |
| TerminateInstancesResponse EC2response = EC2SoapServiceImpl.toTermInstancesResponse( ServiceProvider.getInstance().getEC2Engine().stopInstances( EC2request )); | |
| serializeResponse(response, EC2response); | |
| } | |
| /** | |
| * We are reusing the SOAP code to process this request. We then use Axiom to serialize the | |
| * resulting EC2 Amazon object into XML to return to the client. | |
| */ | |
| private void describeAvailabilityZones( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2DescribeAvailabilityZones EC2request = new EC2DescribeAvailabilityZones(); | |
| // -> load in all the "ZoneName.n" parameters if any | |
| Enumeration<?> names = request.getParameterNames(); | |
| while( names.hasMoreElements()) { | |
| String key = (String)names.nextElement(); | |
| if (key.startsWith("ZoneName")) { | |
| String[] value = request.getParameterValues( key ); | |
| if (null != value && 0 < value.length) EC2request.addZone( value[0] ); | |
| } | |
| } | |
| // -> execute the request | |
| DescribeAvailabilityZonesResponse EC2response = EC2SoapServiceImpl.toDescribeAvailabilityZonesResponse( ServiceProvider.getInstance().getEC2Engine().handleRequest( EC2request )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void describeImages( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2DescribeImages EC2request = new EC2DescribeImages(); | |
| // -> load in all the "ImageId.n" parameters if any, and ignore all other parameters | |
| Enumeration<?> names = request.getParameterNames(); | |
| while( names.hasMoreElements()) { | |
| String key = (String)names.nextElement(); | |
| if (key.startsWith("ImageId")) { | |
| String[] value = request.getParameterValues( key ); | |
| if (null != value && 0 < value.length) EC2request.addImageSet( value[0] ); | |
| } | |
| } | |
| // -> execute the request | |
| EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); | |
| DescribeImagesResponse EC2response = EC2SoapServiceImpl.toDescribeImagesResponse( engine.describeImages( EC2request )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void describeImageAttribute( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2DescribeImages EC2request = new EC2DescribeImages(); | |
| // -> only works for queries about descriptions | |
| String[] descriptions = request.getParameterValues( "Description" ); | |
| if ( null != descriptions && 0 < descriptions.length ) { | |
| String[] value = request.getParameterValues( "ImageId" ); | |
| EC2request.addImageSet( value[0] ); | |
| } | |
| else { | |
| response.sendError(501, "Unsupported - only description supported" ); | |
| return; | |
| } | |
| // -> execute the request | |
| DescribeImageAttributeResponse EC2response = EC2SoapServiceImpl.toDescribeImageAttributeResponse( ServiceProvider.getInstance().getEC2Engine().describeImages( EC2request )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void describeInstances( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException | |
| { | |
| EC2DescribeInstances EC2request = new EC2DescribeInstances(); | |
| // -> load in all the "InstanceId.n" parameters if any | |
| Enumeration<?> names = request.getParameterNames(); | |
| while( names.hasMoreElements()) | |
| { | |
| String key = (String)names.nextElement(); | |
| if (key.startsWith("InstanceId")) { | |
| String[] value = request.getParameterValues( key ); | |
| if (null != value && 0 < value.length) EC2request.addInstanceId( value[0] ); | |
| } | |
| } | |
| // -> are there any filters with this request? | |
| EC2Filter[] filterSet = extractFilters( request ); | |
| if (null != filterSet) | |
| { | |
| EC2InstanceFilterSet ifs = new EC2InstanceFilterSet(); | |
| for( int i=0; i < filterSet.length; i++ ) ifs.addFilter( filterSet[i] ); | |
| EC2request.setFilterSet( ifs ); | |
| } | |
| // -> execute the request | |
| EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); | |
| DescribeInstancesResponse EC2response = EC2SoapServiceImpl.toDescribeInstancesResponse( engine.describeInstances( EC2request ), engine); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void describeAddresses( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2DescribeAddresses ec2Request = new EC2DescribeAddresses(); | |
| // -> load in all the "PublicIp.n" parameters if any | |
| Enumeration<?> names = request.getParameterNames(); | |
| while( names.hasMoreElements()) { | |
| String key = (String)names.nextElement(); | |
| if (key.startsWith("PublicIp")) { | |
| String[] value = request.getParameterValues( key ); | |
| if (null != value && 0 < value.length) ec2Request.addPublicIp( value[0] ); | |
| } | |
| } | |
| // -> execute the request | |
| EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); | |
| serializeResponse(response, EC2SoapServiceImpl.toDescribeAddressesResponse( engine.describeAddresses( ec2Request))); | |
| } | |
| private void allocateAddress( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); | |
| AllocateAddressResponse ec2Response = EC2SoapServiceImpl.toAllocateAddressResponse( engine.allocateAddress()); | |
| serializeResponse(response, ec2Response); | |
| } | |
| private void releaseAddress( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); | |
| String publicIp = request.getParameter( "PublicIp" ); | |
| if (publicIp == null) { | |
| response.sendError(530, "Missing PublicIp parameter"); | |
| return; | |
| } | |
| EC2ReleaseAddress ec2Request = new EC2ReleaseAddress(); | |
| if (ec2Request != null) { | |
| ec2Request.setPublicIp(publicIp); | |
| } | |
| ReleaseAddressResponse EC2Response = EC2SoapServiceImpl.toReleaseAddressResponse( engine.releaseAddress( ec2Request )); | |
| serializeResponse(response, EC2Response); | |
| } | |
| private void associateAddress( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); | |
| String publicIp = request.getParameter( "PublicIp" ); | |
| if (null == publicIp) { | |
| response.sendError(530, "Missing PublicIp parameter" ); | |
| return; | |
| } | |
| String instanceId = request.getParameter( "InstanceId" ); | |
| if (null == instanceId) { | |
| response.sendError(530, "Missing InstanceId parameter" ); | |
| return; | |
| } | |
| EC2AssociateAddress ec2Request = new EC2AssociateAddress(); | |
| if (ec2Request != null) { | |
| ec2Request.setInstanceId(instanceId); | |
| ec2Request.setPublicIp(publicIp); | |
| } | |
| AssociateAddressResponse ec2Response = EC2SoapServiceImpl.toAssociateAddressResponse( engine.associateAddress( ec2Request )); | |
| serializeResponse(response, ec2Response); | |
| } | |
| private void disassociateAddress( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); | |
| String publicIp = request.getParameter( "PublicIp" ); | |
| if (null == publicIp) { | |
| response.sendError(530, "Missing PublicIp parameter" ); | |
| return; | |
| } | |
| EC2DisassociateAddress ec2Request = new EC2DisassociateAddress(); | |
| if (ec2Request != null) { | |
| ec2Request.setPublicIp(publicIp); | |
| } | |
| DisassociateAddressResponse ec2Response = EC2SoapServiceImpl.toDisassociateAddressResponse( engine.disassociateAddress( ec2Request ) ); | |
| serializeResponse(response, ec2Response); | |
| } | |
| private void describeSecurityGroups( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException | |
| { | |
| EC2DescribeSecurityGroups EC2request = new EC2DescribeSecurityGroups(); | |
| // -> load in all the "GroupName.n" parameters if any | |
| Enumeration<?> names = request.getParameterNames(); | |
| while( names.hasMoreElements()) { | |
| String key = (String)names.nextElement(); | |
| if (key.startsWith("GroupName")) { | |
| String[] value = request.getParameterValues( key ); | |
| if (null != value && 0 < value.length) EC2request.addGroupName( value[0] ); | |
| } | |
| } | |
| // -> are there any filters with this request? | |
| EC2Filter[] filterSet = extractFilters( request ); | |
| if (null != filterSet) { | |
| EC2GroupFilterSet gfs = new EC2GroupFilterSet(); | |
| for (EC2Filter filter : filterSet) gfs.addFilter( filter ); | |
| EC2request.setFilterSet( gfs ); | |
| } | |
| // -> execute the request | |
| EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); | |
| DescribeSecurityGroupsResponse EC2response = EC2SoapServiceImpl.toDescribeSecurityGroupsResponse( engine.describeSecurityGroups( EC2request )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void describeInstanceAttribute( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2DescribeInstances EC2request = new EC2DescribeInstances(); | |
| String instanceType = null; | |
| // -> we are only handling queries about the "Attribute=instanceType" | |
| Enumeration<?> names = request.getParameterNames(); | |
| while( names.hasMoreElements()) { | |
| String key = (String)names.nextElement(); | |
| if (key.startsWith("Attribute")) { | |
| String[] value = request.getParameterValues( key ); | |
| if (null != value && 0 < value.length && value[0].equalsIgnoreCase( "instanceType" )) { | |
| instanceType = value[0]; | |
| break; | |
| } | |
| } | |
| } | |
| if ( null != instanceType ) { | |
| String[] value = request.getParameterValues( "InstanceId" ); | |
| EC2request.addInstanceId( value[0] ); | |
| } | |
| else { | |
| response.sendError(501, "Unsupported - only instanceType supported" ); | |
| return; | |
| } | |
| // -> execute the request | |
| DescribeInstanceAttributeResponse EC2response = EC2SoapServiceImpl.toDescribeInstanceAttributeResponse( ServiceProvider.getInstance().getEC2Engine().describeInstances(EC2request)); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void describeSnapshots( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException | |
| { | |
| EC2DescribeSnapshots EC2request = new EC2DescribeSnapshots(); | |
| // -> load in all the "SnapshotId.n" parameters if any, and ignore any other parameters | |
| Enumeration<?> names = request.getParameterNames(); | |
| while( names.hasMoreElements()) | |
| { | |
| String key = (String)names.nextElement(); | |
| if (key.startsWith("SnapshotId")) { | |
| String[] value = request.getParameterValues( key ); | |
| if (null != value && 0 < value.length) EC2request.addSnapshotId( value[0] ); | |
| } | |
| } | |
| // -> are there any filters with this request? | |
| EC2Filter[] filterSet = extractFilters( request ); | |
| if (null != filterSet) | |
| { | |
| EC2SnapshotFilterSet sfs = new EC2SnapshotFilterSet(); | |
| for( int i=0; i < filterSet.length; i++ ) sfs.addFilter( filterSet[i] ); | |
| EC2request.setFilterSet( sfs ); | |
| } | |
| // -> execute the request | |
| EC2Engine engine = ServiceProvider.getInstance().getEC2Engine(); | |
| DescribeSnapshotsResponse EC2response = EC2SoapServiceImpl.toDescribeSnapshotsResponse( engine.handleRequest( EC2request )); | |
| serializeResponse(response, EC2response); | |
| } | |
| private void describeVolumes( HttpServletRequest request, HttpServletResponse response ) | |
| throws ADBException, XMLStreamException, IOException | |
| { | |
| EC2DescribeVolumes EC2request = new EC2DescribeVolumes(); | |
| // -> load in all the "VolumeId.n" parameters if any | |
| Enumeration<?> names = request.getParameterNames(); | |
| while( names.hasMoreElements()) | |
| { | |
| String key = (String)names.nextElement(); | |
| if (key.startsWith("VolumeId")) | |
| { | |
| String[] value = request.getParameterValues( key ); | |
| if (null != value && 0 < value.length) EC2request.addVolumeId( value[0] ); | |
| } | |
| } | |
| // -> are there any filters with this request? | |
| EC2Filter[] filterSet = extractFilters( request ); | |
| if (null != filterSet) | |
| { | |
| EC2VolumeFilterSet vfs = new EC2VolumeFilterSet(); | |
| for( int i=0; i < filterSet.length; i++ ) vfs.addFilter( filterSet[i] ); | |
| EC2request.setFilterSet( vfs ); | |
| } | |
| // -> execute the request | |
| DescribeVolumesResponse EC2response = EC2SoapServiceImpl.toDescribeVolumesResponse( ServiceProvider.getInstance().getEC2Engine().handleRequest( EC2request )); | |
| serializeResponse(response, EC2response); | |
| } | |
| /** | |
| * Example of how the filters are defined in a REST request: | |
| * https://<server>/?Action=DescribeVolumes | |
| * &Filter.1.Name=attachment.instance-id | |
| * &Filter.1.Value.1=i-1a2b3c4d | |
| * &Filter.2.Name=attachment.delete-on-termination | |
| * &Filter.2.Value.1=true | |
| * | |
| * @param request | |
| * @return List<EC2Filter> | |
| */ | |
| private EC2Filter[] extractFilters( HttpServletRequest request ) | |
| { | |
| String filterName = null; | |
| String value = null; | |
| EC2Filter nextFilter = null; | |
| boolean timeFilter = false; | |
| int filterCount = 1; | |
| int valueCount = 1; | |
| List<EC2Filter> filterSet = new ArrayList<EC2Filter>(); | |
| do | |
| { filterName = request.getParameter( "Filter." + filterCount + ".Name" ); | |
| if (null != filterName) | |
| { | |
| nextFilter = new EC2Filter(); | |
| nextFilter.setName( filterName ); | |
| timeFilter = (filterName.equalsIgnoreCase( "attachment.attach-time" ) || filterName.equalsIgnoreCase( "create-time" )); | |
| valueCount = 1; | |
| do | |
| { | |
| value = request.getParameter( "Filter." + filterCount + ".Value." + valueCount ); | |
| if (null != value) | |
| { | |
| // -> time values are not encoded as regexes | |
| if ( timeFilter ) | |
| nextFilter.addValue( value ); | |
| else nextFilter.addValueEncoded( value ); | |
| valueCount++; | |
| } | |
| } | |
| while( null != value ); | |
| filterSet.add( nextFilter ); | |
| filterCount++; | |
| } | |
| } | |
| while( null != filterName ); | |
| if ( 1 == filterCount ) | |
| return null; | |
| else return filterSet.toArray(new EC2Filter[0]); | |
| } | |
| private void describeKeyPairs(HttpServletRequest request, HttpServletResponse response) | |
| throws ADBException, XMLStreamException, IOException { | |
| EC2DescribeKeyPairs ec2Request = new EC2DescribeKeyPairs(); | |
| String[] keyNames = request.getParameterValues( "KeyName" ); | |
| if (keyNames != null) { | |
| for (String keyName : keyNames) { | |
| ec2Request.addKeyName(keyName); | |
| } | |
| } | |
| EC2Filter[] filterSet = extractFilters( request ); | |
| if (null != filterSet){ | |
| EC2KeyPairFilterSet vfs = new EC2KeyPairFilterSet(); | |
| for (EC2Filter filter : filterSet) { | |
| vfs.addFilter(filter); | |
| } | |
| ec2Request.setKeyFilterSet(vfs); | |
| } | |
| DescribeKeyPairsResponse EC2Response = EC2SoapServiceImpl.toDescribeKeyPairs( | |
| ServiceProvider.getInstance().getEC2Engine().describeKeyPairs( ec2Request )); | |
| serializeResponse(response, EC2Response); | |
| } | |
| private void importKeyPair(HttpServletRequest request, HttpServletResponse response) | |
| throws ADBException, XMLStreamException, IOException { | |
| String keyName = request.getParameter("KeyName"); | |
| String publicKeyMaterial = request.getParameter("PublicKeyMaterial"); | |
| if (keyName==null && publicKeyMaterial==null) { | |
| response.sendError(530, "Missing parameter"); | |
| return; | |
| } | |
| if (!publicKeyMaterial.contains(" ")) | |
| publicKeyMaterial = new String(Base64.decodeBase64(publicKeyMaterial.getBytes())); | |
| EC2ImportKeyPair ec2Request = new EC2ImportKeyPair(); | |
| if (ec2Request != null) { | |
| ec2Request.setKeyName(request.getParameter("KeyName")); | |
| ec2Request.setPublicKeyMaterial(request.getParameter("PublicKeyMaterial")); | |
| } | |
| ImportKeyPairResponse EC2Response = EC2SoapServiceImpl.toImportKeyPair( | |
| ServiceProvider.getInstance().getEC2Engine().importKeyPair( ec2Request )); | |
| serializeResponse(response, EC2Response); | |
| } | |
| private void createKeyPair(HttpServletRequest request, HttpServletResponse response) | |
| throws ADBException, XMLStreamException, IOException { | |
| String keyName = request.getParameter("KeyName"); | |
| if (keyName==null) { | |
| response.sendError(530, "Missing KeyName parameter"); | |
| return; | |
| } | |
| EC2CreateKeyPair ec2Request = new EC2CreateKeyPair(); | |
| if (ec2Request != null) { | |
| ec2Request.setKeyName(keyName); | |
| } | |
| CreateKeyPairResponse EC2Response = EC2SoapServiceImpl.toCreateKeyPair( | |
| ServiceProvider.getInstance().getEC2Engine().createKeyPair(ec2Request)); | |
| serializeResponse(response, EC2Response); | |
| } | |
| private void deleteKeyPair(HttpServletRequest request, HttpServletResponse response) | |
| throws ADBException, XMLStreamException, IOException { | |
| String keyName = request.getParameter("KeyName"); | |
| if (keyName==null) { | |
| response.sendError(530, "Missing KeyName parameter"); | |
| return; | |
| } | |
| EC2DeleteKeyPair ec2Request = new EC2DeleteKeyPair(); | |
| ec2Request.setKeyName(keyName); | |
| DeleteKeyPairResponse EC2Response = EC2SoapServiceImpl.toDeleteKeyPair( | |
| ServiceProvider.getInstance().getEC2Engine().deleteKeyPair(ec2Request)); | |
| serializeResponse(response, EC2Response); | |
| } | |
| private void getPasswordData(HttpServletRequest request, HttpServletResponse response) | |
| throws ADBException, XMLStreamException, IOException { | |
| String instanceId = request.getParameter("InstanceId"); | |
| if (instanceId==null) { | |
| response.sendError(530, "Missing InstanceId parameter"); | |
| return; | |
| } | |
| GetPasswordDataResponse EC2Response = EC2SoapServiceImpl.toGetPasswordData( | |
| ServiceProvider.getInstance().getEC2Engine().getPasswordData(instanceId)); | |
| serializeResponse(response, EC2Response); | |
| } | |
| /** | |
| * This function implements the EC2 REST authentication algorithm. It uses the given | |
| * "AWSAccessKeyId" parameter to look up the Cloud.com account holder's secret key which is | |
| * used as input to the signature calculation. In addition, it tests the given "Expires" | |
| * parameter to see if the signature has expired and if so the request fails. | |
| */ | |
| private boolean authenticateRequest( HttpServletRequest request, HttpServletResponse response ) | |
| throws SignatureException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException, ParseException | |
| { | |
| String cloudSecretKey = null; | |
| String cloudAccessKey = null; | |
| String signature = null; | |
| String sigMethod = null; | |
| // [A] Basic parameters required for an authenticated rest request | |
| // -> note that the Servlet engine will un-URL encode all parameters we extract via "getParameterValues()" calls | |
| String[] awsAccess = request.getParameterValues( "AWSAccessKeyId" ); | |
| if ( null != awsAccess && 0 < awsAccess.length ) | |
| cloudAccessKey = awsAccess[0]; | |
| else { response.sendError(530, "Missing AWSAccessKeyId parameter" ); return false; } | |
| String[] clientSig = request.getParameterValues( "Signature" ); | |
| if ( null != clientSig && 0 < clientSig.length ) | |
| signature = clientSig[0]; | |
| else { response.sendError(530, "Missing Signature parameter" ); return false; } | |
| String[] method = request.getParameterValues( "SignatureMethod" ); | |
| if ( null != method && 0 < method.length ) | |
| { | |
| sigMethod = method[0]; | |
| if (!sigMethod.equals( "HmacSHA256" ) && !sigMethod.equals( "HmacSHA1" )) { | |
| response.sendError(531, "Unsupported SignatureMethod value: " + sigMethod + " expecting: HmacSHA256 or HmacSHA1" ); | |
| return false; | |
| } | |
| } | |
| else { response.sendError(530, "Missing SignatureMethod parameter" ); return false; } | |
| String[] version = request.getParameterValues( "Version" ); | |
| if ( null != version && 0 < version.length ) | |
| { | |
| if (!version[0].equals( wsdlVersion )) { | |
| response.sendError(531, "Unsupported Version value: " + version[0] + " expecting: " + wsdlVersion ); | |
| return false; | |
| } | |
| } | |
| else { response.sendError(530, "Missing Version parameter" ); return false; } | |
| String[] sigVersion = request.getParameterValues( "SignatureVersion" ); | |
| if ( null != sigVersion && 0 < sigVersion.length ) | |
| { | |
| if (!sigVersion[0].equals( "2" )) { | |
| response.sendError(531, "Unsupported SignatureVersion value: " + sigVersion[0] + " expecting: 2" ); | |
| return false; | |
| } | |
| } | |
| else { response.sendError(530, "Missing SignatureVersion parameter" ); return false; } | |
| // -> can have only one but not both { Expires | Timestamp } headers | |
| String[] expires = request.getParameterValues( "Expires" ); | |
| if ( null != expires && 0 < expires.length ) | |
| { | |
| // -> contains the date and time at which the signature included in the request EXPIRES | |
| if (hasSignatureExpired( expires[0] )) { | |
| response.sendError(531, "Expires parameter indicates signature has expired: " + expires[0] ); | |
| return false; | |
| } | |
| } | |
| else | |
| { // -> contains the date and time at which the request is SIGNED | |
| String[] time = request.getParameterValues( "Timestamp" ); | |
| if ( null == time || 0 == time.length ) { | |
| response.sendError(530, "Missing Timestamp and Expires parameter, one is required" ); | |
| return false; | |
| } | |
| } | |
| // [B] Use the cloudAccessKey to get the users secret key in the db | |
| UserCredentialsVO cloudKeys = ucDao.getByAccessKey( cloudAccessKey ); | |
| if ( null == cloudKeys ) | |
| { | |
| logger.debug( cloudAccessKey + " is not defined in the EC2 service - call SetUserKeys" ); | |
| response.sendError(404, cloudAccessKey + " is not defined in the EC2 service - call SetUserKeys" ); | |
| return false; | |
| } | |
| else cloudSecretKey = cloudKeys.getSecretKey(); | |
| // [C] Verify the signature | |
| // -> getting the query-string in this way maintains its URL encoding | |
| EC2RestAuth restAuth = new EC2RestAuth(); | |
| restAuth.setHostHeader( request.getHeader( "Host" )); | |
| String requestUri = request.getRequestURI(); | |
| // If forwarded from another basepath: | |
| String forwardedPath = (String) request.getAttribute("javax.servlet.forward.request_uri"); | |
| if(forwardedPath!=null){ | |
| requestUri=forwardedPath; | |
| } | |
| restAuth.setHTTPRequestURI( requestUri); | |
| String queryString = request.getQueryString(); | |
| // getQueryString returns null (does it ever NOT return null for these), | |
| // we need to construct queryString to avoid changing the auth code... | |
| if (queryString == null) { | |
| // construct our idea of a queryString with parameters! | |
| Enumeration<?> params = request.getParameterNames(); | |
| if (params != null) { | |
| while(params.hasMoreElements()) { | |
| String paramName = (String) params.nextElement(); | |
| // exclude the signature string obviously. ;) | |
| if (paramName.equalsIgnoreCase("Signature")) continue; | |
| if (queryString == null) | |
| queryString = paramName + "=" + request.getParameter(paramName); | |
| else | |
| queryString = queryString + "&" + paramName + "=" + URLEncoder.encode(request.getParameter(paramName), "UTF-8"); | |
| } | |
| } | |
| } | |
| restAuth.setQueryString(queryString); | |
| if ( restAuth.verifySignature( request.getMethod(), cloudSecretKey, signature, sigMethod )) { | |
| UserContext.current().initContext( cloudAccessKey, cloudSecretKey, cloudAccessKey, "REST request", null ); | |
| return true; | |
| } | |
| else throw new PermissionDeniedException("Invalid signature"); | |
| } | |
| /** | |
| * We check this to reduce replay attacks. | |
| * | |
| * @param timeStamp | |
| * @return true - if the request is not longer valid, false otherwise | |
| * @throws ParseException | |
| */ | |
| private boolean hasSignatureExpired( String timeStamp ) { | |
| Calendar cal = EC2RestAuth.parseDateString( timeStamp ); | |
| if (null == cal) return false; | |
| Date expiredTime = cal.getTime(); | |
| Date today = new Date(); // -> gets set to time of creation | |
| if ( 0 >= expiredTime.compareTo( today )) { | |
| logger.debug( "timestamp given: [" + timeStamp + "], now: [" + today.toString() + "]" ); | |
| return true; | |
| } | |
| else return false; | |
| } | |
| private static void endResponse(HttpServletResponse response, String content) { | |
| try { | |
| byte[] data = content.getBytes(); | |
| response.setContentLength(data.length); | |
| OutputStream os = response.getOutputStream(); | |
| os.write(data); | |
| os.close(); | |
| } catch(Throwable e) { | |
| logger.error("Unexpected exception " + e.getMessage(), e); | |
| } | |
| } | |
| private void logRequest(HttpServletRequest request) { | |
| if(logger.isInfoEnabled()) { | |
| logger.info("EC2 Request method: " + request.getMethod()); | |
| logger.info("Request contextPath: " + request.getContextPath()); | |
| logger.info("Request pathInfo: " + request.getPathInfo()); | |
| logger.info("Request pathTranslated: " + request.getPathTranslated()); | |
| logger.info("Request queryString: " + request.getQueryString()); | |
| logger.info("Request requestURI: " + request.getRequestURI()); | |
| logger.info("Request requestURL: " + request.getRequestURL()); | |
| logger.info("Request servletPath: " + request.getServletPath()); | |
| Enumeration<?> headers = request.getHeaderNames(); | |
| if(headers != null) { | |
| while(headers.hasMoreElements()) { | |
| Object headerName = headers.nextElement(); | |
| logger.info("Request header " + headerName + ":" + request.getHeader((String)headerName)); | |
| } | |
| } | |
| Enumeration<?> params = request.getParameterNames(); | |
| if(params != null) { | |
| while(params.hasMoreElements()) { | |
| Object paramName = params.nextElement(); | |
| logger.info("Request parameter " + paramName + ":" + | |
| request.getParameter((String)paramName)); | |
| } | |
| } | |
| } | |
| } | |
| /** | |
| * Send out an error response according to Amazon convention. | |
| */ | |
| private void faultResponse(HttpServletResponse response, String errorCode, String errorMessage) { | |
| try { | |
| OutputStreamWriter out = new OutputStreamWriter(response.getOutputStream()); | |
| response.setContentType("text/xml; charset=UTF-8"); | |
| out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); | |
| out.write("<Response><Errors><Error><Code>"); | |
| out.write(errorCode); | |
| out.write("</Code><Message>"); | |
| out.write(errorMessage); | |
| out.write("</Message></Error></Errors><RequestID>"); | |
| out.write(UUID.randomUUID().toString()); | |
| out.write("</RequestID></Response>"); | |
| out.flush(); | |
| out.close(); | |
| } catch (IOException e) { | |
| logger.error("Unexpected exception " + e.getMessage(), e); | |
| } | |
| } | |
| /** | |
| * Serialize Axis beans to XML output. | |
| */ | |
| private void serializeResponse(HttpServletResponse response, ADBBean EC2Response) | |
| throws ADBException, XMLStreamException, IOException { | |
| OutputStream os = response.getOutputStream(); | |
| response.setStatus(200); | |
| response.setContentType("text/xml; charset=UTF-8"); | |
| XMLStreamWriter xmlWriter = xmlOutFactory.createXMLStreamWriter( os ); | |
| MTOMAwareXMLSerializer MTOMWriter = new MTOMAwareXMLSerializer( xmlWriter ); | |
| MTOMWriter.setDefaultNamespace("http://ec2.amazonaws.com/doc/" + wsdlVersion + "/"); | |
| EC2Response.serialize( null, factory, MTOMWriter ); | |
| xmlWriter.flush(); | |
| xmlWriter.close(); | |
| os.close(); | |
| } | |
| } |