diff --git a/awsapi/src/com/cloud/bridge/persist/CloudStackDao.java b/awsapi/src/com/cloud/bridge/persist/CloudStackDao.java new file mode 100644 index 00000000000..56f59369501 --- /dev/null +++ b/awsapi/src/com/cloud/bridge/persist/CloudStackDao.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved. + * + * Licensed 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.persist; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Properties; + +import org.apache.log4j.Logger; + +import com.cloud.bridge.util.ConfigurationHelper; + + +public class CloudStackDao { + public static final Logger logger = Logger.getLogger(CloudStackDao.class); + + private Connection conn = null; + private String dbName = null; + private String dbHost = null; + private String dbUser = null; + private String dbPassword = null; + private String dbPort = null; + + public CloudStackDao() { + File propertiesFile = ConfigurationHelper.findConfigurationFile("db.properties"); + Properties EC2Prop = null; + + if (null != propertiesFile) { + 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); + } + dbHost = EC2Prop.getProperty( "db.cloud.host" ); + dbName = EC2Prop.getProperty( "db.cloud.name" ); + dbUser = EC2Prop.getProperty( "db.cloud.username" ); + dbPassword = EC2Prop.getProperty( "db.cloud.password" ); + dbPort = EC2Prop.getProperty( "db.cloud.port" ); + } + } + + + public String getConfigValue( String configName ){ + String value = null; + try { + openConnection(); + PreparedStatement statement = conn.prepareStatement ( "SELECT value FROM `cloud`.`configuration` where name = ?" ); + statement.setString( 1, configName ); + statement.executeQuery(); + ResultSet rs = statement.getResultSet (); + if (rs.next()) { + value = rs.getString(1); + } + + }catch (Exception e) { + logger.warn("Failed to access CloudStack DB, got error: ", e); + } finally { + try{ + closeConnection(); + }catch(SQLException e){ + + } + } + return value; + } + + private void openConnection() + throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException { + if (null == conn) { + Class.forName( "com.mysql.jdbc.Driver" ).newInstance(); + conn = DriverManager.getConnection( "jdbc:mysql://" + dbHost + ":" + dbPort + "/" + dbName, dbUser, dbPassword ); + } + } + + private void closeConnection() throws SQLException { + if (null != conn) conn.close(); + conn = null; + } + +} diff --git a/awsapi/src/com/cloud/bridge/service/EC2MainServlet.java b/awsapi/src/com/cloud/bridge/service/EC2MainServlet.java index f65f3e4930d..c72054842d1 100644 --- a/awsapi/src/com/cloud/bridge/service/EC2MainServlet.java +++ b/awsapi/src/com/cloud/bridge/service/EC2MainServlet.java @@ -9,6 +9,7 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import com.cloud.bridge.persist.CloudStackDao; import com.cloud.bridge.persist.dao.UserCredentialsDao; import com.cloud.bridge.util.ConfigurationHelper; @@ -18,6 +19,8 @@ public class EC2MainServlet extends HttpServlet{ public static final String EC2_REST_SERVLET_PATH="/rest/AmazonEC2/"; public static final String EC2_SOAP_SERVLET_PATH="/services/AmazonEC2/"; + public static final String ENABLE_EC2_API="enable.ec2.api"; + private static boolean isEC2APIEnabled = false; /** * We build the path to where the keystore holding the WS-Security X509 certificates @@ -26,6 +29,13 @@ public class EC2MainServlet extends HttpServlet{ public void init( ServletConfig config ) throws ServletException { ConfigurationHelper.preConfigureConfigPathFromServletContext(config.getServletContext()); UserCredentialsDao.preCheckTableExistence(); + // check if API is enabled + + CloudStackDao csDao = new CloudStackDao(); + String value = csDao.getConfigValue(ENABLE_EC2_API); + if(value != null){ + isEC2APIEnabled = Boolean.valueOf(value); + } } protected void doGet(HttpServletRequest req, HttpServletResponse resp) { @@ -37,8 +47,13 @@ public class EC2MainServlet extends HttpServlet{ } protected void doGetOrPost(HttpServletRequest request, HttpServletResponse response) { - String action = request.getParameter( "Action" ); - if(action!=null){ + String action = request.getParameter( "Action" ); + + if(!isEC2APIEnabled){ + throw new RuntimeException("EC2 API is disabled."); + } + + if(action != null){ //We presume it's a Query/Rest call try { RequestDispatcher dispatcher = request.getRequestDispatcher(EC2_REST_SERVLET_PATH); diff --git a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java index d62371477b0..9d899e8b5d3 100644 --- a/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java +++ b/awsapi/src/com/cloud/bridge/service/core/ec2/EC2Engine.java @@ -55,6 +55,7 @@ import com.cloud.stack.models.CloudStackPasswordData; import com.cloud.stack.models.CloudStackResourceLimit; import com.cloud.stack.models.CloudStackSecurityGroup; import com.cloud.stack.models.CloudStackSecurityGroupIngress; +import com.cloud.stack.models.CloudStackServiceOffering; import com.cloud.stack.models.CloudStackSnapshot; import com.cloud.stack.models.CloudStackTemplate; import com.cloud.stack.models.CloudStackUser; @@ -1285,9 +1286,17 @@ public class EC2Engine { else createInstances = request.getMaxCount(); - // the mapping stuff - OfferingBundle offer = instanceTypeToOfferBundle( request.getInstanceType()); - + //find CS service Offering ID + String instanceType = "m1.small"; + if(request.getInstanceType() != null){ + instanceType = request.getInstanceType(); + } + CloudStackServiceOffering svcOffering = getCSServiceOfferingId(instanceType); + if(svcOffering == null){ + logger.info("No ServiceOffering found to be defined by name, please contact the administrator "+instanceType ); + throw new EC2ServiceException(ClientError.Unsupported, "instanceType: [" + instanceType + "] not found!"); + } + // zone stuff String zoneId = toZoneId(request.getZoneName(), null); @@ -1304,7 +1313,7 @@ public class EC2Engine { // now actually deploy the vms for( int i=0; i < createInstances; i++ ) { - CloudStackUserVm resp = getApi().deployVirtualMachine(offer.getServiceOfferingId(), + CloudStackUserVm resp = getApi().deployVirtualMachine(svcOffering.getId(), request.getTemplateId(), zoneId, null, null, null, null, null, null, null, request.getKeyName(), null, (network != null ? network.getId() : null), null, null, request.getSize().longValue(), request.getUserData()); @@ -1324,7 +1333,7 @@ public class EC2Engine { vm.setAccountName(resp.getAccountName()); vm.setDomainId(resp.getDomainId()); vm.setHypervisor(resp.getHypervisor()); - vm.setServiceOffering( serviceOfferingIdToInstanceType( offer.getServiceOfferingId())); + vm.setServiceOffering( svcOffering.getName()); instances.addInstance(vm); countCreated++; } @@ -1576,6 +1585,36 @@ public class EC2Engine { return found; } + + /** + * + */ + + private CloudStackServiceOffering getCSServiceOfferingId(String instanceType) throws Exception{ + try { + if (null == instanceType) instanceType = "m1.small"; + + List svcOfferings = getApi().listServiceOfferings(null, null, null, null, null, + null, null); + + if(svcOfferings == null || svcOfferings.isEmpty()){ + logger.debug("No ServiceOffering found to be defined by name: "+instanceType ); + return null; + } + + for(CloudStackServiceOffering offering : svcOfferings){ + if(instanceType.equalsIgnoreCase(offering.getName())){ + return offering; + } + } + + return null; + } catch(Exception e) { + logger.error( "listServiceOfferings - ", e); + throw new EC2ServiceException(ServerError.InternalError, e.getMessage()); + } + } + /** * Convert from the Cloud serviceOfferingId to the Amazon instanceType strings based * on the loaded map. diff --git a/awsapi/src/com/cloud/stack/models/CloudStackServiceOffering.java b/awsapi/src/com/cloud/stack/models/CloudStackServiceOffering.java index a425799c6f4..0b6f87622a4 100644 --- a/awsapi/src/com/cloud/stack/models/CloudStackServiceOffering.java +++ b/awsapi/src/com/cloud/stack/models/CloudStackServiceOffering.java @@ -23,8 +23,8 @@ import com.google.gson.annotations.SerializedName; * */ public class CloudStackServiceOffering { - @SerializedName(ApiConstants.ID) - private Long id; + @SerializedName(ApiConstants.ID) + private String id; @SerializedName(ApiConstants.CPU_NUMBER) private Long cpuNumber; @SerializedName(ApiConstants.CPU_SPEED) @@ -38,7 +38,7 @@ public class CloudStackServiceOffering { @SerializedName(ApiConstants.DOMAIN) private String domain; @SerializedName(ApiConstants.DOMAIN_ID) - private Long domainId; + private String domainId; @SerializedName(ApiConstants.HOST_TAGS) private String hostTags; @SerializedName(ApiConstants.IS_SYSTEM) @@ -68,9 +68,9 @@ public class CloudStackServiceOffering { /** * @return the id */ - public Long getId() { - return id; - } + public String getId() { + return id; + } /** * @return the cpuNumber @@ -117,7 +117,7 @@ public class CloudStackServiceOffering { /** * @return the domainId */ - public Long getDomainId() { + public String getDomainId() { return domainId; } diff --git a/build/build-aws-api.xml b/build/build-aws-api.xml index 22e157f771e..2d632223647 100644 --- a/build/build-aws-api.xml +++ b/build/build-aws-api.xml @@ -98,6 +98,7 @@ + diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index 0c0d73eb8da..0b4f7dd4238 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -218,6 +218,7 @@ public enum Config { ElasticLoadBalancerVmNumVcpu("Advanced", ManagementServer.class, Integer.class, "network.loadbalancer.basiczone.elb.vm.vcpu.num", "1", "Number of VCPU for the elastic load balancer vm", null), ElasticLoadBalancerVmGcInterval("Advanced", ManagementServer.class, Integer.class, "network.loadbalancer.basiczone.elb.gc.interval.minutes", "30", "Garbage collection interval to destroy unused ELB vms in minutes. Minimum of 5", null), SortKeyAlgorithm("Advanced", ManagementServer.class, Boolean.class, "sortkey.algorithm", "false", "Sort algorithm for those who use sort key(template, disk offering, service offering, network offering), true means ascending sort while false means descending sort", null), + EnableEC2API("Advanced", ManagementServer.class, Boolean.class, "enable.ec2.api", "false", "enable EC2 API on CloudStack", null), // Ovm OvmPublicNetwork("Hidden", ManagementServer.class, String.class, "ovm.public.network.device", null, "Specify the public bridge on host for public network", null),