/** * This file is part of CloudML [ http://cloudml.org ] * * Copyright (C) 2012 - SINTEF ICT * Contact: Franck Chauvel <franck.chauvel@sintef.no> * * Module: root * * CloudML is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * CloudML is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with CloudML. If not, see * <http://www.gnu.org/licenses/>. */ package org.cloudml.connectors; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.logging.Level; import java.util.logging.Logger; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.services.elasticbeanstalk.*; import com.amazonaws.services.elasticbeanstalk.model.CheckDNSAvailabilityRequest; import com.amazonaws.services.elasticbeanstalk.model.CheckDNSAvailabilityResult; import com.amazonaws.services.elasticbeanstalk.model.CreateApplicationRequest; import com.amazonaws.services.elasticbeanstalk.model.CreateApplicationResult; import com.amazonaws.services.elasticbeanstalk.model.CreateApplicationVersionRequest; import com.amazonaws.services.elasticbeanstalk.model.CreateEnvironmentRequest; import com.amazonaws.services.elasticbeanstalk.model.CreateEnvironmentResult; import com.amazonaws.services.elasticbeanstalk.model.DeleteApplicationRequest; import com.amazonaws.services.elasticbeanstalk.model.ListAvailableSolutionStacksResult; import com.amazonaws.services.elasticbeanstalk.model.S3Location; import com.amazonaws.services.elasticbeanstalk.model.TerminateEnvironmentRequest; import com.amazonaws.services.elasticbeanstalk.model.UpdateEnvironmentRequest; import com.amazonaws.services.elasticbeanstalk.model.UpdateEnvironmentResult; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.ec2.AmazonEC2Client; import com.amazonaws.services.ec2.model.DescribeInstancesRequest; import com.amazonaws.services.ec2.model.DescribeInstancesResult; import com.amazonaws.services.ec2.model.Reservation; import com.amazonaws.services.elasticbeanstalk.model.DescribeEnvironmentResourcesRequest; import com.amazonaws.services.elasticbeanstalk.model.DescribeEnvironmentResourcesResult; import com.amazonaws.services.elasticbeanstalk.model.Instance; import com.amazonaws.services.rds.AmazonRDSClient; import com.amazonaws.services.rds.model.AuthorizeDBSecurityGroupIngressRequest; import com.amazonaws.services.rds.model.CreateDBInstanceRequest; import com.amazonaws.services.rds.model.CreateDBSecurityGroupRequest; import com.amazonaws.services.rds.model.DBInstance; import com.amazonaws.services.rds.model.DeleteDBInstanceRequest; import com.amazonaws.services.rds.model.DescribeDBInstancesRequest; import com.amazonaws.services.rds.model.DescribeDBInstancesResult; import com.amazonaws.services.rds.model.Endpoint; import com.amazonaws.services.rds.model.ModifyDBInstanceRequest; import com.amazonaws.services.rds.model.RevokeDBSecurityGroupIngressRequest; import com.amazonaws.services.sqs.AmazonSQSAsyncClient; import com.amazonaws.services.sqs.AmazonSQSClient; import com.amazonaws.services.sqs.model.CreateQueueRequest; import com.amazonaws.services.sqs.model.CreateQueueResult; import com.amazonaws.services.sqs.model.DeleteQueueRequest; import com.amazonaws.services.sqs.model.ListQueuesResult; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * PaasConnector for BeansTalk and RDS * * @author Nicolas Ferry for beanstalk,SQS * @author Hui Song for rds */ public class BeanstalkConnector implements PaaSConnector { /* Classical process: 1- create application (name, description) 2- create environment (tier=worker|webserver, platform=tomcat|nodejs..., type=autoscaling|single) 3- create version (upload your app) 4- check dns availability 5- refine env info (name, url, description) 6- select key and type of instance 7- create */ private static final Logger journal = Logger.getLogger(BeanstalkConnector.class.getName()); //private String endpoint="elasticbeanstalk.eu-west-1.amazonaws.com"; private AWSElasticBeanstalkClient beanstalkClient; private AWSCredentials awsCredentials; private String beanstalkEndpoint; private String rdsEndpoint; private String sqsEndpoint; private AmazonRDSClient rdsClient; private AmazonSQSClient sqsClient; Map<String, CreateDBInstanceRequest> previousRequests = new HashMap<String, CreateDBInstanceRequest>(); Map<String, DBInstance> createdInstances = new HashMap<String, DBInstance>(); public BeanstalkConnector(String login, String pass, String region) { awsCredentials = new BasicAWSCredentials(login, pass); beanstalkClient = new AWSElasticBeanstalkClient(awsCredentials); this.beanstalkEndpoint = String.format("elasticbeanstalk.%s.amazonaws.com", region); beanstalkClient.setEndpoint(beanstalkEndpoint); this.rdsEndpoint = String.format("rds.%s.amazonaws.com", region); rdsClient = new AmazonRDSClient(awsCredentials); rdsClient.setEndpoint(rdsEndpoint); this.sqsEndpoint = String.format("sqs.%s.amazonaws.com", region); sqsClient=new AmazonSQSAsyncClient(awsCredentials); sqsClient.setEndpoint(this.sqsEndpoint); } public void createApplication(String name) { CreateApplicationRequest cr = new CreateApplicationRequest(); cr.setApplicationName(name); cr.setDescription("Generated by CloudML"); CreateApplicationResult res = beanstalkClient.createApplication(cr); journal.log(Level.INFO, ">> Status of the application creation: " + res.toString()); } public void terminateApplication(String name) { DeleteApplicationRequest dr = new DeleteApplicationRequest(name); beanstalkClient.deleteApplication(dr); } public CheckDNSAvailabilityResult checkDNS(String domainName) { CheckDNSAvailabilityRequest cr = new CheckDNSAvailabilityRequest(domainName); CheckDNSAvailabilityResult res = beanstalkClient.checkDNSAvailability(cr); journal.log(Level.INFO, ">> Domain Name availability: " + res.toString()); return res; } public void createEnvironment(String applicationName, String domainName, String envName, String stackName) { CreateEnvironmentRequest cr = new CreateEnvironmentRequest(); cr.setApplicationName(applicationName); cr.setEnvironmentName(envName); String stack = findSolutionStack(stackName); if (!stack.equals("")) { cr.setSolutionStackName(stack); CheckDNSAvailabilityResult r = checkDNS(domainName); if (r.isAvailable()) { cr.setCNAMEPrefix(domainName); CreateEnvironmentResult res = beanstalkClient.createEnvironment(cr); journal.log(Level.INFO, ">> Status of the environment creation: " + res.toString()); } else { journal.log(Level.INFO, ">> Status of the environment creation: Domain Name already existing"); } } else { journal.log(Level.INFO, ">> Status of the environment creation: This type of stack does not exist!"); } } public String createEnvironmentWithWar(String applicationName, String domainName, String envName, String stackName, int minRam, String warFile, String versionLabel) { String endPoint=""; prepareWar(new File(warFile), versionLabel, applicationName); CreateEnvironmentRequest cr = new CreateEnvironmentRequest(); cr.setApplicationName(applicationName); cr.setEnvironmentName(envName); cr.setVersionLabel(versionLabel); String stack = findSolutionStack(stackName); if (!stack.equals("")) { cr.setSolutionStackName(stack); CheckDNSAvailabilityResult r = checkDNS(domainName); if (r.isAvailable()) { cr.setCNAMEPrefix(domainName); CreateEnvironmentResult res = beanstalkClient.createEnvironment(cr); endPoint=res.getEndpointURL(); journal.log(Level.INFO, ">> Status of the environment creation: " + res.toString()); } else { journal.log(Level.INFO, ">> Status of the environment creation: Domain Name already existing"); } } else { journal.log(Level.INFO, ">> Status of the environment creation: This type of stack does not exist!"); } return endPoint; } public void uploadWar(String warFile, String versionLabel, String applicationName, String envName, int timeout) { prepareWar(new File(warFile), versionLabel, applicationName); journal.log(Level.INFO, ">> Uploading War file!"); while(timeout-- > 0){ System.out.print("-"); try { Thread.sleep(1000); } catch (InterruptedException ex) { Logger.getLogger(BeanstalkConnector.class.getName()).log(Level.SEVERE, null, ex); } try{ UpdateEnvironmentResult updateEnvironment = beanstalkClient.updateEnvironment(new UpdateEnvironmentRequest() .withEnvironmentName(envName) .withVersionLabel(versionLabel)); journal.log(Level.INFO, ">> War uploaded!"); break; } catch(com.amazonaws.AmazonServiceException e){ } } } public void prepareWar(File warFile, String versionLabel, String applicationName) { AmazonS3 s3 = new AmazonS3Client(awsCredentials); String bucketName = beanstalkClient.createStorageLocation().getS3Bucket(); String key; try { key = URLEncoder.encode(warFile.getName() + "-" + versionLabel, "UTF-8"); s3.putObject(bucketName, key, warFile); beanstalkClient.createApplicationVersion(new CreateApplicationVersionRequest() .withApplicationName(applicationName).withAutoCreateApplication(true) .withVersionLabel(versionLabel) .withSourceBundle(new S3Location(bucketName, key))); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block journal.log(Level.SEVERE, e.getMessage()); } } public void terminateEnvironment(String envName) { TerminateEnvironmentRequest tr = new TerminateEnvironmentRequest(); tr.setEnvironmentName(envName); beanstalkClient.terminateEnvironment(tr); } //to be improved with the detailed version public String findSolutionStack(String name) { ListAvailableSolutionStacksResult list = beanstalkClient.listAvailableSolutionStacks(); for (String s : list.getSolutionStacks()) { if (s.contains(name)) { return s; } } return ""; } public Collection<String> getEnvIPs(String envName, int timeout) { DescribeEnvironmentResourcesRequest request = new DescribeEnvironmentResourcesRequest() .withEnvironmentName(envName); List<Instance> instances = null; System.out.print("Waiting for environment ips"); while(timeout-->0){ try { Thread.sleep(1000); } catch (InterruptedException ex) { Logger.getLogger(BeanstalkConnector.class.getName()).log(Level.SEVERE, null, ex); } System.out.print("-"); DescribeEnvironmentResourcesResult res = beanstalkClient.describeEnvironmentResources(request); instances = res.getEnvironmentResources().getInstances(); if(instances.size()==0) continue; AmazonEC2Client ec2 = new AmazonEC2Client(awsCredentials); ec2.setEndpoint(beanstalkEndpoint.replace("elasticbeanstalk", "ec2")); List<String> instanceIds = new ArrayList<String>(); for (Instance instance : instances) { instanceIds.add(instance.getId()); } List<String> ips = new ArrayList<String>(); DescribeInstancesRequest desins = new DescribeInstancesRequest().withInstanceIds(instanceIds); DescribeInstancesResult desinres = ec2.describeInstances(desins); for (Reservation reservation : desinres.getReservations()) { for (com.amazonaws.services.ec2.model.Instance ins : reservation.getInstances()) { String ip = ins.getPublicIpAddress(); if(ip!=null && ip.length()>0) ips.add(ip); } } if(ips.size()>0) return ips; } return Collections.EMPTY_LIST; } public void createDBInstance(String engine, String version, String dbInstanceIdentifier, String dbName, String username, String password, Integer allocatedSize, String dbInstanceClass, String securityGroup) { if(allocatedSize<=0) //default minimal size for rds allocatedSize = 5; String groupName = dbInstanceIdentifier + "-security-group"; CreateDBSecurityGroupRequest csg = new CreateDBSecurityGroupRequest() .withDBSecurityGroupName(groupName) .withDBSecurityGroupDescription(groupName); try { rdsClient.createDBSecurityGroup(csg); } catch (Exception e) { journal.log(Level.INFO, ">> Security Group " + groupName + " already exists."); } CreateDBInstanceRequest request = new CreateDBInstanceRequest() .withDBName(null) .withAllocatedStorage(allocatedSize) .withDBInstanceIdentifier(dbInstanceIdentifier) .withDBName(dbName) .withMasterUsername(username) .withMasterUserPassword(password) .withEngine(engine) .withPubliclyAccessible(true) .withEngineVersion(version); request.getDBSecurityGroups().add(groupName); if(!securityGroup.equals("")) request.getDBSecurityGroups().add(securityGroup); previousRequests.put(dbInstanceIdentifier, request); if (dbInstanceClass == null || dbInstanceClass.length() == 0) { request.setDBInstanceClass("db.t1.micro"); } else { request.setDBInstanceClass(dbInstanceClass); } DBInstance instance = rdsClient.createDBInstance(request); journal.log(Level.INFO, String.format(">> RDS instance created: %s, at %s", instance.toString(),instance.getEndpoint())); createdInstances.put(dbInstanceIdentifier, instance); } /** * Not used yet! * * @param dbInstanceIdentifier * @param group * @param owner */ public void setSecuretGroup(String dbInstanceIdentifier, String group, String owner) { String groupName = dbInstanceIdentifier + "-security-group"; CreateDBSecurityGroupRequest csg = new CreateDBSecurityGroupRequest(); csg.setDBSecurityGroupName(groupName); csg.setDBSecurityGroupDescription(groupName); rdsClient.createDBSecurityGroup(csg); RevokeDBSecurityGroupIngressRequest rsgi = new RevokeDBSecurityGroupIngressRequest(); rsgi.setDBSecurityGroupName(groupName); rsgi.setEC2SecurityGroupId(group); rsgi.setEC2SecurityGroupOwnerId(owner); rsgi.setRequestCredentials(awsCredentials); //rsgi.set rdsClient.revokeDBSecurityGroupIngress(rsgi); ModifyDBInstanceRequest request = new ModifyDBInstanceRequest(); Collection<String> groups = new ArrayList(); groups.add(groupName); request.setDBSecurityGroups(groups); request.setDBInstanceIdentifier(dbInstanceIdentifier); rdsClient.modifyDBInstance(request); } public void openDBForIps(String dbInstanceIdentifier, Collection<String> ips, int timeout) { String groupName = dbInstanceIdentifier + "-security-group"; for (String ip : ips) { AuthorizeDBSecurityGroupIngressRequest audbgi = new AuthorizeDBSecurityGroupIngressRequest() .withCIDRIP(ip + "/32") .withDBSecurityGroupName(groupName); audbgi.setRequestCredentials(awsCredentials); rdsClient.authorizeDBSecurityGroupIngress(audbgi); } rdsClient.authorizeDBSecurityGroupIngress(new AuthorizeDBSecurityGroupIngressRequest() .withCIDRIP("0.0.0.0/0") .withDBSecurityGroupName(groupName) ); ModifyDBInstanceRequest request = new ModifyDBInstanceRequest(); Collection<String> groups = new ArrayList(); groups.add(groupName); request.setDBSecurityGroups(groups); request.setDBInstanceIdentifier(dbInstanceIdentifier); System.out.print("Modifying security group"); while(timeout-->0){ System.out.print("-"); try { Thread.sleep(1000); } catch (InterruptedException ex) { Logger.getLogger(BeanstalkConnector.class.getName()).log(Level.SEVERE, null, ex); } try{ rdsClient.modifyDBInstance(request); break; }catch(Exception e){ continue; } } } public String getDBEndPoint(String dbInstanceId, int timeout){ DescribeDBInstancesRequest ddbir = new DescribeDBInstancesRequest() .withDBInstanceIdentifier(dbInstanceId); System.out.println("Waiting for DB endpoints"); while(timeout -- > 0){ System.out.print("-"); DescribeDBInstancesResult ddbi = rdsClient.describeDBInstances(ddbir); Endpoint endpoint = ddbi.getDBInstances().get(0).getEndpoint(); if(endpoint != null && endpoint.toString().length()!=0) return endpoint.getAddress()+":"+endpoint.getPort(); try { Thread.sleep(1000); } catch (InterruptedException ex) { Logger.getLogger(BeanstalkConnector.class.getName()).log(Level.SEVERE, null, ex); } } return ""; } public String getDBStatus(String dbInstanceId){ DescribeDBInstancesRequest ddbir = new DescribeDBInstancesRequest() .withDBInstanceIdentifier(dbInstanceId); DescribeDBInstancesResult ddbi = rdsClient.describeDBInstances(ddbir); try{ return ddbi.getDBInstances().get(0).getStatusInfos().toString(); }catch(Exception e){ throw new RuntimeException("DBInstance not found"); } } public void deleteDBInstance(String dbInstanceIdentifier) { DeleteDBInstanceRequest request = new DeleteDBInstanceRequest(); request.setDBInstanceIdentifier(dbInstanceIdentifier); rdsClient.deleteDBInstance(request); } public void restoreDB(String host, String port, String dbUser,String dbPass, String dbName, String local_file){ String executeCmd=""; if(System.getProperty("os.name").toLowerCase().contains("windows")) executeCmd="mysql -h "+host+" -P " + port + " -u " + dbUser + " -p" + dbPass + " " + dbName + " < "+local_file; else executeCmd="mysql -h "+host+" -P " + port + " -u " + dbUser + " -p" + dbPass + " " + dbName + " < "+local_file; System.out.println("run:--> "+executeCmd); Process runtimeProcess = null; try { runtimeProcess = Runtime.getRuntime().exec(executeCmd); int processComplete = runtimeProcess.waitFor(); if (processComplete == 0) { System.out.println("success"); } else { System.out.println("restore failure"); } } catch (IOException e) { journal.log(Level.SEVERE, e.getMessage()); } catch (InterruptedException e) { journal.log(Level.SEVERE, e.getMessage()); } } public String createQueue(String name){ CreateQueueRequest request=new CreateQueueRequest(); request.setQueueName(name); CreateQueueResult res= sqsClient.createQueue(request); return res.getQueueUrl(); } public void deleteQueue(String name){ DeleteQueueRequest request = new DeleteQueueRequest(name); sqsClient.deleteQueue(request); } public List<String> listQueues(){ ListQueuesResult result= sqsClient.listQueues(); return result.getQueueUrls(); } @Override public void configAppParameters(String applicationName, Map<String,String> params) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public void bindDbToApp(String appId, String dbId, String alias) { return; } @Override public void setEnvVar(String appName, String nameVar, String val) { return; } @Override public void deleteApp(String appName) { terminateEnvironment(appName); } }