Java tutorial
/* * 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 org.apache.stratos.aws.extension; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import com.amazonaws.AmazonServiceException; import com.amazonaws.services.ec2.model.*; import com.amazonaws.services.elasticloadbalancing.model.Instance; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.stratos.load.balancer.common.domain.*; import org.apache.stratos.load.balancer.extension.api.exception.LoadBalancerExtensionException; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import com.amazonaws.AmazonClientException; import com.amazonaws.ClientConfiguration; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.services.cloudwatch.AmazonCloudWatchClient; import com.amazonaws.services.cloudwatch.model.Datapoint; import com.amazonaws.services.cloudwatch.model.Dimension; import com.amazonaws.services.cloudwatch.model.GetMetricStatisticsRequest; import com.amazonaws.services.cloudwatch.model.GetMetricStatisticsResult; import com.amazonaws.services.ec2.AmazonEC2Client; import com.amazonaws.services.elasticloadbalancing.AmazonElasticLoadBalancingClient; import com.amazonaws.services.elasticloadbalancing.model.*; public class AWSHelper { private String awsAccessKey; private String awsSecretKey; private String lbPrefix; private String lbSecurityGroupName; private String lbSecurityGroupId; private String lbSecurityGroupDescription; private String allowedCidrIpForLBSecurityGroup; private int statisticsInterval; private String sslCertificateId; private String appStickySessionCookie; private Set<String> initialZones = new HashSet<>(); private Set<String> subnetIds = new HashSet<>(); private Set<String> vpcIds = new HashSet<>(); private String lbScheme; private AtomicInteger lbSequence; private List<String> allowedProtocolsForLBSecurityGroup; private ConcurrentHashMap<String, String> regionToSecurityGroupIdMap; private BasicAWSCredentials awsCredentials; private ClientConfiguration clientConfiguration; AmazonElasticLoadBalancingClient elbClient; AmazonEC2Client ec2Client; private AmazonCloudWatchClient cloudWatchClient; private static final Log log = LogFactory.getLog(AWSHelper.class); public AWSHelper() throws LoadBalancerExtensionException { // Read values for awsAccessKey, awsSecretKey etc. from config file String awsPropertiesFile = System.getProperty(Constants.AWS_PROPERTIES_FILE); Properties properties = new Properties(); InputStream inputStream = null; try { inputStream = new FileInputStream(awsPropertiesFile); properties.load(inputStream); this.awsAccessKey = properties.getProperty(Constants.AWS_ACCESS_KEY); this.awsSecretKey = properties.getProperty(Constants.AWS_SECRET_KEY); if (this.awsAccessKey.isEmpty() || this.awsSecretKey.isEmpty()) { throw new LoadBalancerExtensionException("Invalid AWS credentials."); } this.lbPrefix = properties.getProperty(Constants.LB_PREFIX); if (this.lbPrefix.isEmpty() || this.lbPrefix.length() > Constants.LOAD_BALANCER_PREFIX_MAX_LENGTH) { throw new LoadBalancerExtensionException("Invalid load balancer prefix."); } lbSequence = new AtomicInteger(1); this.lbSecurityGroupName = properties.getProperty(Constants.LOAD_BALANCER_SECURITY_GROUP_NAME); lbSecurityGroupId = properties.getProperty(Constants.LOAD_BALANCER_SECURITY_GROUP_ID); if ((lbSecurityGroupId == null || lbSecurityGroupId.isEmpty()) && (this.lbSecurityGroupName.isEmpty() || this.lbSecurityGroupName.length() > Constants.SECURITY_GROUP_NAME_MAX_LENGTH)) { throw new LoadBalancerExtensionException( "Either security group name or security " + "group id is required"); } // if (this.lbSecurityGroupName.isEmpty() || this.lbSecurityGroupName.length() > // Constants.SECURITY_GROUP_NAME_MAX_LENGTH) { // throw new LoadBalancerExtensionException("Invalid load balancer security group name."); // } // Read the SSL certificate Id. This is mandatory if only we are using HTTPS as the front end protocol. // http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/using-elb-listenerconfig-quickref.html this.sslCertificateId = properties.getProperty(Constants.LOAD_BALANCER_SSL_CERTIFICATE_ID).trim(); // Cookie name for application level stickiness this.appStickySessionCookie = properties.getProperty(Constants.APP_STICKY_SESSION_COOKIE_NAME).trim(); this.allowedCidrIpForLBSecurityGroup = properties.getProperty(Constants.ALLOWED_CIDR_IP_KEY); if (this.allowedCidrIpForLBSecurityGroup.isEmpty()) { throw new LoadBalancerExtensionException("Invalid allowed CIDR IP."); } String allowedProtocols = properties.getProperty(Constants.ALLOWED_PROTOCOLS); if (allowedProtocols.isEmpty()) { throw new LoadBalancerExtensionException("Please specify at least one Internet protocol."); } String[] protocols = allowedProtocols.split(","); this.allowedProtocolsForLBSecurityGroup = new ArrayList<String>(); for (String protocol : protocols) { this.allowedProtocolsForLBSecurityGroup.add(protocol); } String interval = properties.getProperty(Constants.STATISTICS_INTERVAL); if (interval == null || interval.isEmpty()) { this.statisticsInterval = Constants.STATISTICS_INTERVAL_MULTIPLE_OF; } else { try { this.statisticsInterval = Integer.parseInt(interval); if (this.statisticsInterval % Constants.STATISTICS_INTERVAL_MULTIPLE_OF != 0) { this.statisticsInterval = Constants.STATISTICS_INTERVAL_MULTIPLE_OF; } } catch (NumberFormatException e) { log.warn("Invalid statistics interval. Setting it to 15."); this.statisticsInterval = 15; } } this.lbSecurityGroupDescription = Constants.LOAD_BALANCER_SECURITY_GROUP_DESCRIPTION; String commaSeparatedInitialZones = properties.getProperty(Constants.INITIAL_AVAILABILITY_ZONES); if (commaSeparatedInitialZones != null && !commaSeparatedInitialZones.isEmpty()) { initialZones.addAll(Arrays.asList(commaSeparatedInitialZones.trim().split("\\s*," + "\\s*"))); } String commaSeparatedSubnetIds = properties.getProperty(Constants.SUBNET_IDS); if (commaSeparatedSubnetIds != null && !commaSeparatedSubnetIds.isEmpty()) { subnetIds.addAll(Arrays.asList(commaSeparatedSubnetIds.trim().split("\\s*," + "\\s*"))); } String commaSeparatedVPCIds = properties.getProperty(Constants.VPC_IDS); if (commaSeparatedVPCIds != null && !commaSeparatedVPCIds.isEmpty()) { vpcIds.addAll(Arrays.asList(commaSeparatedVPCIds.trim().split("\\s*," + "\\s*"))); } lbScheme = properties.getProperty(Constants.LB_SCHEME); regionToSecurityGroupIdMap = new ConcurrentHashMap<String, String>(); awsCredentials = new BasicAWSCredentials(awsAccessKey, awsSecretKey); clientConfiguration = new ClientConfiguration(); elbClient = new AmazonElasticLoadBalancingClient(awsCredentials, clientConfiguration); ec2Client = new AmazonEC2Client(awsCredentials, clientConfiguration); cloudWatchClient = new AmazonCloudWatchClient(awsCredentials, clientConfiguration); } catch (IOException e) { log.error("Error reading aws configuration file."); throw new LoadBalancerExtensionException("Error reading aws configuration file.", e); } finally { try { inputStream.close(); } catch (Exception e) { log.warn("Failed to close input stream to aws configuration file."); } } } public AWSHelper(String awsAccessKey, String awsSecretKey) { this.awsAccessKey = awsAccessKey; this.awsSecretKey = awsSecretKey; } public int getStatisticsInterval() { return statisticsInterval; } public int getNextLBSequence() { return lbSequence.getAndIncrement(); } public String getLbSecurityGroupName() { return lbSecurityGroupName; } public List<String> getAllowedProtocolsForLBSecurityGroup() { return allowedProtocolsForLBSecurityGroup; } /** * Creates a load balancer and returns its DNS name. Useful when a new * cluster is added. * * @param name of the load balancer to be created * @param listeners to be attached to the load balancer * @param region in which the load balancer needs to be created * @return DNS name of newly created load balancer * @throws LoadBalancerExtensionException */ public String createLoadBalancer(String name, List<Listener> listeners, String region, Set<String> availabilityZones, boolean inVPC) throws LoadBalancerExtensionException { log.info("Creating load balancer " + name); CreateLoadBalancerRequest createLoadBalancerRequest = new CreateLoadBalancerRequest(name); createLoadBalancerRequest.setListeners(listeners); // don't need this now since we are anyway updating zone according to the member // Set<String> availabilityZones = new HashSet<String>(); // availabilityZones.add(getAvailabilityZoneFromRegion(region)); // // createLoadBalancerRequest.setAvailabilityZones(availabilityZones); try { if (inVPC) { List<String> securityGroups = new ArrayList<String>(); if (!vpcIds.isEmpty()) { for (String vpcId : vpcIds) { String securityGroupId = getSecurityGroupIdForRegion(region, vpcId); securityGroups.add(securityGroupId); } } else { String securityGroupId = getSecurityGroupIdForRegion(region, null); securityGroups.add(securityGroupId); } createLoadBalancerRequest.setSecurityGroups(securityGroups); // set subnet ids if (!getSubnetIds().isEmpty()) { createLoadBalancerRequest.setSubnets(subnetIds); } // set scheme to 'internal' if specified if (getLbScheme() != null && getLbScheme().equals(Constants.LB_SCHEME_INTERNAL)) { createLoadBalancerRequest.setScheme(getLbScheme()); } } else { // set initial availability zones createLoadBalancerRequest.setAvailabilityZones(availabilityZones); } elbClient.setEndpoint(String.format(Constants.ELB_ENDPOINT_URL_FORMAT, region)); CreateLoadBalancerResult clbResult = elbClient.createLoadBalancer(createLoadBalancerRequest); return clbResult.getDNSName(); } catch (AmazonClientException e) { String errorMsg = "Could not create load balancer " + name; log.error(errorMsg, e); throw new LoadBalancerExtensionException(errorMsg, e); } } /** * Deletes the load balancer with the name provided. Useful when a cluster, * with which this load balancer was associated, is removed. * * @param loadBalancerName to be deleted * @param region of the laod balancer */ public void deleteLoadBalancer(String loadBalancerName, String region) { log.info("Deleting load balancer " + loadBalancerName); DeleteLoadBalancerRequest deleteLoadBalancerRequest = new DeleteLoadBalancerRequest(); deleteLoadBalancerRequest.setLoadBalancerName(loadBalancerName); try { elbClient.setEndpoint(String.format(Constants.ELB_ENDPOINT_URL_FORMAT, region)); elbClient.deleteLoadBalancer(deleteLoadBalancerRequest); log.info("Deleted load balancer " + loadBalancerName); } catch (AmazonClientException e) { log.error("Could not delete load balancer : " + loadBalancerName, e); } } /** * Attaches provided instances to the load balancer. Useful when new * instances get added to the cluster with which this load balancer is * associated. * * @param loadBalancerName * @param instances to attached to the load balancer * @param region of the load balancer */ public void registerInstancesToLoadBalancer(String loadBalancerName, List<Instance> instances, String region) { log.info("Registering following instance(s) to load balancer " + loadBalancerName); for (Instance instance : instances) { log.info(instance.getInstanceId()); } RegisterInstancesWithLoadBalancerRequest registerInstancesWithLoadBalancerRequest = new RegisterInstancesWithLoadBalancerRequest( loadBalancerName, instances); RegisterInstancesWithLoadBalancerResult registerInstancesWithLBRes = null; try { elbClient.setEndpoint(String.format(Constants.ELB_ENDPOINT_URL_FORMAT, region)); registerInstancesWithLBRes = elbClient .registerInstancesWithLoadBalancer(registerInstancesWithLoadBalancerRequest); } catch (AmazonClientException e) { log.error("Could not register instances to load balancer " + loadBalancerName, e); } if (registerInstancesWithLBRes != null && registerInstancesWithLBRes.getInstances().size() > 0) { log.info("Total instances attached to the LB " + loadBalancerName + " : " + registerInstancesWithLBRes.getInstances().size()); } else { log.warn("No instances attached to the LB " + loadBalancerName); } } /** * Detaches provided instances from the load balancer, associated with some * cluster. Useful when instances are removed from the cluster with which * this load balancer is associated. * * @param loadBalancerName * @param instances to be de-registered from load balancer * @param region of the load balancer */ public void deregisterInstancesFromLoadBalancer(String loadBalancerName, List<Instance> instances, String region) { log.info("De-registering following instance(s) from load balancer " + loadBalancerName); for (Instance instance : instances) { log.info(instance.getInstanceId()); } DeregisterInstancesFromLoadBalancerRequest deregisterInstancesFromLoadBalancerRequest = new DeregisterInstancesFromLoadBalancerRequest( loadBalancerName, instances); try { elbClient.setEndpoint(String.format(Constants.ELB_ENDPOINT_URL_FORMAT, region)); elbClient.deregisterInstancesFromLoadBalancer(deregisterInstancesFromLoadBalancerRequest); } catch (AmazonClientException e) { log.error("Could not de-register instances from load balancer " + loadBalancerName, e); } } /** * Returns description of the load balancer which is helpful in determining * instances, listeners associated with load balancer * * @param loadBalancerName * @param region of the load balancer * @return description of the load balancer */ public LoadBalancerDescription getLoadBalancerDescription(String loadBalancerName, String region) { List<String> loadBalancers = new ArrayList<String>(); loadBalancers.add(loadBalancerName); DescribeLoadBalancersRequest describeLoadBalancersRequest = new DescribeLoadBalancersRequest(loadBalancers); try { elbClient.setEndpoint(String.format(Constants.ELB_ENDPOINT_URL_FORMAT, region)); DescribeLoadBalancersResult result = elbClient.describeLoadBalancers(describeLoadBalancersRequest); if (result.getLoadBalancerDescriptions() != null && result.getLoadBalancerDescriptions().size() > 0) return result.getLoadBalancerDescriptions().get(0); } catch (AmazonClientException e) { log.error("Could not find description of load balancer " + loadBalancerName, e); } return null; } /** * Returns instances attached to the load balancer. Useful when deciding if * all attached instances are required or some should be detached. * * @param loadBalancerName * @param region * @return list of instances attached */ public List<Instance> getAttachedInstances(String loadBalancerName, String region) { try { LoadBalancerDescription lbDescription = getLoadBalancerDescription(loadBalancerName, region); if (lbDescription == null) { log.warn("Could not find description of load balancer " + loadBalancerName); return null; } return lbDescription.getInstances(); } catch (AmazonClientException e) { log.error("Could not find instances attached load balancer " + loadBalancerName, e); } return null; } /** * Returns all the listeners attached to the load balancer. Useful while * deciding if all the listeners are necessary or some should be removed. * * @param loadBalancerName * @param region * @return list of instances attached to load balancer */ public List<Listener> getAttachedListeners(String loadBalancerName, String region) { try { LoadBalancerDescription lbDescription = getLoadBalancerDescription(loadBalancerName, region); if (lbDescription == null) { log.warn("Could not find description of load balancer " + loadBalancerName); return null; } List<Listener> listeners = new ArrayList<Listener>(); List<ListenerDescription> listenerDescriptions = lbDescription.getListenerDescriptions(); for (ListenerDescription listenerDescription : listenerDescriptions) { listeners.add(listenerDescription.getListener()); } return listeners; } catch (AmazonClientException e) { log.error("Could not find description of load balancer " + loadBalancerName); return null; } } /** * Checks if the security group is already present in the given region. If * yes, then returns its group id. If not, present the returns null. * * @param groupName to be checked for presence. * @param region * @return id of the security group */ public String getSecurityGroupId(String groupName, String region) { if (groupName == null || groupName.isEmpty()) { return null; } DescribeSecurityGroupsRequest describeSecurityGroupsRequest = new DescribeSecurityGroupsRequest(); if (AWSExtensionContext.getInstance().isOperatingInVPC()) { if (getVpcIds().size() > 0) { // vpc id filter Set<Filter> filters = getFilters(getVpcIds().iterator().next(), lbSecurityGroupName); describeSecurityGroupsRequest.setFilters(filters); } else { List<String> groupNames = new ArrayList<String>(); groupNames.add(groupName); describeSecurityGroupsRequest.setGroupNames(groupNames); } } try { ec2Client.setEndpoint(String.format(Constants.EC2_ENDPOINT_URL_FORMAT, region)); DescribeSecurityGroupsResult describeSecurityGroupsResult = ec2Client .describeSecurityGroups(describeSecurityGroupsRequest); List<SecurityGroup> securityGroups = describeSecurityGroupsResult.getSecurityGroups(); if (securityGroups != null && securityGroups.size() > 0) { return securityGroups.get(0).getGroupId(); } else { log.warn("Could not find security group id for group " + groupName); } } catch (AmazonClientException e) { log.debug("Could not describe security groups.", e); } return null; } /** * Creates security group with the given name in the given region * * @param groupName to be created * @param description * @param region in which the security group to be created * @return Id of the security group created * @throws LoadBalancerExtensionException */ public String createSecurityGroup(String groupName, String description, String region, String vpcId) throws LoadBalancerExtensionException { if (groupName == null || groupName.isEmpty()) { throw new LoadBalancerExtensionException("Invalid Security Group Name."); } CreateSecurityGroupRequest createSecurityGroupRequest = new CreateSecurityGroupRequest(); createSecurityGroupRequest.setGroupName(groupName); createSecurityGroupRequest.setDescription(description); if (vpcId != null) { createSecurityGroupRequest.setVpcId(vpcId); } try { ec2Client.setEndpoint(String.format(Constants.EC2_ENDPOINT_URL_FORMAT, region)); CreateSecurityGroupResult createSecurityGroupResult = ec2Client .createSecurityGroup(createSecurityGroupRequest); return createSecurityGroupResult.getGroupId(); } catch (AmazonClientException e) { log.error("Could not create security group.", e); throw new LoadBalancerExtensionException("Could not create security group.", e); } } /** * Adds inbound rule to the security group which allows users to access load * balancer at specified port and using the specified protocol. Port * specified should be a proxy port mentioned in the port mappings of the * cartridge. * * @param groupId to which this rule to be added * @param region of the security group * @param protocol with which load balancer can be accessed * @param port at which load balancer can be accessed * @throws LoadBalancerExtensionException */ public void addInboundRuleToSecurityGroup(String groupId, String region, String protocol, int port) throws LoadBalancerExtensionException { if (groupId == null || groupId.isEmpty()) { throw new LoadBalancerExtensionException( "Invalid security group Id for addInboundRuleToSecurityGroup."); } boolean ruleAlreadyPresent = false; DescribeSecurityGroupsRequest describeSecurityGroupsRequest = new DescribeSecurityGroupsRequest(); List<String> groupIds = new ArrayList<String>(); groupIds.add(groupId); describeSecurityGroupsRequest.setGroupIds(groupIds); SecurityGroup secirutyGroup = null; try { ec2Client.setEndpoint(String.format(Constants.EC2_ENDPOINT_URL_FORMAT, region)); DescribeSecurityGroupsResult describeSecurityGroupsResult = ec2Client .describeSecurityGroups(describeSecurityGroupsRequest); List<SecurityGroup> securityGroups = describeSecurityGroupsResult.getSecurityGroups(); if (securityGroups != null && securityGroups.size() > 0) { secirutyGroup = securityGroups.get(0); } else { log.warn("No Security Groups found for group id " + groupId); } } catch (AmazonClientException e) { log.error("Could not describe security groups.", e); } if (secirutyGroup != null) { List<IpPermission> existingPermissions = secirutyGroup.getIpPermissions(); IpPermission neededPermission = new IpPermission(); neededPermission.setFromPort(port); neededPermission.setToPort(port); neededPermission.setIpProtocol(protocol); Collection<String> ipRanges = new HashSet<String>(); ipRanges.add(this.allowedCidrIpForLBSecurityGroup); neededPermission.setIpRanges(ipRanges); if (existingPermissions.contains(neededPermission)) { ruleAlreadyPresent = true; } } if (!ruleAlreadyPresent) { AuthorizeSecurityGroupIngressRequest authorizeSecurityGroupIngressRequest = new AuthorizeSecurityGroupIngressRequest(); authorizeSecurityGroupIngressRequest.setGroupId(groupId); authorizeSecurityGroupIngressRequest.setCidrIp(this.allowedCidrIpForLBSecurityGroup); authorizeSecurityGroupIngressRequest.setFromPort(port); authorizeSecurityGroupIngressRequest.setToPort(port); authorizeSecurityGroupIngressRequest.setIpProtocol(protocol); try { ec2Client.setEndpoint(String.format(Constants.EC2_ENDPOINT_URL_FORMAT, region)); ec2Client.authorizeSecurityGroupIngress(authorizeSecurityGroupIngressRequest); } catch (AmazonClientException e) { throw new LoadBalancerExtensionException( "Could not add inbound rule to security group " + groupId + ".", e); } } else { log.info("Rules already present for security group " + groupId); } } /** * Returns the security group id for the given region if it is already * present. If it is not already present then creates a new security group * in that region. * * @param region * @param vpcId * @return Id of the security group * @throws LoadBalancerExtensionException */ public String getSecurityGroupIdForRegion(String region, String vpcId) throws LoadBalancerExtensionException { // if (region == null) // return null; // // if (this.regionToSecurityGroupIdMap.contains(region)) { // return this.regionToSecurityGroupIdMap.get(region); // } else { // // Get the the security group id if it is already present. // String securityGroupId = getSecurityGroupId( // this.lbSecurityGroupName, region); // // if (securityGroupId == null) { // securityGroupId = createSecurityGroup(this.lbSecurityGroupName, // this.lbSecurityGroupDescription, region, vpcId); // } // // this.regionToSecurityGroupIdMap.put(region, securityGroupId); // // return securityGroupId; // } // if lb security group id is defined, use that, do not create a new security group if (lbSecurityGroupId != null && !lbSecurityGroupId.isEmpty()) { return lbSecurityGroupId; } // check if the security group is already exists DescribeSecurityGroupsRequest describeSecurityGroupsReq = new DescribeSecurityGroupsRequest(); // set filter for vpc id if (vpcId != null) { Set<Filter> filters = getFilters(vpcId, lbSecurityGroupName); describeSecurityGroupsReq.setFilters(filters); } else { // no vpc id defined, assume default vpc List<String> groupNames = new ArrayList<String>(); groupNames.add(lbSecurityGroupName); describeSecurityGroupsReq.setGroupNames(groupNames); } DescribeSecurityGroupsResult describeSecurityGroupsRes = null; try { ec2Client.setEndpoint(String.format(Constants.EC2_ENDPOINT_URL_FORMAT, region)); describeSecurityGroupsRes = ec2Client.describeSecurityGroups(describeSecurityGroupsReq); if (describeSecurityGroupsRes != null && describeSecurityGroupsRes.getSecurityGroups() != null) { // already exists, return the id if (describeSecurityGroupsRes.getSecurityGroups().size() > 0) { return describeSecurityGroupsRes.getSecurityGroups().get(0).getGroupId(); } } } catch (AmazonClientException e) { throw new LoadBalancerExtensionException(e.getMessage(), e); } return createSecurityGroup(this.lbSecurityGroupName, this.lbSecurityGroupDescription, region, vpcId); } private Set<Filter> getFilters(String vpcId, String securityGroupName) { // vpc id filter Filter vpcIdFilter = new Filter(); vpcIdFilter.setName("vpc-id"); Set<String> singleVpcIdSet = new HashSet<>(); singleVpcIdSet.add(vpcId); vpcIdFilter.setValues(singleVpcIdSet); // group name filter Filter groupNameFilter = new Filter(); groupNameFilter.setName("group-name"); Set<String> singleGroupNameSet = new HashSet<>(); singleGroupNameSet.add(securityGroupName); groupNameFilter.setValues(singleGroupNameSet); Set<Filter> filters = new HashSet<>(); filters.add(vpcIdFilter); filters.add(groupNameFilter); return filters; } /** * Retrieves the total number of requests that were made to the load * balancer during the given time interval in the past * * @param loadBalancerName * @param region * @param timeInterval in seconds which must be multiple of 60 * @return number of requests made */ public int getRequestCount(String loadBalancerName, String region, int timeInterval) { int count = 0; try { GetMetricStatisticsRequest request = new GetMetricStatisticsRequest(); request.setMetricName(Constants.REQUEST_COUNT_METRIC_NAME); request.setNamespace(Constants.CLOUD_WATCH_NAMESPACE_NAME); Date currentTime = new DateTime(DateTimeZone.UTC).toDate(); Date pastTime = new DateTime(DateTimeZone.UTC).minusSeconds(timeInterval).toDate(); request.setStartTime(pastTime); request.setEndTime(currentTime); request.setPeriod(timeInterval); HashSet<String> statistics = new HashSet<String>(); statistics.add(Constants.SUM_STATISTICS_NAME); request.setStatistics(statistics); HashSet<Dimension> dimensions = new HashSet<Dimension>(); Dimension loadBalancerDimension = new Dimension(); loadBalancerDimension.setName(Constants.LOAD_BALANCER_DIMENTION_NAME); loadBalancerDimension.setValue(loadBalancerName); dimensions.add(loadBalancerDimension); request.setDimensions(dimensions); cloudWatchClient.setEndpoint(String.format(Constants.CLOUD_WATCH_ENDPOINT_URL_FORMAT, region)); GetMetricStatisticsResult result = cloudWatchClient.getMetricStatistics(request); List<Datapoint> dataPoints = result.getDatapoints(); if (dataPoints != null && dataPoints.size() > 0) { count = dataPoints.get(0).getSum().intValue(); } } catch (AmazonClientException e) { log.error("Could not get request count statistics of load balancer " + loadBalancerName, e); } return count; } /** * Retrieves total number of responses generated by all instances attached * to the load balancer during the time interval in the past. * * @param loadBalancerName * @param region * @param timeInterval in seconds which must be multiple of 60 * @return number of responses generated */ public int getAllResponsesCount(String loadBalancerName, String region, int timeInterval) { int total = 0; Date currentTime = new DateTime(DateTimeZone.UTC).toDate(); Date pastTime = new DateTime(DateTimeZone.UTC).minusSeconds(timeInterval).toDate(); total += getResponseCountForMetric(loadBalancerName, region, Constants.HTTP_RESPONSE_2XX, pastTime, currentTime, timeInterval); total += getResponseCountForMetric(loadBalancerName, region, Constants.HTTP_RESPONSE_3XX, pastTime, currentTime, timeInterval); total += getResponseCountForMetric(loadBalancerName, region, Constants.HTTP_RESPONSE_4XX, pastTime, currentTime, timeInterval); total += getResponseCountForMetric(loadBalancerName, region, Constants.HTTP_RESPONSE_5XX, pastTime, currentTime, timeInterval); return total; } /** * Retrieves the number of responses generated for a particular response * code like 2XX, 3XX, 4XX, 5XX * * @param loadBalancerName * @param region * @param metricName which is one among HTTPCode_Backend_2XX or * HTTPCode_Backend_3XX or HTTPCode_Backend_4XX or * HTTPCode_Backend_5XX * @param startTime of the window to be scanned * @param endTime of the window to be scanned * @param timeInterval in seconds * @return number for response for this metric */ public int getResponseCountForMetric(String loadBalancerName, String region, String metricName, Date startTime, Date endTime, int timeInterval) { int count = 0; try { GetMetricStatisticsRequest request = new GetMetricStatisticsRequest(); request.setMetricName(metricName); request.setNamespace(Constants.CLOUD_WATCH_NAMESPACE_NAME); request.setStartTime(startTime); request.setEndTime(endTime); request.setPeriod(timeInterval); HashSet<String> statistics = new HashSet<String>(); statistics.add(Constants.SUM_STATISTICS_NAME); request.setStatistics(statistics); HashSet<Dimension> dimensions = new HashSet<Dimension>(); Dimension loadBalancerDimension = new Dimension(); loadBalancerDimension.setName(Constants.LOAD_BALANCER_DIMENTION_NAME); loadBalancerDimension.setValue(loadBalancerName); dimensions.add(loadBalancerDimension); request.setDimensions(dimensions); cloudWatchClient.setEndpoint(String.format(Constants.CLOUD_WATCH_ENDPOINT_URL_FORMAT, region)); GetMetricStatisticsResult result = cloudWatchClient.getMetricStatistics(request); List<Datapoint> dataPoints = result.getDatapoints(); if (dataPoints != null && dataPoints.size() > 0) { count = dataPoints.get(0).getSum().intValue(); } } catch (AmazonClientException e) { log.error("Could not get the statistics for metric " + metricName + " of load balancer " + loadBalancerName, e); } return count; } /** * Returns the Listeners required for the service. Listeners are derived * from the proxy port, port and protocol values of the service. * * @param service * @return list of listeners required for the service */ public List<Listener> getRequiredListeners(Member member) throws LoadBalancerExtensionException { List<Listener> listeners = new ArrayList<Listener>(); Collection<Port> ports = member.getPorts(); for (Port port : ports) { int instancePort = port.getValue(); int proxyPort = port.getProxy(); String protocol = port.getProtocol().toUpperCase(); String instanceProtocol = protocol; Listener listener = new Listener(protocol, proxyPort, instancePort); listener.setInstanceProtocol(instanceProtocol); if ("HTTPS".equalsIgnoreCase(protocol) || "SSL".equalsIgnoreCase(protocol)) { // if the SSL certificate is not configured in the aws.properties file, can't continue if (getSslCertificateId() == null || getSslCertificateId().isEmpty()) { String errorMsg = "Required property " + Constants.LOAD_BALANCER_SSL_CERTIFICATE_ID + " not provided in configuration"; log.error(errorMsg); throw new LoadBalancerExtensionException(errorMsg); } // TODO: make debug? if (log.isInfoEnabled()) { log.info("Listener protocol = " + protocol + ", hence setting the SSL Certificate Id: " + getSslCertificateId()); } listener.setSSLCertificateId(getSslCertificateId()); } listeners.add(listener); } return listeners; } /** * Constructs name of the load balancer to be associated with the cluster * * @param clusterId * @return name of the load balancer * @throws LoadBalancerExtensionException */ public String generateLoadBalancerName(String serviceName) throws LoadBalancerExtensionException { String name = null; //name = lbPrefix + getNextLBSequence(); name = lbPrefix + serviceName; if (name.length() > Constants.LOAD_BALANCER_NAME_MAX_LENGTH) throw new LoadBalancerExtensionException("Load balanacer name length (32 characters) exceeded"); return name; } /** * Extract instance id in IaaS side from member instance name * * @param memberInstanceName * @return instance id in IaaS */ public String getAWSInstanceName(String memberInstanceName) { if (memberInstanceName.contains("/")) { return memberInstanceName.substring(memberInstanceName.indexOf("/") + 1); } else { return memberInstanceName; } } /** * Extract IaaS region from member instance name * * @param memberInstanceName * @return IaaS region to which member belongs */ public String getAWSRegion(String memberInstanceName) { if (memberInstanceName.contains("/")) { return memberInstanceName.substring(0, memberInstanceName.indexOf("/")); } else { return null; } } /** * Get availability zone from region * * @param region * @return Availability zone of IaaS */ public String getAvailabilityZoneFromRegion(String region) { if (region != null) { return region + "a"; } else return null; } public CreateAppCookieStickinessPolicyResult createStickySessionPolicy(String lbName, String cookieName, String policyName, String region) { elbClient.setEndpoint(String.format(Constants.ELB_ENDPOINT_URL_FORMAT, region)); CreateAppCookieStickinessPolicyRequest stickinessPolicyReq = new CreateAppCookieStickinessPolicyRequest() .withLoadBalancerName(lbName).withCookieName(cookieName).withPolicyName(policyName); CreateAppCookieStickinessPolicyResult stickinessPolicyResult = null; try { stickinessPolicyResult = elbClient.createAppCookieStickinessPolicy(stickinessPolicyReq); } catch (AmazonServiceException e) { log.error(e.getMessage(), e); } catch (AmazonClientException e) { log.error(e.getMessage(), e); } if (stickinessPolicyResult == null) { log.error("Error in creating Application Stickiness policy for for cookie name: " + cookieName + ", policy: " + policyName); } else { log.info("Enabled Application stickiness using: " + cookieName + ", policy: " + policyName + " for LB " + lbName); } return stickinessPolicyResult; } public void applyPolicyToLBListenerPorts(Collection<Port> ports, String loadBalancerName, String policyName, String region) { for (Port port : ports) { if ("HTTP".equalsIgnoreCase(port.getProtocol()) || "HTTPS".equalsIgnoreCase(port.getProtocol())) { applyPolicyToListener(loadBalancerName, port.getProxy(), policyName, region); // hack to stop too many calls to AWS API :( try { Thread.sleep(2000); } catch (InterruptedException e) { } } } } private void applyPolicyToListener(String loadBalancerName, int listenerPort, String policyName, String region) { SetLoadBalancerPoliciesOfListenerRequest loadBalancerPoliciesOfListenerReq = new SetLoadBalancerPoliciesOfListenerRequest() .withLoadBalancerName(loadBalancerName).withLoadBalancerPort(listenerPort) .withPolicyNames(policyName); elbClient.setEndpoint(String.format(Constants.ELB_ENDPOINT_URL_FORMAT, region)); SetLoadBalancerPoliciesOfListenerResult setLBPoliciesOfListenerRes = null; try { setLBPoliciesOfListenerRes = elbClient .setLoadBalancerPoliciesOfListener(loadBalancerPoliciesOfListenerReq); } catch (AmazonServiceException e) { log.error(e.getMessage(), e); } catch (AmazonClientException e) { log.error(e.getMessage(), e); } if (setLBPoliciesOfListenerRes == null) { log.error("Unable to apply policy " + policyName + " for Listener port: " + listenerPort + " for LB: " + loadBalancerName); } else { log.info("Successfully applied policy " + policyName + " for Listener port: " + listenerPort + " for LB: " + loadBalancerName); } } public List<String> getAvailabilityZonesFromRegion(final String region) { DescribeAvailabilityZonesRequest availabilityZonesReq = new DescribeAvailabilityZonesRequest(); List<Filter> availabilityZoneFilters = new ArrayList<Filter>(); availabilityZoneFilters.add(new Filter("region-name", new ArrayList<String>() { { add(region); } })); availabilityZoneFilters.add(new Filter("state", new ArrayList<String>() { { add("available"); } })); ec2Client.setEndpoint(String.format(Constants.EC2_ENDPOINT_URL_FORMAT, region)); DescribeAvailabilityZonesResult availabilityZonesRes = null; try { availabilityZonesRes = ec2Client.describeAvailabilityZones(availabilityZonesReq); } catch (AmazonServiceException e) { log.error(e.getMessage(), e); } catch (AmazonClientException e) { log.error(e.getMessage(), e); } List<String> availabilityZones = null; if (availabilityZonesRes != null) { availabilityZones = new ArrayList<>(); for (AvailabilityZone zone : availabilityZonesRes.getAvailabilityZones()) { availabilityZones.add(zone.getZoneName()); } } else { log.error("Unable to retrieve the active availability zones for region " + region); } return availabilityZones; } public void addAvailabilityZonesForLoadBalancer(String loadBalancerName, List<String> availabilityZones, String region) { EnableAvailabilityZonesForLoadBalancerRequest enableAvailabilityZonesReq = new EnableAvailabilityZonesForLoadBalancerRequest() .withLoadBalancerName(loadBalancerName).withAvailabilityZones(availabilityZones); elbClient.setEndpoint(String.format(Constants.ELB_ENDPOINT_URL_FORMAT, region)); EnableAvailabilityZonesForLoadBalancerResult enableAvailabilityZonesRes = null; try { enableAvailabilityZonesRes = elbClient .enableAvailabilityZonesForLoadBalancer(enableAvailabilityZonesReq); } catch (AmazonServiceException e) { log.error(e.getMessage(), e); } catch (AmazonClientException e) { log.error(e.getMessage(), e); } if (enableAvailabilityZonesRes != null) { log.info("Availability zones successfully added to LB " + loadBalancerName + ". Updated zone list: "); for (String zone : enableAvailabilityZonesRes.getAvailabilityZones()) { log.info(zone); } } else { log.error("Updating availability zones failed for LB " + loadBalancerName); } } public void modifyLBAttributes(String loadBalancerName, String region, boolean enableCrossZoneLbing, boolean enableConnDraining) { if (!enableCrossZoneLbing && !enableConnDraining) { log.info("No attributes specified to modify in the LB " + loadBalancerName); return; } ModifyLoadBalancerAttributesRequest modifyLBAttributesReq = new ModifyLoadBalancerAttributesRequest() .withLoadBalancerName(loadBalancerName); LoadBalancerAttributes modifiedLbAttributes = new LoadBalancerAttributes(); if (enableCrossZoneLbing) { modifiedLbAttributes.setCrossZoneLoadBalancing(new CrossZoneLoadBalancing().withEnabled(true)); } if (enableConnDraining) { modifiedLbAttributes.setConnectionDraining(new ConnectionDraining().withEnabled(true)); } modifyLBAttributesReq.setLoadBalancerAttributes(modifiedLbAttributes); elbClient.setEndpoint(String.format(Constants.ELB_ENDPOINT_URL_FORMAT, region)); ModifyLoadBalancerAttributesResult modifyLBAttributesRes = elbClient .modifyLoadBalancerAttributes(modifyLBAttributesReq); if (modifyLBAttributesRes != null) { log.info("Successfully enabled cross zone load balancing and connection draining for " + loadBalancerName); } else { log.error("Failed to enable cross zone load balancing and connection draining for " + loadBalancerName); } } public String getSslCertificateId() { return sslCertificateId; } public String getAppStickySessionCookie() { return appStickySessionCookie; } public Set<String> getInitialZones() { return initialZones; } public Set<String> getSubnetIds() { return subnetIds; } public String getLbScheme() { return lbScheme; } public Set<String> getVpcIds() { return vpcIds; } public String getLbSecurityGroupIdDefinedInConfiguration() { return lbSecurityGroupId; } }