/************************************************************************* * Copyright 2009-2015 Eucalyptus Systems, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need * additional information or have any questions. ************************************************************************/ package com.eucalyptus.loadbalancing.service; import static com.eucalyptus.loadbalancing.LoadBalancerZone.LoadBalancerZoneCoreView.name; import static com.eucalyptus.loadbalancing.LoadBalancerZone.LoadBalancerZoneCoreView.subnetId; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; import java.util.concurrent.Callable; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.persistence.EntityNotFoundException; import javax.persistence.OptimisticLockException; import com.eucalyptus.component.annotation.ComponentNamed; import com.eucalyptus.loadbalancing.*; import com.eucalyptus.loadbalancing.activities.LoadBalancerVersionException; import com.eucalyptus.loadbalancing.common.LoadBalancing; import com.eucalyptus.loadbalancing.workflow.LoadBalancingWorkflowException; import com.eucalyptus.system.Threads; import org.apache.log4j.Logger; import com.eucalyptus.auth.AuthException; import com.eucalyptus.auth.AuthQuotaException; import com.eucalyptus.auth.principal.AccountFullName; import com.eucalyptus.auth.principal.UserFullName; import com.eucalyptus.compute.common.ClusterInfoType; import com.eucalyptus.compute.common.InternetGatewayType; import com.eucalyptus.compute.common.RunningInstancesItemType; import com.eucalyptus.compute.common.SecurityGroupItemType; import com.eucalyptus.compute.common.SubnetType; import com.eucalyptus.compute.common.VpcType; import com.eucalyptus.context.Context; import com.eucalyptus.context.Contexts; import com.eucalyptus.entities.Entities; import com.eucalyptus.entities.TransactionException; import com.eucalyptus.entities.TransactionResource; import com.eucalyptus.loadbalancing.LoadBalancer; import com.eucalyptus.loadbalancing.LoadBalancer.LoadBalancerCoreView; import com.eucalyptus.loadbalancing.LoadBalancerBackendInstance.LoadBalancerBackendInstanceCoreView; import com.eucalyptus.loadbalancing.LoadBalancerBackendServerDescription; import com.eucalyptus.loadbalancing.LoadBalancerBackendServers; import com.eucalyptus.loadbalancing.LoadBalancerListener; import com.eucalyptus.loadbalancing.LoadBalancerListener.LoadBalancerListenerCoreView; import com.eucalyptus.loadbalancing.LoadBalancerListener.LoadBalancerListenerEntityTransform; import com.eucalyptus.loadbalancing.LoadBalancerListener.PROTOCOL; import com.eucalyptus.loadbalancing.LoadBalancerPolicyDescription.LoadBalancerPolicyDescriptionCoreView; import com.eucalyptus.loadbalancing.LoadBalancerSecurityGroup.LoadBalancerSecurityGroupCoreView; import com.eucalyptus.loadbalancing.LoadBalancerZone.LoadBalancerZoneCoreView; import com.eucalyptus.loadbalancing.LoadBalancers.DeploymentVersion; import com.eucalyptus.loadbalancing.LoadBalancers; import com.eucalyptus.loadbalancing.activities.EucalyptusActivityTasks; import com.eucalyptus.loadbalancing.common.LoadBalancingMetadatas; import com.eucalyptus.loadbalancing.common.msgs.AddTagsResponseType; import com.eucalyptus.loadbalancing.common.msgs.AddTagsType; import com.eucalyptus.loadbalancing.common.msgs.ApplySecurityGroupsToLoadBalancerResponseType; import com.eucalyptus.loadbalancing.common.msgs.ApplySecurityGroupsToLoadBalancerType; import com.eucalyptus.loadbalancing.common.msgs.AttachLoadBalancerToSubnetsResponseType; import com.eucalyptus.loadbalancing.common.msgs.AttachLoadBalancerToSubnetsType; import com.eucalyptus.loadbalancing.common.msgs.ConfigureHealthCheckResponseType; import com.eucalyptus.loadbalancing.common.msgs.ConfigureHealthCheckType; import com.eucalyptus.loadbalancing.common.msgs.CreateAppCookieStickinessPolicyResponseType; import com.eucalyptus.loadbalancing.common.msgs.CreateAppCookieStickinessPolicyType; import com.eucalyptus.loadbalancing.common.msgs.CreateLBCookieStickinessPolicyResponseType; import com.eucalyptus.loadbalancing.common.msgs.CreateLBCookieStickinessPolicyType; import com.eucalyptus.loadbalancing.common.msgs.CreateLoadBalancerListenersResponseType; import com.eucalyptus.loadbalancing.common.msgs.CreateLoadBalancerListenersType; import com.eucalyptus.loadbalancing.common.msgs.CreateLoadBalancerPolicyResponseType; import com.eucalyptus.loadbalancing.common.msgs.CreateLoadBalancerPolicyType; import com.eucalyptus.loadbalancing.common.msgs.CreateLoadBalancerResponseType; import com.eucalyptus.loadbalancing.common.msgs.CreateLoadBalancerType; import com.eucalyptus.loadbalancing.common.msgs.DeleteLoadBalancerListenersResponseType; import com.eucalyptus.loadbalancing.common.msgs.DeleteLoadBalancerListenersType; import com.eucalyptus.loadbalancing.common.msgs.DeleteLoadBalancerPolicyResponseType; import com.eucalyptus.loadbalancing.common.msgs.DeleteLoadBalancerPolicyType; import com.eucalyptus.loadbalancing.common.msgs.DeleteLoadBalancerResponseType; import com.eucalyptus.loadbalancing.common.msgs.DeleteLoadBalancerType; import com.eucalyptus.loadbalancing.common.msgs.DeregisterInstancesFromLoadBalancerResponseType; import com.eucalyptus.loadbalancing.common.msgs.DeregisterInstancesFromLoadBalancerType; import com.eucalyptus.loadbalancing.common.msgs.DescribeInstanceHealthResponseType; import com.eucalyptus.loadbalancing.common.msgs.DescribeInstanceHealthType; import com.eucalyptus.loadbalancing.common.msgs.DescribeLoadBalancerAttributesResponseType; import com.eucalyptus.loadbalancing.common.msgs.DescribeLoadBalancerAttributesType; import com.eucalyptus.loadbalancing.common.msgs.DescribeLoadBalancerPoliciesResponseType; import com.eucalyptus.loadbalancing.common.msgs.DescribeLoadBalancerPoliciesType; import com.eucalyptus.loadbalancing.common.msgs.DescribeLoadBalancerPolicyTypesResponseType; import com.eucalyptus.loadbalancing.common.msgs.DescribeLoadBalancerPolicyTypesType; import com.eucalyptus.loadbalancing.common.msgs.DescribeLoadBalancersResponseType; import com.eucalyptus.loadbalancing.common.msgs.DescribeLoadBalancersType; import com.eucalyptus.loadbalancing.common.msgs.DescribeTagsResponseType; import com.eucalyptus.loadbalancing.common.msgs.DescribeTagsType; import com.eucalyptus.loadbalancing.common.msgs.DetachLoadBalancerFromSubnetsResponseType; import com.eucalyptus.loadbalancing.common.msgs.DetachLoadBalancerFromSubnetsType; import com.eucalyptus.loadbalancing.common.msgs.DisableAvailabilityZonesForLoadBalancerResponseType; import com.eucalyptus.loadbalancing.common.msgs.DisableAvailabilityZonesForLoadBalancerType; import com.eucalyptus.loadbalancing.common.msgs.EnableAvailabilityZonesForLoadBalancerResponseType; import com.eucalyptus.loadbalancing.common.msgs.EnableAvailabilityZonesForLoadBalancerType; import com.eucalyptus.loadbalancing.common.msgs.AccessLog; import com.eucalyptus.loadbalancing.common.msgs.AppCookieStickinessPolicies; import com.eucalyptus.loadbalancing.common.msgs.AppCookieStickinessPolicy; import com.eucalyptus.loadbalancing.common.msgs.AvailabilityZones; import com.eucalyptus.loadbalancing.common.msgs.BackendServerDescription; import com.eucalyptus.loadbalancing.common.msgs.BackendServerDescriptions; import com.eucalyptus.loadbalancing.common.msgs.ConfigureHealthCheckResult; import com.eucalyptus.loadbalancing.common.msgs.ConnectionSettings; import com.eucalyptus.loadbalancing.common.msgs.CreateLoadBalancerResult; import com.eucalyptus.loadbalancing.common.msgs.CrossZoneLoadBalancing; import com.eucalyptus.loadbalancing.common.msgs.DeleteLoadBalancerResult; import com.eucalyptus.loadbalancing.common.msgs.DeregisterInstancesFromLoadBalancerResult; import com.eucalyptus.loadbalancing.common.msgs.DescribeInstanceHealthResult; import com.eucalyptus.loadbalancing.common.msgs.DescribeLoadBalancerPoliciesResult; import com.eucalyptus.loadbalancing.common.msgs.DescribeLoadBalancerPolicyTypesResult; import com.eucalyptus.loadbalancing.common.msgs.DescribeLoadBalancersResult; import com.eucalyptus.loadbalancing.common.msgs.DisableAvailabilityZonesForLoadBalancerResult; import com.eucalyptus.loadbalancing.common.msgs.EnableAvailabilityZonesForLoadBalancerResult; import com.eucalyptus.loadbalancing.common.msgs.HealthCheck; import com.eucalyptus.loadbalancing.common.msgs.Instance; import com.eucalyptus.loadbalancing.common.msgs.InstanceState; import com.eucalyptus.loadbalancing.common.msgs.InstanceStates; import com.eucalyptus.loadbalancing.common.msgs.Instances; import com.eucalyptus.loadbalancing.common.msgs.LBCookieStickinessPolicies; import com.eucalyptus.loadbalancing.common.msgs.LBCookieStickinessPolicy; import com.eucalyptus.loadbalancing.common.msgs.Listener; import com.eucalyptus.loadbalancing.common.msgs.ModifyLoadBalancerAttributesResponseType; import com.eucalyptus.loadbalancing.common.msgs.ModifyLoadBalancerAttributesType; import com.eucalyptus.loadbalancing.common.msgs.RegisterInstancesWithLoadBalancerResponseType; import com.eucalyptus.loadbalancing.common.msgs.RegisterInstancesWithLoadBalancerType; import com.eucalyptus.loadbalancing.common.msgs.RemoveTagsResponseType; import com.eucalyptus.loadbalancing.common.msgs.RemoveTagsType; import com.eucalyptus.loadbalancing.common.msgs.SetLoadBalancerListenerSSLCertificateResponseType; import com.eucalyptus.loadbalancing.common.msgs.SetLoadBalancerListenerSSLCertificateType; import com.eucalyptus.loadbalancing.common.msgs.SetLoadBalancerPoliciesForBackendServerResponseType; import com.eucalyptus.loadbalancing.common.msgs.SetLoadBalancerPoliciesForBackendServerType; import com.eucalyptus.loadbalancing.common.msgs.SetLoadBalancerPoliciesOfListenerResponseType; import com.eucalyptus.loadbalancing.common.msgs.SetLoadBalancerPoliciesOfListenerType; import com.eucalyptus.loadbalancing.common.msgs.ListenerDescription; import com.eucalyptus.loadbalancing.common.msgs.ListenerDescriptions; import com.eucalyptus.loadbalancing.common.msgs.LoadBalancerAttributes; import com.eucalyptus.loadbalancing.common.msgs.LoadBalancerDescription; import com.eucalyptus.loadbalancing.common.msgs.LoadBalancerDescriptions; import com.eucalyptus.loadbalancing.common.msgs.Policies; import com.eucalyptus.loadbalancing.common.msgs.PolicyAttribute; import com.eucalyptus.loadbalancing.common.msgs.PolicyDescription; import com.eucalyptus.loadbalancing.common.msgs.PolicyDescriptions; import com.eucalyptus.loadbalancing.common.msgs.PolicyNames; import com.eucalyptus.loadbalancing.common.msgs.PolicyTypeDescription; import com.eucalyptus.loadbalancing.common.msgs.PolicyTypeDescriptions; import com.eucalyptus.loadbalancing.common.msgs.SecurityGroups; import com.eucalyptus.loadbalancing.common.msgs.SetLoadBalancerListenerSSLCertificateResult; import com.eucalyptus.loadbalancing.common.msgs.SourceSecurityGroup; import com.eucalyptus.loadbalancing.common.msgs.Subnets; import com.eucalyptus.loadbalancing.common.msgs.Tag; import com.eucalyptus.loadbalancing.common.msgs.TagDescription; import com.eucalyptus.loadbalancing.common.msgs.TagDescriptions; import com.eucalyptus.loadbalancing.common.msgs.TagKeyOnly; import com.eucalyptus.loadbalancing.common.msgs.TagList; import com.eucalyptus.loadbalancing.workflow.LoadBalancingWorkflows; import com.eucalyptus.util.CollectionUtils; import com.eucalyptus.util.EucalyptusCloudException; import com.eucalyptus.util.Exceptions; import com.eucalyptus.util.RestrictedTypes; import com.eucalyptus.util.TypeMappers; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.base.Optional; import com.google.common.base.Predicates; import com.google.common.base.Strings; import com.google.common.base.Supplier; import com.google.common.base.Predicate; import com.google.common.collect.BiMap; import com.google.common.collect.Collections2; import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Ordering; import com.google.common.collect.Sets; import com.google.common.net.HostSpecifier; /** * @author Sang-Min Park */ @SuppressWarnings( "UnusedDeclaration" ) @ComponentNamed public class LoadBalancingService { private static Logger LOG = Logger.getLogger( LoadBalancingService.class ); private static final int MIN_HEALTHCHECK_INTERVAL_SEC = 5; public static final int MAX_HEALTHCHECK_INTERVAL_SEC = 120; private static final int MIN_HEALTHCHECK_THRESHOLDS = 2; private static final Set<String> reservedPrefixes = ImmutableSet.<String>builder().add("aws:").add("euca:").build(); public CreateLoadBalancerResponseType createLoadBalancer( final CreateLoadBalancerType request ) throws EucalyptusCloudException { final CreateLoadBalancerResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final UserFullName ownerFullName = ctx.getUserFullName( ); final String lbName = request.getLoadBalancerName( ); // verify loadbalancer name if( lbName == null || !HostSpecifier.isValid(String.format("%s.com", lbName)) || !lbName.matches("[a-zA-Z0-9-]{1,32}")){ throw new InvalidConfigurationRequestException("Invalid character found in the loadbalancer name"); } final Map<String,String> tags = Maps.newHashMap( ); if ( request.getTags( ) != null ) { for ( final Tag tag : request.getTags( ).getMember( ) ) { if ( tags.put( tag.getKey( ), Strings.nullToEmpty( tag.getValue( ) ) ) != null ) { throw new LoadBalancingClientException( "DuplicateTagKeys", "Duplicate tag key (" + tag.getKey( ) + ")" ); } } final int userTags = Iterables.size( Iterables.filter( tags.keySet( ), Predicates.not( isReservedTagPrefix( ) ) ) ); if ( tags.size( ) - userTags > 0 && !Contexts.lookup( ).isPrivileged( ) ) { throw new InvalidConfigurationRequestException("Invalid tag key (reserved prefix)"); } if ( userTags > LoadBalancingServiceProperties.getMaxTags( ) ) { throw Exceptions.toUndeclared( new LoadBalancingClientException( "TooManyTags", "Tag limit exceeded" ) ); } } final List<Listener> listeners = request.getListeners( ) != null ? request.getListeners( ).getMember( ) : Collections.<Listener>emptyList( ); LoadBalancers.validateListener( listeners ); // Check SSL Certificate Id before creating LB try { for(final Listener l : listeners){ if("HTTPS".equals(l.getProtocol().toUpperCase()) || "SSL".equals(l.getProtocol().toUpperCase())){ final String certArn = l.getSSLCertificateId(); if(certArn==null || certArn.length()<=0) throw new InvalidConfigurationRequestException("SSLCertificateId is required for HTTPS or SSL protocol"); LoadBalancers.checkSSLCertificate(ctx.getAccountNumber(), certArn); } } }catch(Exception ex){ if(! (ex instanceof LoadBalancingException)){ LOG.error("failed to check SSL certificate Id", ex); ex = new InternalFailure400Exception("failed to check SSL certificate Id", ex); } throw (LoadBalancingException) ex; } if ( request.getAvailabilityZones( ) != null && !request.getAvailabilityZones( ).getMember( ).isEmpty( ) ) { final Set<String> validZones = Sets.newHashSet( ); try { Iterables.addAll( validZones, Iterables.transform( EucalyptusActivityTasks.getInstance().describeAvailabilityZones(), ClusterInfoType.zoneName() ) ); } catch( Exception ex ){ throw new InternalFailure400Exception("Unable to verify the requested zones"); } for( final String zone : request.getAvailabilityZones( ).getMember( ) ) { if( !validZones.contains( zone ) ) { throw new InvalidConfigurationRequestException( "No cluster named " + zone + " is available" ); } } } String subnetVpcId = null; final Map<String,String> zoneToSubnetIdMap = Maps.newHashMap( ); if ( request.getSubnets( ) != null ) { final List<SubnetType> subnets = EucalyptusActivityTasks.getInstance( ).describeSubnets( request.getSubnets( ).getMember( ) ); if ( subnets.size( ) != request.getSubnets( ).getMember( ).size( ) ) { throw new LoadBalancingClientException( "SubnetNotFound", "Invalid subnet(s)" ); } for ( final SubnetType subnetType : subnets ) { if ( subnetVpcId == null ) { subnetVpcId = subnetType.getVpcId( ); } else if ( !subnetVpcId.equals( subnetType.getVpcId( ) ) ) { throw new InvalidConfigurationRequestException( "Subnets must belong to the same VPC" ); } if ( zoneToSubnetIdMap.put( subnetType.getAvailabilityZone( ), subnetType.getSubnetId( ) ) != null ) { throw new InvalidConfigurationRequestException( "Multiple subnets for zone ("+subnetType.getAvailabilityZone( )+")" ); } } } final AccountFullName accountFullName = ctx.getAccount( ); boolean defaultVpc = false; if ( subnetVpcId == null ) { // check for a default VPC final Optional<VpcType> vpcOptional = EucalyptusActivityTasks.getInstance( ).defaultVpc( accountFullName ); subnetVpcId = vpcOptional.transform( VpcType.id( ) ).orNull( ); defaultVpc = subnetVpcId != null; } final String vpcId = subnetVpcId; if ( defaultVpc && zoneToSubnetIdMap.isEmpty( ) && request.getAvailabilityZones( ) != null ) { final List<SubnetType> subnets = EucalyptusActivityTasks.getInstance( ).describeSubnetsByZone( vpcId, true, request.getAvailabilityZones( ).getMember( ) ); CollectionUtils.putAll( subnets, zoneToSubnetIdMap, SubnetType.zone( ), SubnetType.id( ) ); if ( request.getAvailabilityZones( ).getMember( ).size( ) != zoneToSubnetIdMap.size( ) ) { throw new InvalidConfigurationRequestException( "Default subnet not found for zone(s)" ); } } LoadBalancer.Scheme reqScheme = LoadBalancer.Scheme.fromString( request.getScheme( ) ).orNull( ); if ( reqScheme == null && !Strings.isNullOrEmpty( request.getScheme( ) ) ) { throw new InvalidConfigurationRequestException("Invalid scheme ("+request.getScheme( )+")"); } if ( reqScheme != null && vpcId == null ) { if ( reqScheme != LoadBalancer.Scheme.InternetFacing ) { throw new InvalidConfigurationRequestException( "Scheme (" + request.getScheme() + ") should not be specified for EC2-Classic platform" ); } else { reqScheme = null; // ignore internet-facing scheme for non-vpc ELB } } final LoadBalancer.Scheme scheme = reqScheme; if ( vpcId != null && scheme != LoadBalancer.Scheme.Internal ) try { final List<InternetGatewayType> internetGateways = EucalyptusActivityTasks.getInstance( ).describeInternetGateways( Collections.singleton( vpcId ) ); if ( internetGateways.isEmpty( ) ) { throw new LoadBalancingClientException( "InvalidSubnet", "VPC "+vpcId+" has no internet gateway" ); } } catch ( final LoadBalancingException e ) { throw e; } catch ( final Exception e ) { LOG.error( "Error checking internet gateway", e ); throw new InternalFailureException( "Unable to verify VPC configuration" ); } final Set<String> securityGroupIds = Sets.newHashSet( ); if ( request.getSecurityGroups( ) != null ) { securityGroupIds.addAll( request.getSecurityGroups( ).getMember( ) ); } else if ( vpcId != null && request.getSubnets() != null ) { // for default VPC (where availability-zone is specified), a group is created/discovered later final List<SecurityGroupItemType> groups = EucalyptusActivityTasks.getInstance( ) .describeUserSecurityGroupsByName( accountFullName, vpcId, "default" ); if ( groups.isEmpty( ) ) { throw new InvalidConfigurationRequestException( "Default security group not found for VPC " + vpcId ); } securityGroupIds.add( groups.get( 0 ).getGroupId( ) ); } if ( !securityGroupIds.isEmpty( ) && vpcId == null ) { throw new InvalidConfigurationRequestException("Security groups should not be specified for EC2-Classic platform"); } final List<SecurityGroupItemType> groups = securityGroupIds.isEmpty() ? Collections.<SecurityGroupItemType>emptyList( ) : EucalyptusActivityTasks.getInstance().describeUserSecurityGroupsById( accountFullName, vpcId, securityGroupIds ); if ( groups.size( ) != securityGroupIds.size( ) ) { throw new LoadBalancingClientException( "InvalidSecurityGroup", "Invalid security group(s)" ); } final Collection<String> zones = Lists.<String>newArrayList( ); if ( request.getAvailabilityZones( ) != null ) zones.addAll( request.getAvailabilityZones( ).getMember( ) ); if ( zones.isEmpty( ) ) zones.addAll( zoneToSubnetIdMap.keySet( ) ); if ( !zoneToSubnetIdMap.isEmpty( ) && !zoneToSubnetIdMap.keySet( ).equals( Sets.newHashSet( zones ) ) ) { throw new InvalidConfigurationRequestException("Availability zones and subnets are inconsistent"); } final Supplier<LoadBalancer> allocator = new Supplier<LoadBalancer>() { @Override public LoadBalancer get() { try { final Map<String,String> securityGroupIdsToNames = CollectionUtils.putAll( groups, Maps.<String,String>newHashMap( ), SecurityGroupItemType.groupId( ), SecurityGroupItemType.groupName( ) ); return LoadBalancers.addLoadbalancer( ownerFullName, lbName, vpcId, scheme, securityGroupIdsToNames, tags ); } catch ( LoadBalancingException e ) { throw Exceptions.toUndeclared( e ); } } }; final LoadBalancer lb; try { lb = LoadBalancingMetadatas.allocateUnitlessResource( allocator ); } catch ( Exception e ) { throw handleException( e ); } Function<String, Boolean> rollback = new Function<String, Boolean>(){ @Override public Boolean apply(String lbName){ try{ LoadBalancers.unsetForeignKeys(ctx, lbName); }catch(final Exception ex){ LOG.warn("unable to unset foreign keys", ex); } try{ LoadBalancers.removeZone(lbName, ctx, zones); }catch(final Exception ex){ LOG.error("unable to delete availability zones during rollback", ex); } try{ LoadBalancers.deleteLoadbalancer(ownerFullName, lbName); }catch(LoadBalancingException ex){ LOG.error("failed to rollback the loadbalancer: " + lbName, ex); return false; } return true; } }; Entities.evictCache( LoadBalancer.class ); try { LoadBalancingWorkflows.createLoadBalancerSync(ctx.getAccountNumber(), lbName, Lists.newArrayList(zones)); if( !listeners.isEmpty( ) ){ LoadBalancers.createLoadbalancerListener(lbName, ctx, Lists.newArrayList(listeners)); if (! LoadBalancingWorkflows.createListenersSync(ctx.getAccountNumber(), lbName, Lists.newArrayList(listeners))) { throw new InternalFailure400Exception("Workflow for creating listeners has failed"); } } if ( !zones.isEmpty()) { if (! LoadBalancingWorkflows.enableZonesSync(ctx.getAccountNumber(), lbName, Lists.newArrayList(zones), zoneToSubnetIdMap)) { throw new InternalFailure400Exception("Workflow for enabling ELB availablity zone has failed"); } } LoadBalancingWorkflows.runInstanceStatusPolling(ctx.getAccountNumber(), lbName); LoadBalancingWorkflows.runCloudWatchPutMetric(ctx.getAccountNumber(), lbName); LoadBalancingWorkflows.runUpdateLoadBalancer(ctx.getAccountNumber(), lbName); } catch(final LoadBalancingWorkflowException ex) { rollback.apply(lbName); final int statusCode = ex.getStatusCode(); final String reason = ex.getMessage(); if (statusCode == 400) { final String errorMessage = reason!=null ? "Failed to create loadbalancer: " + reason : "Failed to create loadbalancer: internal error"; throw new InternalFailure400Exception(errorMessage); } else if (reason != null) { throw new InternalFailureException("Failed to create loadbalancer: " + reason); } else { throw new InternalFailureException("Failed to create loadbalancer: internal error"); } } catch(final LoadBalancingException ex) { rollback.apply(lbName); throw ex; } catch(final Exception e) { rollback.apply(lbName); LOG.error( "Error creating the loadbalancer: " + e.getMessage(), e ); final String reason = e.getCause()!=null && e.getCause().getMessage()!=null ? e.getMessage() : "internal error"; throw new InternalFailure400Exception(String.format("Failed to create loadbalancer: %s", reason), e ); } final CreateLoadBalancerResult result = new CreateLoadBalancerResult( ); result.setDnsName( LoadBalancers.getLoadBalancerDnsName( lb ) ); reply.setCreateLoadBalancerResult( result ); reply.set_return( true ); return reply; } public DescribeLoadBalancersResponseType describeLoadBalancers(DescribeLoadBalancersType request) throws EucalyptusCloudException { DescribeLoadBalancersResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String accountNumber = ctx.getAccount().getAccountNumber(); final Set<String> requestedNames = Sets.newHashSet( ); if ( request.getLoadBalancerNames( ) != null ) { requestedNames.addAll( request.getLoadBalancerNames().getMember() ); } final boolean showAll = requestedNames.remove( "verbose" ) && ctx.isAdministrator(); final Function<Set<String>, Set<LoadBalancer>> lookupAccountLBs = new Function<Set<String>, Set<LoadBalancer>>( ) { @Override public Set<LoadBalancer> apply( final Set<String> identifiers ) { try { final Predicate<? super LoadBalancer> requestedAndAccessible = LoadBalancingMetadatas.filteringFor( LoadBalancer.class ) .byId( identifiers ) .byPrivileges( ) .buildPredicate( ); final LoadBalancer example = showAll ? LoadBalancer.named( null, null ) : LoadBalancer.namedByAccountId( accountNumber, null ); final List<LoadBalancer> lbs = Entities.query( example, true ); return Sets.newHashSet( Iterables.filter( lbs, requestedAndAccessible ) ); } catch ( EntityNotFoundException e ) { Entities.evictCache( LoadBalancer.class ); throw new OptimisticLockException( "Error loading load balancers", e ); } } }; final Set<LoadBalancer> allowedLBs = Entities.asTransaction( LoadBalancer.class, lookupAccountLBs ).apply( requestedNames ); final Function<Set<LoadBalancer>, Set<LoadBalancerDescription>> lookupLBDescriptions = new Function<Set<LoadBalancer>, Set<LoadBalancerDescription>> () { public Set<LoadBalancerDescription> apply (final Set<LoadBalancer> input){ final Set<LoadBalancerDescription> descs = Sets.newHashSet(); for (final LoadBalancer lb : input){ LoadBalancerDescription desc = new LoadBalancerDescription(); if(lb==null) // loadbalancer not found continue; final String lbName = lb.getDisplayName(); desc.setLoadBalancerName(lbName); desc.setCreatedTime(lb.getCreationTimestamp()); // dns name desc.setDnsName( LoadBalancers.getLoadBalancerDnsName( lb ) ); // instances if(lb.getBackendInstances().size()>0){ desc.setInstances(new Instances()); Iterables.addAll( desc.getInstances().getMember( ), Iterables.transform( lb.getBackendInstances(), Functions.compose( Instance.instance( ), LoadBalancerBackendInstance.LoadBalancerBackendInstanceCoreView.instanceId( ) ) ) ); } // availability zones final List<String> subnetIds = Lists.newArrayList( ); if( !lb.getZones().isEmpty( ) ){ desc.setAvailabilityZones(new AvailabilityZones()); final List<LoadBalancerZoneCoreView> currentZones = Lists.newArrayList(Iterables.filter(lb.getZones(), new Predicate<LoadBalancerZoneCoreView>(){ @Override public boolean apply( LoadBalancerZoneCoreView arg0 ) { return arg0.getState().equals(LoadBalancerZone.STATE.InService); } })); Iterables.addAll( desc.getAvailabilityZones().getMember( ), Iterables.transform( currentZones, LoadBalancerZoneCoreView.name( ) ) ); Iterables.addAll( subnetIds, Iterables.filter( Iterables.transform( currentZones, LoadBalancerZoneCoreView.subnetId() ), Predicates.notNull() ) ); } // subnets if ( !subnetIds.isEmpty( ) ) { desc.setSubnets( new Subnets( subnetIds ) ); } desc.setVpcId( lb.getVpcId( ) ); // listeners if(lb.getListeners().size()>0){ desc.setListenerDescriptions(new ListenerDescriptions()); desc.getListenerDescriptions().setMember(new ArrayList<>( Collections2.transform(lb.getListeners(), new Function<LoadBalancerListenerCoreView, ListenerDescription>(){ @Override public ListenerDescription apply(final LoadBalancerListenerCoreView input){ ListenerDescription desc = new ListenerDescription(); Listener listener = new Listener(); listener.setLoadBalancerPort(input.getLoadbalancerPort()); listener.setInstancePort(input.getInstancePort()); if(input.getInstanceProtocol() != PROTOCOL.NONE) listener.setInstanceProtocol(input.getInstanceProtocol().name()); listener.setProtocol(input.getProtocol().name()); if(input.getCertificateId()!=null) listener.setSSLCertificateId(input.getCertificateId()); desc.setListener(listener); final LoadBalancerListener lbListener = LoadBalancerListenerEntityTransform.INSTANCE.apply(input); final PolicyNames pnames = new PolicyNames(); pnames.setMember(new ArrayList<>(Lists.transform(lbListener.getPolicies(), new Function<LoadBalancerPolicyDescriptionCoreView, String>(){ @Override public String apply( LoadBalancerPolicyDescriptionCoreView arg0) { return arg0.getPolicyName(); } }))); desc.setPolicyNames(pnames); return desc; } }))); } /// health check try{ int interval = lb.getHealthCheckInterval(); String target = lb.getHealthCheckTarget(); int timeout = lb.getHealthCheckTimeout(); int healthyThresholds = lb.getHealthyThreshold(); int unhealthyThresholds = lb.getHealthCheckUnhealthyThreshold(); final HealthCheck hc = new HealthCheck(); hc.setInterval(interval); hc.setHealthyThreshold(healthyThresholds); hc.setTarget(target); hc.setTimeout(timeout); hc.setUnhealthyThreshold(unhealthyThresholds); desc.setHealthCheck(hc); } catch(Exception ex){ } /// backend server description try{ final List<LoadBalancerBackendServerDescription> backendServers = LoadBalancerBackendServers.getLoadBalancerBackendServerDescription(lb); final List<BackendServerDescription> backendDescription = Lists.newArrayList(); for(final LoadBalancerBackendServerDescription server : backendServers){ final BackendServerDescription serverDesc = new BackendServerDescription(); serverDesc.setInstancePort(server.getInstancePort()); final PolicyNames polNames = new PolicyNames(); polNames.setMember(new ArrayList<String>(Lists.transform(server.getPolicyDescriptions(), new Function<LoadBalancerPolicyDescriptionCoreView, String>() { @Override public String apply(LoadBalancerPolicyDescriptionCoreView arg0) { return arg0.getPolicyName(); } }))); serverDesc.setPolicyNames(polNames); backendDescription.add(serverDesc); } final BackendServerDescriptions backendDescs = new BackendServerDescriptions(); backendDescs.setMember((ArrayList<BackendServerDescription>) backendDescription); desc.setBackendServerDescriptions(backendDescs); }catch(final Exception ex){ LOG.error("Failed to load backend server description", ex); } /// source security group try{ LoadBalancerSecurityGroupCoreView group = lb.getGroup(); if(group!=null){ desc.setSourceSecurityGroup( new SourceSecurityGroup( group.getGroupOwnerAccountId(), group.getName() ) ); } }catch(Exception ex){ } if ( !lb.getCoreView( ).getSecurityGroupIdsToNames( ).isEmpty() ) { desc.setSecurityGroups( new SecurityGroups( lb.getCoreView( ).getSecurityGroupIdsToNames( ).keySet( ) ) ); if ( desc.getSourceSecurityGroup( ) == null ) { desc.setSourceSecurityGroup( new SourceSecurityGroup( lb.getOwnerAccountNumber( ), Iterables.get( lb.getCoreView().getSecurityGroupIdsToNames().values(), 0 ) ) ); } } // policies try{ final List<LoadBalancerPolicyDescription> lbPolicies = LoadBalancerPolicies.getLoadBalancerPolicyDescription( lb ); final ArrayList<AppCookieStickinessPolicy> appCookiePolicies = Lists.newArrayList(); final ArrayList<LBCookieStickinessPolicy> lbCookiePolicies = Lists.newArrayList(); final ArrayList<String> otherPolicies = Lists.newArrayList(); for(final LoadBalancerPolicyDescription policy : lbPolicies){ if("LBCookieStickinessPolicyType".equals(policy.getPolicyTypeName())){ final LBCookieStickinessPolicy lbp = new LBCookieStickinessPolicy(); lbp.setPolicyName(policy.getPolicyName()); final List<LoadBalancerPolicyAttributeDescription> attrs = policy.findAttributeDescription("CookieExpirationPeriod"); if ( ! attrs.isEmpty()) lbp.setCookieExpirationPeriod(Long.parseLong(attrs.get(0).getAttributeValue())); lbCookiePolicies.add(lbp); }else if("AppCookieStickinessPolicyType".equals(policy.getPolicyTypeName())){ final AppCookieStickinessPolicy app = new AppCookieStickinessPolicy(); app.setPolicyName(policy.getPolicyName()); final List<LoadBalancerPolicyAttributeDescription> attrs = policy.findAttributeDescription("CookieName"); if (! attrs.isEmpty()) app.setCookieName(attrs.get(0).getAttributeValue()); appCookiePolicies.add(app); } else otherPolicies.add(policy.getPolicyName()); } final Policies p = new Policies(); final LBCookieStickinessPolicies lbp = new LBCookieStickinessPolicies(); lbp.setMember(lbCookiePolicies); final AppCookieStickinessPolicies app = new AppCookieStickinessPolicies(); app.setMember(appCookiePolicies); final PolicyNames other = new PolicyNames(); other.setMember(otherPolicies); p.setAppCookieStickinessPolicies(app); p.setLbCookieStickinessPolicies(lbp); p.setOtherPolicies(other); desc.setPolicies(p); desc.setScheme( Objects.toString( lb.getScheme( ), "internet-facing" ) ); } catch(final Exception ex){ LOG.error("Failed to retrieve policies", ex); } descs.add(desc); } return descs; } }; Set<LoadBalancerDescription> descs = lookupLBDescriptions.apply(allowedLBs); DescribeLoadBalancersResult descResult = new DescribeLoadBalancersResult(); LoadBalancerDescriptions lbDescs = new LoadBalancerDescriptions(); lbDescs.setMember(new ArrayList<>(descs)); descResult.setLoadBalancerDescriptions(lbDescs); reply.setDescribeLoadBalancersResult(descResult); reply.set_return(true); return reply; } public DeleteLoadBalancerResponseType deleteLoadBalancer( DeleteLoadBalancerType request ) throws EucalyptusCloudException { DeleteLoadBalancerResponseType reply = request.getReply(); final String candidateLB = request.getLoadBalancerName(); final Context ctx = Contexts.lookup(); Function<String, LoadBalancer> findLoadBalancer = new Function<String, LoadBalancer>(){ @Override @Nullable public LoadBalancer apply(@Nullable String lbName) { try{ return LoadBalancers.getLoadbalancer(ctx, lbName); }catch(NoSuchElementException ex){ if(ctx.isAdministrator()){ try{ return LoadBalancers.getLoadBalancerByDnsName(lbName); }catch(Exception ex2){ if(ex2 instanceof NoSuchElementException) throw Exceptions.toUndeclared(new LoadBalancingException("Unable to find the loadbalancer (use DNS name if you are an administrator)")); throw Exceptions.toUndeclared(ex2); } } throw ex; } } }; LoadBalancer lb = null; try { if ( candidateLB != null ) { String lbToDelete = null; try { lb = findLoadBalancer.apply(candidateLB); lbToDelete = lb.getDisplayName(); } catch ( NoSuchElementException ex ) { } catch ( Exception ex){ if(ex.getCause() != null && ex.getCause() instanceof LoadBalancingException) throw (LoadBalancingException) ex.getCause(); else throw ex; } //IAM Support for deleting load balancers if (lb != null && ! LoadBalancingMetadatas.filterPrivileged().apply( lb )) throw new AccessPointNotFoundException(); List<Integer> ports = null; if ( lb != null ) { Collection<LoadBalancerListenerCoreView> listeners = lb.getListeners(); ports = Lists.newArrayList( Collections2.transform( listeners, new Function<LoadBalancerListenerCoreView, Integer>() { @Override public Integer apply( @Nullable LoadBalancerListenerCoreView arg0 ) { return arg0.getLoadbalancerPort(); } } ) ); if (!LoadBalancingWorkflows.deleteListenersSync(lb.getOwnerAccountNumber(), lbToDelete, Lists.newArrayList(ports))) { throw new Exception("Workflow for deleting listeners has failed"); } else if(!LoadBalancingWorkflows.deleteLoadBalancerSync(lb.getOwnerAccountNumber(), lbToDelete)) { throw new Exception("Workflow for deleting loadbalancer has failed"); } else { /// perhaps these workflows should be stopped in the clean-up workflow LoadBalancingWorkflows.cancelInstanceStatusPolling(lb.getOwnerAccountNumber(), lbToDelete); LoadBalancingWorkflows.cancelCloudWatchPutMetric(lb.getOwnerAccountNumber(), lbToDelete); LoadBalancingWorkflows.cancelUpdateLoadBalancer(lb.getOwnerAccountNumber(), lbToDelete); LoadBalancers.deleteLoadbalancer(UserFullName.getInstanceForAccount(lb.getOwnerAccountNumber(),lb.getOwnerUserId()), lbToDelete); } } } }catch (LoadBalancingException e){ throw new InternalFailure400Exception(e.getMessage()); }catch ( Exception e ) { // success if the lb is not found in the system if ( !(e.getCause() instanceof NoSuchElementException) ) { LOG.error( "Error deleting the loadbalancer: " + e.getMessage(), e ); final String reason = "internal error"; throw new InternalFailure400Exception( String.format("Failed to delete the loadbalancer: %s", reason), e ); } } DeleteLoadBalancerResult result = new DeleteLoadBalancerResult(); reply.setDeleteLoadBalancerResult( result ); reply.set_return( true ); return reply; } public CreateLoadBalancerListenersResponseType createLoadBalancerListeners(CreateLoadBalancerListenersType request) throws EucalyptusCloudException { final CreateLoadBalancerListenersResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String lbName = request.getLoadBalancerName(); final List<Listener> listeners = request.getListeners().getMember(); LoadBalancer lb = null; try{ lb = LoadBalancers.getLoadbalancer(ctx, lbName); }catch(Exception ex){ throw new AccessPointNotFoundException(); } //IAM support to restricted lb modification if( !LoadBalancingMetadatas.filterPrivileged().apply(lb)) { throw new AccessPointNotFoundException(); } if(listeners!=null) LoadBalancers.validateListener(lb, listeners); try{ for(final Listener l : listeners){ if("HTTPS".equals(l.getProtocol().toUpperCase()) || "SSL".equals(l.getProtocol().toUpperCase())){ final String certArn = l.getSSLCertificateId(); if(certArn==null || certArn.length()<=0) throw new InvalidConfigurationRequestException("SSLCertificateId is required for HTTPS or SSL protocol"); LoadBalancers.checkSSLCertificate(ctx.getAccountNumber(), certArn); } } }catch(Exception ex){ if(! (ex instanceof LoadBalancingException)){ LOG.error("failed to check SSL certificate Id", ex); ex = new InternalFailure400Exception("failed to check SSL certificate Id", ex); } throw (LoadBalancingException) ex; } LoadBalancers.checkWorkerCertificateExpiration(lb); try{ LoadBalancers.createLoadbalancerListener(lbName, ctx, listeners); }catch(final LoadBalancingException ex){ throw ex; }catch(final Exception e){ final String reason = e.getCause()!=null && e.getCause().getMessage()!=null ? e.getMessage() : "internal error"; throw new InternalFailure400Exception(String.format("Failed to create listener: %s", reason), e ); } try{ if(! LoadBalancingWorkflows.createListenersSync(ctx.getAccountNumber(), lbName, listeners)) throw new Exception("Workflow for creating listeners failed"); }catch(final Exception e){ try ( final TransactionResource db = Entities.transactionFor( LoadBalancerListener.class ) ) { for (final Listener l : listeners){ try{ final LoadBalancerListener exist = Entities.uniqueResult(LoadBalancerListener.named(lb, l.getLoadBalancerPort())); Entities.delete(exist); }catch(final Exception ex) { ; } } } final String reason = e.getCause()!=null && e.getCause().getMessage()!=null ? e.getMessage() : "internal error"; throw new InternalFailure400Exception(String.format("Failed to create listener: %s", reason), e ); } LoadBalancingWorkflows.updateLoadBalancer(ctx.getAccountNumber(), lbName); reply.set_return(true); return reply; } public DeleteLoadBalancerListenersResponseType deleteLoadBalancerListeners(DeleteLoadBalancerListenersType request) throws EucalyptusCloudException { final DeleteLoadBalancerListenersResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String lbName = request.getLoadBalancerName(); final Collection<Integer> listenerPorts; try{ listenerPorts = Collections2.transform( request.getLoadBalancerPorts().getMember(), new Function<String, Integer>(){ @Override public Integer apply(final String input){ return new Integer(input); } }); }catch(Exception ex){ throw new InvalidConfigurationRequestException("Invalid port number"); } final LoadBalancer lb; try{ lb = LoadBalancers.getLoadbalancer(ctx, lbName); }catch(NoSuchElementException ex){ throw new AccessPointNotFoundException(); }catch(Exception ex){ LOG.error("failed to query loadbalancer due to unknown reason", ex); final String reason = ex.getCause()!=null && ex.getCause().getMessage()!=null ? ex.getMessage() : "internal error"; throw new InternalFailure400Exception( String.format("Failed to delete the listener: %s", reason), ex ); } //IAM support to restricted lb modification if( !LoadBalancingMetadatas.filterPrivileged().apply(lb) ) { throw new AccessPointNotFoundException(); } final Function<Void, Collection<Integer>> filter = new Function<Void, Collection<Integer>>(){ @Override public Collection<Integer> apply(Void v){ final Collection<Integer> filtered = Sets.newHashSet(); for(Integer port : listenerPorts){ final LoadBalancerListenerCoreView found = lb.findListener(port); if(found!=null) filtered.add(port); } return filtered; } }; final Collection<Integer> toDelete = Entities.asTransaction(LoadBalancer.class, filter).apply(null); final Predicate<Collection<Integer>> remover = new Predicate<Collection<Integer>>(){ @Override public boolean apply(Collection<Integer> listeners){ for(Integer port : listeners){ try{ final LoadBalancerListener exist = Entities.uniqueResult(LoadBalancerListener.named(lb, port)); Entities.delete(exist); }catch(NoSuchElementException ex){ }catch(Exception ex){ LOG.error("Failed to delete the listener", ex); throw Exceptions.toUndeclared(ex); } } return true; } }; try{ if(! LoadBalancingWorkflows.deleteListenersSync(ctx.getAccountNumber(), lbName, Lists.newArrayList(toDelete))) throw new Exception("Workflow for deleting listeners has failed"); }catch(final Exception e){ final String reason = e.getCause()!=null && e.getCause().getMessage()!=null ? e.getCause().getMessage() : "internal error"; throw new InternalFailure400Exception(String.format("Failed to delete listener: %s",reason),e ); } try{ Entities.asTransaction(LoadBalancerListener.class, remover).apply(toDelete); }catch(final Exception ex){ final String reason = ex.getCause()!=null && ex.getCause().getMessage()!=null ? ex.getCause().getMessage() : "internal error"; throw new InternalFailure400Exception(String.format("Failed to delete listener: %s",reason), ex ); } LoadBalancingWorkflows.updateLoadBalancer(ctx.getAccountNumber(), lbName); reply.set_return(true); return reply; } public RegisterInstancesWithLoadBalancerResponseType registerInstancesWithLoadBalancer( final RegisterInstancesWithLoadBalancerType request ) throws EucalyptusCloudException { final RegisterInstancesWithLoadBalancerResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final UserFullName ownerFullName = ctx.getUserFullName( ); final String lbName = request.getLoadBalancerName( ); final Collection<Instance> instances = request.getInstances( ).getMember( ); final LoadBalancer lb; try{ lb = LoadBalancers.getLoadbalancer( ctx, lbName ); }catch(final Exception ex){ throw new AccessPointNotFoundException(); } if( !LoadBalancingMetadatas.filterPrivileged( ).apply( lb ) ) { // IAM policy restriction throw new AccessPointNotFoundException( ); } final Set<String> backends = Sets.newHashSet( Iterables.transform( lb.getBackendInstances( ), LoadBalancerBackendInstanceCoreView.instanceId( ) ) ); /*********** Verify requests ************/ if ( lb.getVpcId( ) != null ) { final List<RunningInstancesItemType> instanceItems = EucalyptusActivityTasks.getInstance( ).describeUserInstances(ctx.getAccountNumber( ), Lists.newArrayList( Iterables.transform( instances, Instance.instanceId()))); for ( final RunningInstancesItemType instanceItem : instanceItems ) { if ( !lb.getVpcId( ).equals( instanceItem.getVpcId( ) ) ) { throw new InvalidConfigurationRequestException( "Invalid instance(s) for load balancer." ); } } } final Collection<LoadBalancerZoneCoreView> enabledZones = Collections2.filter(lb.getZones(), new Predicate<LoadBalancerZoneCoreView>() { @Override public boolean apply(LoadBalancerZoneCoreView arg0) { return LoadBalancerZone.STATE.InService.equals(arg0.getState()); } }); final Set<String> lbZones = Sets.newHashSet(Collections2.transform(enabledZones, new Function<LoadBalancerZoneCoreView, String>(){ @Override public String apply(LoadBalancerZoneCoreView arg0) { return arg0.getName(); } })); final String acctNumber = ctx.getAccountNumber(); final List<String> requestedInstanceIds = Lists.newArrayList(Collections2.transform(instances, new Function<Instance,String>() { @Override public String apply(Instance arg0) { return arg0.getInstanceId(); } })); List<RunningInstancesItemType> eucaInstances = Lists.newArrayList(); try{ eucaInstances = EucalyptusActivityTasks.getInstance().describeUserInstances(acctNumber, requestedInstanceIds); }catch(final Exception ex) { throw new InvalidConfigurationRequestException( "Failed to look up requested instances" ); } for(final RunningInstancesItemType instance : eucaInstances) { if(! lbZones.contains(instance.getPlacement())) { throw new InvalidConfigurationRequestException("Instance "+instance.getInstanceId()+"'s availaibility zone is not enabled for the loadbalancer"); } } /*********** END Verify requests ************/ // when there's any new instance in the request if (instances.stream().anyMatch( vm -> !backends.contains(vm.getInstanceId()))) { final Predicate<LoadBalancer> creator = new Predicate<LoadBalancer>() { @Override public boolean apply(LoadBalancer lb) { for (Instance vm : instances) { if (lb.hasBackendInstance(vm.getInstanceId())) continue; // the vm instance is already registered try { final LoadBalancerBackendInstance beInstance = LoadBalancerBackendInstance.newInstance(ownerFullName, lb, vm.getInstanceId()); final LoadBalancerBackendInstanceStates registration = LoadBalancerBackendInstanceStates.InitialRegistration; beInstance.setState(registration.getState()); beInstance.setReasonCode(registration.getReasonCode()); beInstance.setDescription(registration.getDescription()); Entities.persist(beInstance); } catch (final LoadBalancingException ex) { throw Exceptions.toUndeclared(ex); } } return true; } }; try { Entities.asTransaction(LoadBalancerBackendInstance.class, creator).apply(lb); Iterables.addAll(backends, Iterables.transform(instances, Instance.instanceId())); } catch (Exception ex) { throw handleException(ex); } final String accountNumber = ctx.getAccountNumber(); LoadBalancingWorkflows.updateLoadBalancer(accountNumber, lbName); Threads.enqueue(LoadBalancing.class, LoadBalancingService.class, new Callable<Boolean>() { @Override public Boolean call() throws Exception { try { Thread.sleep(2000); // delay polling signal by a few sec. Best-effort approach to poll InService instance after registration } catch (final Exception ex) { ; } LoadBalancingWorkflows.pollInstanceStatus(accountNumber, lbName); return true; } }); } final Instances returnInstances = new Instances(); Iterables.addAll( returnInstances.getMember( ), Iterables.transform( backends, Instance.instance( ) ) ); reply.getRegisterInstancesWithLoadBalancerResult( ).setInstances( returnInstances ); return reply; } public DeregisterInstancesFromLoadBalancerResponseType deregisterInstancesFromLoadBalancer(DeregisterInstancesFromLoadBalancerType request) throws EucalyptusCloudException { DeregisterInstancesFromLoadBalancerResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String lbName = request.getLoadBalancerName(); final Collection<Instance> instances = request.getInstances().getMember(); LoadBalancer lb; try{ lb = LoadBalancers.getLoadbalancer(ctx, lbName); }catch(Exception ex){ throw new AccessPointNotFoundException(); } if( !LoadBalancingMetadatas.filterPrivileged().apply(lb) ) { // IAM policy restriction throw new AccessPointNotFoundException(); } final List<LoadBalancerBackendInstanceCoreView> allInstances = Lists.newArrayList(lb.getBackendInstances()); final Function<LoadBalancer, Collection<LoadBalancerBackendInstanceCoreView>> filter = new Function<LoadBalancer, Collection<LoadBalancerBackendInstanceCoreView>>(){ @Override public Collection<LoadBalancerBackendInstanceCoreView> apply(LoadBalancer lb){ Collection<LoadBalancerBackendInstanceCoreView> filtered = Sets.newHashSet(); for(final LoadBalancerBackendInstanceCoreView be: lb.getBackendInstances()){ for(Instance inst : instances){ if(be.getInstanceId()!=null && be.getInstanceId().equals(inst.getInstanceId())){ filtered.add(be); break; } } } return filtered; } }; final Collection<LoadBalancerBackendInstanceCoreView> instancesToRemove = Entities.asTransaction(LoadBalancer.class, filter).apply(lb); if(instancesToRemove==null){ reply.set_return(false); return reply; } final Predicate<Void> remover = new Predicate<Void>(){ @Override public boolean apply(Void v){ for(final LoadBalancerBackendInstanceCoreView instanceView : instancesToRemove){ final LoadBalancerBackendInstance sample = LoadBalancerBackendInstance.LoadBalancerBackendInstanceEntityTransform.INSTANCE.apply(instanceView); LoadBalancerBackendInstance toDelete; try{ toDelete = Entities.uniqueResult(sample); }catch(NoSuchElementException ex){ toDelete=null; throw Exceptions.toUndeclared(new InvalidEndPointException()); }catch(Exception ex){ LOG.error("Can't query loadbalancer backend instance for "+instanceView.getInstanceId(), ex); toDelete=null; } if(toDelete==null) continue; Entities.delete(toDelete); } return true; } }; final Function<Void, ArrayList<Instance>> finder = new Function<Void, ArrayList<Instance>>(){ @Override public ArrayList<Instance> apply(Void v){ LoadBalancer lb; try{ lb= LoadBalancers.getLoadbalancer(ctx, lbName); }catch(Exception ex){ LOG.warn("No loadbalancer is found with name="+lbName); return Lists.newArrayList(); } Entities.refresh(lb); ArrayList<Instance> result = new ArrayList<>(Collections2.transform(lb.getBackendInstances(), new Function<LoadBalancerBackendInstanceCoreView, Instance>(){ @Override public Instance apply(final LoadBalancerBackendInstanceCoreView input){ final Instance newInst = new Instance(); newInst.setInstanceId(input.getInstanceId()); return newInst; }})); return result; } }; try{ reply.set_return(Entities.asTransaction(LoadBalancerBackendInstance.class, remover).apply(null)); allInstances.removeAll(instancesToRemove); }catch(final Exception ex){ final String reason = ex.getCause()!=null && ex.getCause().getMessage()!=null ? ex.getCause().getMessage() : "internal error"; throw new InternalFailure400Exception(String.format("Failed to deregister instances: %s", reason), ex ); } LoadBalancingWorkflows.updateLoadBalancer(ctx.getAccountNumber(), lbName); DeregisterInstancesFromLoadBalancerResult result = new DeregisterInstancesFromLoadBalancerResult(); Instances returnInstances = new Instances(); returnInstances.setMember( new ArrayList<>(Collections2.transform(allInstances, new Function<LoadBalancerBackendInstanceCoreView, Instance>(){ @Override public Instance apply(final LoadBalancerBackendInstanceCoreView input){ final Instance newInst = new Instance(); newInst.setInstanceId(input.getInstanceId()); return newInst; }})) ); //Entities.asTransaction(LoadBalancer.class, finder).apply(null)); result.setInstances(returnInstances); reply.setDeregisterInstancesFromLoadBalancerResult(result); return reply; } public EnableAvailabilityZonesForLoadBalancerResponseType enableAvailabilityZonesForLoadBalancer(EnableAvailabilityZonesForLoadBalancerType request) throws EucalyptusCloudException { final EnableAvailabilityZonesForLoadBalancerResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String lbName = request.getLoadBalancerName( ); final Collection<String> requestedZones = request.getAvailabilityZones().getMember(); LoadBalancer lb; try{ lb = LoadBalancers.getLoadbalancer(ctx, lbName); }catch(final Exception ex){ throw new AccessPointNotFoundException(); } if( !LoadBalancingMetadatas.filterPrivileged().apply(lb) ) { // IAM policy restriction throw new AccessPointNotFoundException(); } final Set<String> allZones = Sets.newHashSet( Iterables.transform( lb.getZones(), LoadBalancerZoneCoreView.name( ) ) ); // check for a default VPC final Optional<VpcType> vpcOptional = EucalyptusActivityTasks.getInstance( ).defaultVpc( ctx.getAccount() ); final Map<String,String> zoneToSubnetIdMap = Maps.newHashMap( ); if ( vpcOptional.isPresent( ) && Objects.equals( lb.getVpcId( ), vpcOptional.get( ).getVpcId() ) ) { final List<SubnetType> subnets = EucalyptusActivityTasks.getInstance( ).describeSubnetsByZone( lb.getVpcId( ), true, requestedZones ); CollectionUtils.putAll( subnets, zoneToSubnetIdMap, SubnetType.zone( ), SubnetType.id( ) ); if ( requestedZones.size( ) != zoneToSubnetIdMap.size( ) ) { throw new InvalidConfigurationRequestException( "Cannot enable zone for VPC loadbalancer, default subnet not found." ); } } else if ( lb.getVpcId( ) != null ) { throw new InvalidConfigurationRequestException( "Cannot enable zone for VPC loadbalancer" ); } LoadBalancers.checkVersion(lb, DeploymentVersion.v4_2_0); if (lb.getVpcId() != null) { LoadBalancers.checkVersion(lb, DeploymentVersion.v4_3_0); } // check if requested AZ names are valid final List<LoadBalancerZoneCoreView> enabledZones = Lists.newArrayList(Collections2.filter(lb.getZones(), new Predicate<LoadBalancerZoneCoreView>(){ @Override public boolean apply(@Nullable LoadBalancerZoneCoreView arg0) { return arg0.getState().equals(LoadBalancerZone.STATE.InService); } })); final List<String> enabledZoneNames = Lists.newArrayList( Iterables.transform( enabledZones, LoadBalancerZoneCoreView.name( ) ) ); requestedZones.removeAll(enabledZoneNames); /// make sure the clusters match the requested zone final List<ClusterInfoType> clusters = EucalyptusActivityTasks.getInstance().describeAvailabilityZones(false); final List<String> foundZones = Lists.transform(clusters, new Function<ClusterInfoType, String>(){ @Override public String apply(@Nullable ClusterInfoType arg0) { return arg0.getZoneName(); } }); for(final String zone : requestedZones ){ if(! foundZones.contains(zone)){ throw new InvalidConfigurationRequestException(String.format("The requested zone %s is not valid", zone)); } } if( !requestedZones.isEmpty( ) ) { try{ if(! LoadBalancingWorkflows.enableZonesSync(ctx.getAccountNumber(), lbName, Lists.newArrayList(requestedZones), zoneToSubnetIdMap)) throw new Exception("Workflow for enabling availability zone has failed"); }catch(final Exception e){ final String reason = e.getCause()!=null && e.getCause().getMessage()!=null ? e.getCause().getMessage() : "internal error"; throw new InternalFailure400Exception(String.format("Failed to enable zones: %s", reason),e ); } } allZones.addAll( requestedZones ); final EnableAvailabilityZonesForLoadBalancerResult result = new EnableAvailabilityZonesForLoadBalancerResult(); final AvailabilityZones availZones = new AvailabilityZones( ); availZones.getMember( ).addAll( allZones ); reply.getEnableAvailabilityZonesForLoadBalancerResult( ).setAvailabilityZones( availZones ); return reply; } public DisableAvailabilityZonesForLoadBalancerResponseType disableAvailabilityZonesForLoadBalancer(DisableAvailabilityZonesForLoadBalancerType request) throws EucalyptusCloudException { final DisableAvailabilityZonesForLoadBalancerResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String lbName = request.getLoadBalancerName(); Collection<String> zones = request.getAvailabilityZones().getMember(); LoadBalancer lb; try{ lb = LoadBalancers.getLoadbalancer(ctx, lbName); }catch(final Exception ex){ throw new AccessPointNotFoundException(); } if( !LoadBalancingMetadatas.filterPrivileged().apply(lb) ) { // IAM policy restriction throw new AccessPointNotFoundException(); } // check for a default VPC final Optional<VpcType> vpcOptional = EucalyptusActivityTasks.getInstance( ).defaultVpc( ctx.getAccount() ); final Map<String,String> zoneToSubnetIdMap = Maps.newHashMap( ); if ( vpcOptional.isPresent( ) && Objects.equals( lb.getVpcId( ), vpcOptional.get( ).getVpcId() ) ) { final List<SubnetType> subnets = EucalyptusActivityTasks.getInstance( ).describeSubnetsByZone( lb.getVpcId( ), true, zones ); CollectionUtils.putAll( subnets, zoneToSubnetIdMap, SubnetType.zone( ), SubnetType.id( ) ); if ( zones.size( ) != zoneToSubnetIdMap.size( ) ) { throw new InvalidConfigurationRequestException( "Cannot disable zone for VPC loadbalancer, default subnet not found." ); } } else if ( lb.getVpcId( ) != null ) { throw new InvalidConfigurationRequestException( "Cannot disable zone for VPC loadbalancer" ); } LoadBalancers.checkVersion(lb, DeploymentVersion.v4_2_0); if (lb.getVpcId() != null) { LoadBalancers.checkVersion(lb, DeploymentVersion.v4_3_0); } /// validate the requested zones final List<LoadBalancerZoneCoreView> availableZones = Lists.newArrayList(Collections2.filter(lb.getZones(), new Predicate<LoadBalancerZoneCoreView>(){ @Override public boolean apply(@Nullable LoadBalancerZoneCoreView arg0) { return arg0.getState().equals(LoadBalancerZone.STATE.InService); } })); final Set<String> inServiceZoneNames = Sets.newHashSet(Collections2.transform(availableZones, new Function<LoadBalancerZoneCoreView, String>(){ @Override public String apply(@Nullable LoadBalancerZoneCoreView arg0) { return arg0.getName(); } })); zones = zones.stream() .filter( az -> inServiceZoneNames.contains( az )) .collect(Collectors.toList()); if(zones != null && zones.size()>0) { inServiceZoneNames.removeAll(zones); if (inServiceZoneNames.size() <= 0) { throw new InvalidConfigurationRequestException("There must be at least one availability zone"); } try{ if(! LoadBalancingWorkflows.disableZonesSync(ctx.getAccountNumber(), lbName, Lists.newArrayList(zones))) throw new Exception("Workflow for disabling availability zone has failed"); }catch(final Exception e){ final String reason = e.getCause()!=null && e.getCause().getMessage()!=null ? e.getCause().getMessage() : "internal error"; throw new InternalFailure400Exception(String.format("Failed to disable zones: %s", reason), e ); } } List<String> availableZoneNames = Lists.newArrayList(); try{ final LoadBalancer updatedLb = LoadBalancers.getLoadbalancer(ctx, lbName); availableZoneNames = Lists.transform(LoadBalancers.findZonesInService(updatedLb), new Function<LoadBalancerZoneCoreView, String>(){ @Override public String apply(@Nullable LoadBalancerZoneCoreView arg0) { return arg0.getName(); } }); }catch(Exception ex){ } final DisableAvailabilityZonesForLoadBalancerResult result = new DisableAvailabilityZonesForLoadBalancerResult(); final AvailabilityZones availZones = new AvailabilityZones(); availZones.setMember(Lists.newArrayList(availableZoneNames)); result.setAvailabilityZones(availZones); reply.setDisableAvailabilityZonesForLoadBalancerResult(result); reply.set_return(true); return reply; } public ConfigureHealthCheckResponseType configureHealthCheck(ConfigureHealthCheckType request) throws EucalyptusCloudException { ConfigureHealthCheckResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String lbName = request.getLoadBalancerName(); final HealthCheck hc = request.getHealthCheck(); final Integer healthyThreshold = hc.getHealthyThreshold(); if (healthyThreshold == null) throw new InvalidConfigurationRequestException("Healthy tresholds must be specified"); final Integer interval = hc.getInterval(); if(interval == null) throw new InvalidConfigurationRequestException("Interval must be specified"); final String target = hc.getTarget(); if(target == null) throw new InvalidConfigurationRequestException("Target must be specified"); final Integer timeout = hc.getTimeout(); if(timeout==null) throw new InvalidConfigurationRequestException("Timeout must be specified"); final Integer unhealthyThreshold = hc.getUnhealthyThreshold(); if(unhealthyThreshold == null) throw new InvalidConfigurationRequestException("Unhealthy tresholds must be specified"); if(interval < MIN_HEALTHCHECK_INTERVAL_SEC){ throw new InvalidConfigurationRequestException( String.format("Interval must be longer than %d seconds", MIN_HEALTHCHECK_INTERVAL_SEC)); } if(interval > MAX_HEALTHCHECK_INTERVAL_SEC) { throw new InvalidConfigurationRequestException( String.format("Interval must be smaller than %d seconds", MAX_HEALTHCHECK_INTERVAL_SEC)); } if(healthyThreshold < MIN_HEALTHCHECK_THRESHOLDS){ throw new InvalidConfigurationRequestException( String.format("Healthy thresholds must be larger than %d", MIN_HEALTHCHECK_THRESHOLDS)); } if(unhealthyThreshold < MIN_HEALTHCHECK_THRESHOLDS) { throw new InvalidConfigurationRequestException( String.format("Unhealthy thresholds must be larger than %d", MIN_HEALTHCHECK_THRESHOLDS)); } LoadBalancer lb; try{ lb = LoadBalancers.getLoadbalancer(ctx, lbName); }catch(NoSuchElementException ex){ throw new AccessPointNotFoundException(); }catch(Exception ex){ throw new InternalFailure400Exception("Failed to find the loadbalancer"); } if( lb!=null && !LoadBalancingMetadatas.filterPrivileged().apply(lb) ) { // IAM policy restriction throw new AccessPointNotFoundException(); } try ( final TransactionResource db = Entities.transactionFor( LoadBalancer.class ) ){ final LoadBalancer update = Entities.uniqueResult(lb); update.setHealthCheck(healthyThreshold, interval, target, timeout, unhealthyThreshold); hc.setTarget( update.getHealthCheckTarget( ) ); Entities.persist(update); db.commit(); }catch(final IllegalArgumentException ex){ throw new InvalidConfigurationRequestException(ex.getMessage()); }catch(final Exception ex){ LOG.error("failed to persist health check config", ex); throw new InternalFailure400Exception("Failed to persist the health check config", ex); } LoadBalancingWorkflows.updateLoadBalancer(ctx.getAccountNumber(), lbName); ConfigureHealthCheckResult result = new ConfigureHealthCheckResult(); result.setHealthCheck(hc); reply.setConfigureHealthCheckResult(result); return reply; } public DescribeInstanceHealthResponseType describeInstanceHealth(DescribeInstanceHealthType request) throws EucalyptusCloudException { final DescribeInstanceHealthResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String lbName = request.getLoadBalancerName(); final Instances instances = request.getInstances(); LoadBalancer lb; try { lb = lookupAuthorizedByNameOrDnsName( ctx.getAccountNumber(), lbName ); } catch ( final LoadBalancingException e ) { throw e; } catch( Exception ex ){ throw new InternalFailureException("Failed to find the loadbalancer"); } List<LoadBalancerBackendInstanceCoreView> lbInstances = Lists.newArrayList(lb.getBackendInstances()); List<LoadBalancerBackendInstanceCoreView> instancesFound; if(instances != null && instances.getMember()!= null && instances.getMember().size()>0){ instancesFound = Lists.newArrayList(); for(Instance inst : instances.getMember()){ String instId = inst.getInstanceId(); for(final LoadBalancerBackendInstanceCoreView lbInstance : lbInstances){ if(instId.equals(lbInstance.getInstanceId())){ instancesFound.add(lbInstance); break; } } } }else{ instancesFound = Lists.newArrayList(lb.getBackendInstances()); } final ArrayList<InstanceState> stateList = Lists.newArrayList(); for(final LoadBalancerBackendInstanceCoreView instance : instancesFound) { InstanceState state = new InstanceState(); state.setInstanceId(instance.getDisplayName()); state.setState(instance.getState().name()); if(instance.getReasonCode()!=null) state.setReasonCode(instance.getReasonCode()); if(instance.getDescription()!=null) state.setDescription(instance.getDescription()); stateList.add(state); } final InstanceStates states = new InstanceStates(); states.setMember(stateList); final DescribeInstanceHealthResult result = new DescribeInstanceHealthResult(); result.setInstanceStates(states); reply.setDescribeInstanceHealthResult(result); return reply; } public SetLoadBalancerListenerSSLCertificateResponseType setLoadBalancerListenerSSLCertificate(SetLoadBalancerListenerSSLCertificateType request) throws EucalyptusCloudException { final SetLoadBalancerListenerSSLCertificateResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String lbName = request.getLoadBalancerName(); final int lbPort = request.getLoadBalancerPort(); final String certArn = request.getSSLCertificateId(); if(lbPort <=0 || lbPort > 65535) throw new InvalidConfigurationRequestException("Invalid port"); if(certArn == null || certArn.length()<=0) throw new InvalidConfigurationRequestException("SSLCertificateId is not specified"); LoadBalancer lb; try{ lb = LoadBalancers.getLoadbalancer(ctx, lbName); }catch(NoSuchElementException ex){ throw new AccessPointNotFoundException(); }catch(Exception ex){ throw new InternalFailure400Exception("Failed to find the loadbalancer"); } LoadBalancers.checkWorkerCertificateExpiration(lb); try{ LoadBalancers.setLoadBalancerListenerSSLCertificate(lb, lbPort, certArn); LoadBalancingWorkflows.updateLoadBalancer(ctx.getAccountNumber(), lbName); }catch(final LoadBalancingException ex){ throw ex; }catch(final Exception ex){ LOG.error("Failed to set loadbalancer listener SSL certificate", ex); throw new InternalFailure400Exception("Failed to set loadbalancer listener SSL certificate", ex); } reply.setSetLoadBalancerListenerSSLCertificateResult(new SetLoadBalancerListenerSSLCertificateResult()); reply.set_return(true); return reply; } public DescribeLoadBalancerPolicyTypesResponseType describeLoadBalancerPolicyTypes(DescribeLoadBalancerPolicyTypesType request) throws EucalyptusCloudException { final List<PolicyTypeDescription> policyTypes = Lists.newArrayList(); Set<String> requestedTypeNames = Sets.newHashSet(); if (request.getPolicyTypeNames()!=null && request.getPolicyTypeNames().getMember()!=null){ requestedTypeNames.addAll(request.getPolicyTypeNames().getMember()); } try{ final List<LoadBalancerPolicyTypeDescription> internalPolicyTypes = LoadBalancerPolicies.getLoadBalancerPolicyTypeDescriptions(); for(final LoadBalancerPolicyTypeDescription from : internalPolicyTypes){ if(requestedTypeNames.isEmpty() || requestedTypeNames.contains(from.getPolicyTypeName())) policyTypes.add(LoadBalancerPolicies.AsPolicyTypeDescription.INSTANCE.apply(from)); } }catch(final Exception ex){ LOG.error("Failed to retrieve policy types", ex); throw new InternalFailure400Exception("Failed to retrieve policy types", ex); } final DescribeLoadBalancerPolicyTypesResponseType reply = request.getReply( ); final PolicyTypeDescriptions desc = new PolicyTypeDescriptions(); desc.setMember((ArrayList<PolicyTypeDescription>) policyTypes); final DescribeLoadBalancerPolicyTypesResult result = new DescribeLoadBalancerPolicyTypesResult(); result.setPolicyTypeDescriptions(desc); reply.setDescribeLoadBalancerPolicyTypesResult(result); reply.set_return(true); return reply; } public DescribeLoadBalancerPoliciesResponseType describeLoadBalancerPolicies(DescribeLoadBalancerPoliciesType request) throws EucalyptusCloudException { DescribeLoadBalancerPoliciesResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String lbName = request.getLoadBalancerName(); final PolicyNames policyNames = request.getPolicyNames(); if(lbName == null || lbName.isEmpty()){ // return sample policies according to ELB API final DescribeLoadBalancerPoliciesResult result = new DescribeLoadBalancerPoliciesResult(); final PolicyDescriptions descs = new PolicyDescriptions(); final List<PolicyDescription> policies = LoadBalancerPolicies.getSamplePolicyDescription(); descs.setMember((ArrayList<PolicyDescription>) policies); result.setPolicyDescriptions(descs); reply.setDescribeLoadBalancerPoliciesResult(result); }else{ LoadBalancer lb; try { lb = lookupAuthorizedByNameOrDnsName( ctx.getAccountNumber(), lbName ); } catch ( final LoadBalancingException e ) { throw e; } catch ( final Exception ex ){ LOG.error("Failed to find the loadbalancer", ex); throw new InternalFailureException("Failed to find the loadbalancer"); } List<LoadBalancerPolicyDescription> lbPolicies; try{ if(policyNames != null && policyNames.getMember()!=null && policyNames.getMember().size()>0) lbPolicies = LoadBalancerPolicies.getLoadBalancerPolicyDescription(lb, policyNames.getMember()); else lbPolicies = LoadBalancerPolicies.getLoadBalancerPolicyDescription(lb); }catch(final Exception ex){ LOG.error("Failed to find policy descriptions", ex); throw new InternalFailure400Exception("Failed to retrieve the policy descriptions", ex); } final DescribeLoadBalancerPoliciesResult result = new DescribeLoadBalancerPoliciesResult(); final PolicyDescriptions descs = new PolicyDescriptions(); final List<PolicyDescription> policies = Lists.newArrayList(); for(final LoadBalancerPolicyDescription lbPolicy : lbPolicies){ policies.add(LoadBalancerPolicies.AsPolicyDescription.INSTANCE.apply(lbPolicy)); } descs.setMember((ArrayList<PolicyDescription>) policies); result.setPolicyDescriptions(descs); reply.setDescribeLoadBalancerPoliciesResult(result); } reply.set_return(true); return reply; } public CreateLoadBalancerPolicyResponseType createLoadBalancerPolicy(CreateLoadBalancerPolicyType request) throws EucalyptusCloudException { CreateLoadBalancerPolicyResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String lbName = request.getLoadBalancerName(); final String policyName = request.getPolicyName(); final String policyTypeName = request.getPolicyTypeName(); if(lbName==null || lbName.isEmpty()) throw new InvalidConfigurationRequestException("Loadbalancer name must be specified"); if(policyName==null || policyName.isEmpty()) throw new InvalidConfigurationRequestException("policy name must be specified"); if(policyTypeName==null || policyTypeName.isEmpty()) throw new InvalidConfigurationRequestException("policy type name must be specified"); LoadBalancer lb; try{ lb= LoadBalancers.getLoadbalancer(ctx, lbName); }catch(NoSuchElementException ex){ throw new AccessPointNotFoundException(); }catch(final Exception ex){ LOG.error("Failed to find the loadbalancer", ex); throw new InternalFailure400Exception("Failed to find the loadbalancer"); } if( lb!=null && !LoadBalancingMetadatas.filterPrivileged().apply(lb) ) { // IAM policy restriction throw new AccessPointNotFoundException(); } final List<PolicyAttribute> attrs = (request.getPolicyAttributes() != null ? request.getPolicyAttributes().getMember() : Lists.<PolicyAttribute>newArrayList()); try{ LoadBalancerPolicies.addLoadBalancerPolicy(lb, policyName, policyTypeName, attrs); LoadBalancingWorkflows.updateLoadBalancer(ctx.getAccountNumber(), lbName); }catch(final LoadBalancingException ex){ throw ex; }catch(final Exception ex){ LOG.error("Failed to add the policy", ex); throw new InternalFailure400Exception("Failed to add the policy", ex); } reply.set_return(true); return reply; } public DeleteLoadBalancerPolicyResponseType deleteLoadBalancerPolicy(DeleteLoadBalancerPolicyType request) throws EucalyptusCloudException { final DeleteLoadBalancerPolicyResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String lbName = request.getLoadBalancerName(); final String policyName = request.getPolicyName(); if(lbName==null || lbName.isEmpty()) throw new InvalidConfigurationRequestException("Loadbalancer name must be specified"); if(policyName==null || policyName.isEmpty()) throw new InvalidConfigurationRequestException("policy name must be specified"); LoadBalancer lb; try{ lb= LoadBalancers.getLoadbalancer(ctx, lbName); }catch(NoSuchElementException ex){ throw new AccessPointNotFoundException(); }catch(final Exception ex){ LOG.error("Failed to find the loadbalancer", ex); throw new InternalFailure400Exception("Failed to find the loadbalancer"); } if( lb!=null && !LoadBalancingMetadatas.filterPrivileged().apply(lb) ) { // IAM policy restriction throw new AccessPointNotFoundException(); } try{ LoadBalancerPolicies.deleteLoadBalancerPolicy(lb, policyName); LoadBalancingWorkflows.updateLoadBalancer(ctx.getAccountNumber(), lbName); }catch(final LoadBalancingException ex){ throw ex; }catch(final Exception ex){ LOG.error("Failed to delete policy", ex); throw new InternalFailure400Exception("Failed to delete policy", ex); } reply.set_return(true); return reply; } public CreateLBCookieStickinessPolicyResponseType createLBCookieStickinessPolicy(CreateLBCookieStickinessPolicyType request) throws EucalyptusCloudException { CreateLBCookieStickinessPolicyResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String lbName = request.getLoadBalancerName(); final String policyName = request.getPolicyName(); final Long expiration = request.getCookieExpirationPeriod(); if(lbName==null || lbName.isEmpty()) throw new InvalidConfigurationRequestException("Loadbalancer name must be specified"); if(policyName==null || policyName.isEmpty()) throw new InvalidConfigurationRequestException("Policy name must be specified"); if(expiration == null || expiration <= 0) throw new InvalidConfigurationRequestException("Expiration period must be bigger than 0"); LoadBalancer lb; try{ lb= LoadBalancers.getLoadbalancer(ctx, lbName); }catch(final NoSuchElementException ex){ throw new AccessPointNotFoundException(); }catch(final Exception ex){ LOG.error("Failed to find the loadbalancer", ex); throw new InternalFailure400Exception("Failed to find the loadbalancer"); } if( lb!=null && !LoadBalancingMetadatas.filterPrivileged().apply(lb) ) { // IAM policy restriction throw new AccessPointNotFoundException(); } try{ final PolicyAttribute attr = new PolicyAttribute(); attr.setAttributeName("CookieExpirationPeriod"); attr.setAttributeValue(expiration.toString()); LoadBalancerPolicies.addLoadBalancerPolicy(lb, policyName, "LBCookieStickinessPolicyType", Lists.newArrayList(attr)); LoadBalancingWorkflows.updateLoadBalancer(ctx.getAccountNumber(), lbName); }catch(final LoadBalancingException ex){ throw ex; }catch(final Exception ex){ LOG.error("Failed to create policy", ex); throw new InternalFailure400Exception("Failed to create policy", ex); } reply.set_return(true); return reply; } public CreateAppCookieStickinessPolicyResponseType createAppCookieStickinessPolicy(CreateAppCookieStickinessPolicyType request) throws EucalyptusCloudException { CreateAppCookieStickinessPolicyResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String lbName = request.getLoadBalancerName(); final String policyName = request.getPolicyName(); final String cookieName = request.getCookieName(); if(lbName==null || lbName.isEmpty()) throw new InvalidConfigurationRequestException("Loadbalancer name must be specified"); if(policyName==null || policyName.isEmpty()) throw new InvalidConfigurationRequestException("Policy name must be specified"); if(cookieName == null) throw new InvalidConfigurationRequestException("Cookie name must be specified"); LoadBalancer lb; try{ lb= LoadBalancers.getLoadbalancer(ctx, lbName); }catch(final NoSuchElementException ex){ throw new AccessPointNotFoundException(); }catch(final Exception ex){ LOG.error("Failed to find the loadbalancer", ex); throw new InternalFailure400Exception("Failed to find the loadbalancer"); } if( lb!=null && !LoadBalancingMetadatas.filterPrivileged().apply(lb) ) { // IAM policy restriction throw new AccessPointNotFoundException(); } try{ final PolicyAttribute attr = new PolicyAttribute(); attr.setAttributeName("CookieName"); attr.setAttributeValue(cookieName); LoadBalancerPolicies.addLoadBalancerPolicy(lb, policyName, "AppCookieStickinessPolicyType", Lists.newArrayList(attr)); LoadBalancingWorkflows.updateLoadBalancer(ctx.getAccountNumber(), lbName); }catch(final LoadBalancingException ex){ throw ex; }catch(final Exception ex){ LOG.error("Failed to create policy", ex); throw new InternalFailure400Exception("Failed to create policy", ex); } reply.set_return(true); return reply; } public SetLoadBalancerPoliciesOfListenerResponseType setLoadBalancerPoliciesOfListener(SetLoadBalancerPoliciesOfListenerType request) throws EucalyptusCloudException { final SetLoadBalancerPoliciesOfListenerResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String lbName = request.getLoadBalancerName(); final int portNum = request.getLoadBalancerPort(); final PolicyNames pNames = request.getPolicyNames(); if(lbName==null || lbName.isEmpty()) throw new InvalidConfigurationRequestException("Loadbalancer name must be specified"); if(portNum <0 || portNum >65535) throw new InvalidConfigurationRequestException("Invalid port number specified"); final List<String> policyNames = pNames.getMember(); LoadBalancer lb; try{ lb= LoadBalancers.getLoadbalancer(ctx, lbName); }catch(final NoSuchElementException ex){ throw new AccessPointNotFoundException(); }catch(final Exception ex){ LOG.error("Failed to find the loadbalancer", ex); throw new InternalFailure400Exception("Failed to find the loadbalancer"); } if( lb!=null && !LoadBalancingMetadatas.filterPrivileged().apply(lb) ) { // IAM policy restriction throw new AccessPointNotFoundException(); } try{ LoadBalancerListener listener = null; for(final LoadBalancerListenerCoreView l : lb.getListeners()){ if(l.getLoadbalancerPort() == portNum){ listener = LoadBalancerListenerEntityTransform.INSTANCE.apply(l); break; } } if(listener == null) throw new ListenerNotFoundException(); final List<LoadBalancerPolicyDescription> policies = Lists.newArrayList(); if(policyNames!=null){ for(final String policyName : policyNames){ try{ final LoadBalancerPolicyDescription p = LoadBalancerPolicies.getLoadBalancerPolicyDescription(lb, policyName); final String policyType = p.getPolicyTypeName(); if (! ( "SSLNegotiationPolicyType".equals(policyType) || "LBCookieStickinessPolicyType".equals(policyType) || "AppCookieStickinessPolicyType".equals(policyType))) { throw new InvalidConfigurationRequestException(policyType +" cannot be set to listeners"); } policies.add(p); }catch(final LoadBalancingException ex) { throw ex; }catch(final Exception ex){ throw new PolicyNotFoundException(); } } } final List<LoadBalancerPolicyDescription> oldPolicies = LoadBalancerPolicies.getPoliciesOfListener(listener); LoadBalancerPolicies.removePoliciesFromListener(listener); try{ if(policies.size()>0) LoadBalancerPolicies.addPoliciesToListener(listener, policies); }catch(final Exception ex){ LoadBalancerPolicies.addPoliciesToListener(listener, oldPolicies); throw ex; } LoadBalancingWorkflows.updateLoadBalancer(ctx.getAccountNumber(), lbName); }catch(final LoadBalancingException ex){ throw ex; }catch(final Exception ex){ LOG.error("Failed to set policies to listener", ex); throw new InternalFailure400Exception("Failed to set policies to listener", ex); } return reply; } public SetLoadBalancerPoliciesForBackendServerResponseType setLoadBalancerPoliciesForBackendServer(SetLoadBalancerPoliciesForBackendServerType request) throws EucalyptusCloudException { final SetLoadBalancerPoliciesForBackendServerResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String lbName = request.getLoadBalancerName(); final int instancePort = request.getInstancePort(); final PolicyNames pNames = request.getPolicyNames(); if(lbName==null || lbName.isEmpty()) throw new InvalidConfigurationRequestException("Loadbalancer name must be specified"); if(instancePort <0 || instancePort >65535) throw new InvalidConfigurationRequestException("Invalid port number specified"); final List<String> policyNames = pNames.getMember(); LoadBalancer lb; try{ lb= LoadBalancers.getLoadbalancer(ctx, lbName); }catch(final NoSuchElementException ex){ throw new AccessPointNotFoundException(); }catch(final Exception ex){ LOG.error("Failed to find the loadbalancer", ex); throw new InternalFailure400Exception("Failed to find the loadbalancer"); } if( lb!=null && !LoadBalancingMetadatas.filterPrivileged().apply(lb) ) { // IAM policy restriction throw new AccessPointNotFoundException(); } try{ final Set<String> policyTypes = Sets.newHashSet(); final List<LoadBalancerPolicyDescription> policiesToAdd = Lists.newArrayList(); if(policyNames!=null){ for(final String policyName : policyNames){ try{ final LoadBalancerPolicyDescription policy = LoadBalancerPolicies.getLoadBalancerPolicyDescription(lb, policyName); if(! "BackendServerAuthenticationPolicyType".equals(policy.getPolicyTypeName())) throw new InvalidConfigurationRequestException("Only BackendServerAuthenticationPolicyType can be set to backend server"); policyTypes.add(policy.getPolicyTypeName()); policiesToAdd.add(policy); }catch(final LoadBalancingException ex) { throw ex; }catch(final Exception ex){ throw new PolicyNotFoundException(); } } } boolean listenerFound = false; for(final LoadBalancerListenerCoreView l : lb.getListeners()){ if(l.getInstancePort() == instancePort) { if( policyTypes.contains("BackendServerAuthenticationPolicyType") && !(PROTOCOL.HTTPS.equals(l.getInstanceProtocol()) || PROTOCOL.SSL.equals(l.getInstanceProtocol()))){ throw new InvalidConfigurationRequestException("Policies of BackendServerAuthenticationPolicyType can be set to only HTTPS/SSL instance protocol"); } listenerFound =true; break; } } if(!listenerFound) throw new InvalidConfigurationRequestException("Listener with the specified backend instance port is not found"); LoadBalancerBackendServerDescription backend = null; if(! LoadBalancerBackendServers.hasBackendServerDescription(lb, instancePort)){ backend= LoadBalancerBackendServers.createBackendServerDescription(lb, instancePort); }else{ backend = LoadBalancerBackendServers.getBackendServerDescription(lb, instancePort); } if (backend == null) throw new InvalidConfigurationRequestException("Failed to find the backend server description for port " + instancePort); LoadBalancerPolicies.clearPoliciesFromBackendServer(backend); try{ if(policiesToAdd.size()>0) LoadBalancerPolicies.addPoliciesToBackendServer(backend, policiesToAdd); }catch(final Exception ex){ throw ex; } LoadBalancingWorkflows.updateLoadBalancer(ctx.getAccountNumber(), lbName); }catch(final LoadBalancingException ex){ throw ex; }catch(final Exception ex){ LOG.error("Failed to set policies to backend server description", ex); throw new InternalFailure400Exception("Failed to set policies to backend server description", ex); } return reply; } public ApplySecurityGroupsToLoadBalancerResponseType applySecurityGroupsToLoadBalancer( final ApplySecurityGroupsToLoadBalancerType request ) throws EucalyptusCloudException { final ApplySecurityGroupsToLoadBalancerResponseType reply = request.getReply(); final Context ctx = Contexts.lookup(); final String accountNumber = ctx.getAccountNumber(); final Set<String> securityGroupIds = Sets.newHashSet( request.getSecurityGroups( ).getMember( ) ); final List<SecurityGroupItemType> groups = EucalyptusActivityTasks.getInstance( ).describeUserSecurityGroupsById( ctx.getAccount( ), null, securityGroupIds ); if ( groups.size( ) != securityGroupIds.size( ) ) { throw new LoadBalancingClientException( "InvalidSecurityGroup", "Invalid security group(s)" ); } final Function<String, Map<String,String>> updateSecurityGroups = new Function<String, Map<String,String>>( ) { @Override public Map<String,String> apply( final String identifier ) { try { final LoadBalancer example = LoadBalancer.namedByAccountId( accountNumber, identifier ); final LoadBalancer loadBalancer = Entities.uniqueResult( example ); if ( RestrictedTypes.filterPrivileged( ).apply( loadBalancer ) ) { if ( loadBalancer.getVpcId( ) == null ) { throw Exceptions.toUndeclared( new InvalidConfigurationRequestException( "VPC only" ) ); } LoadBalancers.checkVersion(loadBalancer, DeploymentVersion.v4_3_0); for(final SecurityGroupItemType group : groups) { if (!loadBalancer.getVpcId( ).equals(group.getVpcId())) throw Exceptions.toUndeclared(new InvalidConfigurationRequestException( String.format("Security group \"%s\" does not belong to VPC \"%s\"", group.getGroupId(), loadBalancer.getVpcId()))); } final List<SecurityGroupItemType> sortedGroups = Ordering.natural( ).onResultOf( SecurityGroupItemType.groupId( ) ).sortedCopy( groups ); loadBalancer.setSecurityGroupRefs( Lists.newArrayList( Iterables.transform( sortedGroups, TypeMappers.lookup( SecurityGroupItemType.class, LoadBalancerSecurityGroupRef.class ) ) ) ); final Map<String,String> groupIdToNameMap = CollectionUtils.putAll( sortedGroups, Maps.<String, String>newLinkedHashMap( ), SecurityGroupItemType.groupId( ), SecurityGroupItemType.groupName( ) ); return groupIdToNameMap; } else { throw new NoSuchElementException( ); } } catch ( NoSuchElementException e ) { throw Exceptions.toUndeclared( new AccessPointNotFoundException( ) ); } catch ( TransactionException e ) { throw Exceptions.toUndeclared( e ); } catch ( LoadBalancerVersionException e) { throw Exceptions.toUndeclared( e ); } } }; final Map<String,String> groupIdToNameMap; try { groupIdToNameMap = Entities.asTransaction( LoadBalancer.class, updateSecurityGroups ).apply( request.getLoadBalancerName( ) ); } catch ( Exception e ) { throw handleException( e ); } try{ if(!LoadBalancingWorkflows.applySecurityGroupsSync(accountNumber, request.getLoadBalancerName(), groupIdToNameMap)); } catch(final Exception ex) { LOG.error("Workflow for applying security groups failed", ex); final String reason = Optional.fromNullable( ex.getCause( ) ).transform( Exceptions.message( ) ).or( "internal error" ); throw new InternalFailure400Exception(String.format("Failed to apply security groups to loadbalancer: %s", reason), ex); } reply.getApplySecurityGroupsToLoadBalancerResult( ).setSecurityGroups( new SecurityGroups( Collections2.transform( groups, SecurityGroupItemType.groupId( ) ) ) ); return reply; } public AttachLoadBalancerToSubnetsResponseType attachLoadBalancerToSubnets( final AttachLoadBalancerToSubnetsType request ) throws EucalyptusCloudException { final AttachLoadBalancerToSubnetsResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String lbName = request.getLoadBalancerName( ); final Collection<String> requestedSubnetIds = request.getSubnets( ).getMember( ); final String vpcId; final BiMap<String,String> zoneToSubnetIdMap = HashBiMap.create( ); final LoadBalancerCoreView lb; try { final LoadBalancer lbEntity = LoadBalancers.getLoadbalancer( ctx, lbName ); if ( !LoadBalancingMetadatas.filterPrivileged().apply( lbEntity ) ) { // IAM policy restriction throw new AccessPointNotFoundException(); } lb = lbEntity.getCoreView(); vpcId = lb.getVpcId(); CollectionUtils.putAll( lbEntity.getZones().stream() .filter(az -> LoadBalancerZone.STATE.InService.equals(az.getState())) .collect(Collectors.toList()), zoneToSubnetIdMap, name(), subnetId() ); } catch ( final LoadBalancingException e ) { throw e; } catch ( final Exception ex ){ throw new AccessPointNotFoundException( ); } if ( vpcId == null ) { throw new InvalidConfigurationRequestException( "Invalid subnet for load balancer" ); } final List<SubnetType> subnets = EucalyptusActivityTasks.getInstance( ).describeSubnets( requestedSubnetIds ); if ( subnets.size( ) != requestedSubnetIds.size( ) ) { throw new LoadBalancingClientException( "SubnetNotFound", "Invalid subnet(s)" ); } for ( final SubnetType subnetType : subnets ) { if ( !vpcId.equals( subnetType.getVpcId( ) ) ) { throw new InvalidConfigurationRequestException( "Invalid subnet for load balancer" ); } final String previousSubnetId = zoneToSubnetIdMap.put( subnetType.getAvailabilityZone( ), subnetType.getSubnetId( ) ); if ( previousSubnetId != null && !previousSubnetId.equals( subnetType.getSubnetId( ) ) ) { throw new InvalidConfigurationRequestException( "Multiple subnets for zone ("+subnetType.getAvailabilityZone( )+")" ); } } final List<String> requestedZones = Lists.newArrayList( ); Iterables.addAll( requestedZones, Iterables.transform( requestedSubnetIds, Functions.forMap( zoneToSubnetIdMap.inverse( ) ) ) ); try{ LoadBalancingWorkflows.enableZonesSync(ctx.getAccountNumber(), lbName, requestedZones, Maps.newHashMap(zoneToSubnetIdMap)); }catch(final Exception e){ LOG.error("Workflow for enabling availablity zones failed", e); final String reason = e.getCause()!=null && e.getCause().getMessage()!=null ? e.getCause().getMessage() : "internal error"; throw new InternalFailure400Exception(String.format("Failed to enable zones: %s", reason),e ); } final List<String> attachedSubnets = Lists.newArrayList(); try{ final LoadBalancer lbEntity = LoadBalancers.getLoadbalancer( ctx, lbName ); attachedSubnets.addAll( Collections2.transform(Collections2.filter(lbEntity.getZones(), new Predicate<LoadBalancerZoneCoreView>() { @Override public boolean apply(LoadBalancerZoneCoreView zone) { return LoadBalancerZone.STATE.InService.equals(zone.getState()) && zoneToSubnetIdMap.containsKey(zone.getName()); } }), new Function<LoadBalancerZoneCoreView, String> () { @Override public String apply(LoadBalancerZoneCoreView zone) { return zoneToSubnetIdMap.get(zone.getName()); } }) ); }catch(final Exception ex) { ; } final Subnets replySubnets = new Subnets( ); replySubnets.getMember().addAll( attachedSubnets ); reply.getAttachLoadBalancerToSubnetsResult( ).setSubnets( replySubnets ); return reply; } public DetachLoadBalancerFromSubnetsResponseType detachLoadBalancerFromSubnets( final DetachLoadBalancerFromSubnetsType request ) throws EucalyptusCloudException { final DetachLoadBalancerFromSubnetsResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String lbName = request.getLoadBalancerName(); final Collection<String> requestedSubnetIds = request.getSubnets( ).getMember(); final BiMap<String,String> zoneToSubnetIdMap = HashBiMap.create( ); final String vpcId; final LoadBalancerCoreView lb; final Set<String> inServiceZones; try { final LoadBalancer lbEntity = LoadBalancers.getLoadbalancer( ctx, lbName ); if ( !LoadBalancingMetadatas.filterPrivileged( ).apply( lbEntity ) ) { // IAM policy restriction throw new AccessPointNotFoundException( ); } lb = lbEntity.getCoreView( ); vpcId = lb.getVpcId( ); CollectionUtils.putAll( lbEntity.getZones(), zoneToSubnetIdMap, name(), subnetId() ); inServiceZones = lbEntity.getZones().stream() .filter( az -> LoadBalancerZone.STATE.InService.equals(az.getState()) ) .map( az -> az.getName() ) .collect(Collectors.toSet()); } catch ( final LoadBalancingException e ) { throw e; } catch ( final Exception ex ){ throw new AccessPointNotFoundException( ); } if ( vpcId == null ) { throw new InvalidConfigurationRequestException( "Invalid subnet for load balancer" ); } List<String> zones = Lists.newArrayList( ); Iterables.addAll( zones, Iterables.transform( requestedSubnetIds, Functions.forMap( zoneToSubnetIdMap.inverse( ) ) ) ); zones = zones.stream() .filter( az -> inServiceZones.contains(az) ) .collect(Collectors.toList()); if (!zones.isEmpty( )) { inServiceZones.removeAll(zones); if (inServiceZones.size() <= 0) { throw new InvalidConfigurationRequestException("Loadbalancer must be attached to at least one subnet"); } try{ if(!LoadBalancingWorkflows.disableZonesSync(ctx.getAccountNumber(), lbName, zones)) throw new Exception("Workflow for disabling availability zones failed"); }catch(final Exception e){ final String reason = e.getCause()!=null && e.getCause().getMessage()!=null ? e.getCause().getMessage() : "internal error"; throw new InternalFailure400Exception(String.format("Failed to disable zones: %s", reason), e ); } } final List<String> attachedSubnets = Lists.newArrayList(); try{ final LoadBalancer lbEntity = LoadBalancers.getLoadbalancer( ctx, lbName ); attachedSubnets.addAll( Collections2.transform(Collections2.filter(lbEntity.getZones(), new Predicate<LoadBalancerZoneCoreView>() { @Override public boolean apply(LoadBalancerZoneCoreView zone) { return LoadBalancerZone.STATE.InService.equals(zone.getState()) && zoneToSubnetIdMap.containsKey(zone.getName()); } }), new Function<LoadBalancerZoneCoreView, String> () { @Override public String apply(LoadBalancerZoneCoreView zone) { return zoneToSubnetIdMap.get(zone.getName()); } }) ); }catch(final Exception ex) { ; } final Subnets replySubnets = new Subnets( ); replySubnets.getMember().addAll( attachedSubnets ); reply.getDetachLoadBalancerFromSubnetsResult( ).setSubnets( replySubnets ); return reply; } public AddTagsResponseType addTags( final AddTagsType request ) throws EucalyptusCloudException { final AddTagsResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String accountNumber = ctx.getAccount( ).getAccountNumber( ); final Set<String> requestedNames = Sets.newHashSet( ); if ( request.getLoadBalancerNames( ) != null ) { requestedNames.addAll( request.getLoadBalancerNames( ).getMember( ) ); } if ( requestedNames.size( ) > 1 ) { throw new LoadBalancingClientException( "InvalidParameterValue", "Too many load balancers" ); } final Map<String,String> tags = Maps.newHashMap( ); if ( request.getTags( ) != null ) { for ( final Tag tag : request.getTags( ).getMember( ) ) { if ( tags.put( tag.getKey( ), Strings.nullToEmpty( tag.getValue( ) ) ) != null ) { throw new LoadBalancingClientException( "DuplicateTagKeys", "Duplicate tag key (" + tag.getKey( ) + ")" ); } } final int reservedTags = Iterables.size( Iterables.filter( tags.keySet( ), isReservedTagPrefix() ) ); if ( reservedTags > 0 && !Contexts.lookup( ).isPrivileged( ) ) { throw new InvalidConfigurationRequestException("Invalid tag key (reserved prefix)"); } } final Function<String, Void> updateTags = new Function<String, Void>( ) { @Override public Void apply( final String identifier ) { try { final LoadBalancer example = LoadBalancer.namedByAccountId( accountNumber, identifier ); final LoadBalancer loadBalancer = Entities.uniqueResult( example ); if ( RestrictedTypes.filterPrivileged( ).apply( loadBalancer ) ) { final Map<String,String> lbTags = loadBalancer.getTags( ); lbTags.putAll( tags ); if ( Iterables.size( Iterables.filter( lbTags.keySet( ), Predicates.not( isReservedTagPrefix( ) ) ) ) > LoadBalancingServiceProperties.getMaxTags( ) ) { throw Exceptions.toUndeclared( new LoadBalancingClientException( "TooManyTags", "Tag limit exceeded" ) ); } return null; } else { throw new NoSuchElementException( ); } } catch ( NoSuchElementException e ) { throw Exceptions.toUndeclared( new AccessPointNotFoundException( ) ); } catch ( TransactionException e ) { throw Exceptions.toUndeclared( e ); } } }; try { Entities.asTransaction( LoadBalancer.class, updateTags ).apply( Iterables.getOnlyElement( requestedNames ) ); } catch ( Exception e ) { throw handleException( e ); } return reply; } public DescribeTagsResponseType describeTags( final DescribeTagsType request ) throws EucalyptusCloudException { final DescribeTagsResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String accountNumber = ctx.getAccount( ).getAccountNumber( ); final Set<String> requestedNames = Sets.newHashSet( ); if ( request.getLoadBalancerNames( ) != null ) { requestedNames.addAll( request.getLoadBalancerNames().getMember() ); } final boolean showAll = requestedNames.remove( "verbose" ) && ctx.isAdministrator(); final Function<Set<String>, TagDescriptions> lookupAccountLBs = new Function<Set<String>, TagDescriptions>( ) { @Override public TagDescriptions apply( final Set<String> identifiers ) { try { final Predicate<? super LoadBalancer> requestedAndAccessible = LoadBalancingMetadatas.filteringFor( LoadBalancer.class ) .byId( identifiers ) .byPrivileges( ) .buildPredicate( ); final LoadBalancer example = showAll ? LoadBalancer.named( null, null ) : LoadBalancer.namedByAccountId( accountNumber, null ); final List<LoadBalancer> lbs = Entities.query( example, true ); final TagDescriptions tagDescriptions = new TagDescriptions( ); for ( final LoadBalancer loadBalancer : Iterables.filter( lbs, requestedAndAccessible ) ) { final TagDescription tagDescription = new TagDescription( ); tagDescription.setLoadBalancerName( loadBalancer.getName( ) ); final TagList tagList = new TagList( ); tagDescription.setTags( tagList ); for ( final Map.Entry<String,String> tagKeyAndValue : loadBalancer.getTags( ).entrySet( ) ) { final Tag tag = new Tag(); tag.setKey( tagKeyAndValue.getKey( ) ); tag.setValue( Strings.emptyToNull( tagKeyAndValue.getValue( ) ) ); tagList.getMember().add( tag ); } tagDescriptions.getMember().add( tagDescription ); } return tagDescriptions; } catch ( EntityNotFoundException e ) { Entities.evictCache( LoadBalancer.class ); throw new OptimisticLockException( "Error loading load balancers", e ); } } }; final TagDescriptions tagDescriptions = Entities.asTransaction( LoadBalancer.class, lookupAccountLBs ).apply( requestedNames ); reply.getDescribeTagsResult( ).setTagDescriptions( tagDescriptions ); return reply; } public RemoveTagsResponseType removeTags( final RemoveTagsType request ) throws EucalyptusCloudException { final RemoveTagsResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String accountNumber = ctx.getAccount( ).getAccountNumber( ); final Set<String> requestedNames = Sets.newHashSet( ); if ( request.getLoadBalancerNames( ) != null ) { requestedNames.addAll( request.getLoadBalancerNames( ).getMember( ) ); } if ( requestedNames.size( ) > 1 ) { throw new LoadBalancingClientException( "InvalidParameterValue", "Too many load balancers" ); } final Set<String> tags = Sets.newHashSet( ); if ( request.getTags( ) != null ) { for ( final TagKeyOnly tag : request.getTags( ).getMember( ) ) { tags.add( tag.getKey( ) ); } final int reservedTags = Iterables.size( Iterables.filter( tags, isReservedTagPrefix( ) ) ); if ( reservedTags > 0 && !Contexts.lookup( ).isPrivileged( ) ) { throw new InvalidConfigurationRequestException("Invalid tag key (reserved prefix)"); } } final Function<String, Void> removeTags = new Function<String, Void>( ) { @Override public Void apply( final String identifier ) { try { final LoadBalancer example = LoadBalancer.namedByAccountId( accountNumber, identifier ); final LoadBalancer loadBalancer = Entities.uniqueResult( example ); if ( RestrictedTypes.filterPrivileged( ).apply( loadBalancer ) ) { final Map<String,String> lbTags = loadBalancer.getTags( ); lbTags.keySet( ).removeAll( tags ); return null; } else { throw new NoSuchElementException( ); } } catch ( NoSuchElementException e ) { throw Exceptions.toUndeclared( new AccessPointNotFoundException( ) ); } catch ( TransactionException e ) { throw Exceptions.toUndeclared( e ); } } }; try { Entities.asTransaction( LoadBalancer.class, removeTags ).apply( Iterables.getOnlyElement( requestedNames ) ); } catch ( Exception e ) { throw handleException( e ); } return reply; } public ModifyLoadBalancerAttributesResponseType modifyLoadBalancerAttributes( final ModifyLoadBalancerAttributesType request ) throws EucalyptusCloudException { final ModifyLoadBalancerAttributesResponseType reply = request.getReply( ); final Context ctx = Contexts.lookup( ); final String accountNumber = ctx.getAccount( ).getAccountNumber( ); try { LoadBalancers.getLoadbalancer(accountNumber, request.getLoadBalancerName()); } catch (final NoSuchElementException ex) { throw new AccessPointNotFoundException( ); } catch (final Exception ex) { throw new InternalFailure400Exception("Failed to modify attributes: unable to find the loadbalancer"); } final Function<String, LoadBalancerAttributes> modifyAttributes = new Function<String, LoadBalancerAttributes>( ) { @Override public LoadBalancerAttributes apply( final String identifier ) { try { final LoadBalancer example = LoadBalancer.namedByAccountId( accountNumber, identifier ); final LoadBalancer loadBalancer = Entities.uniqueResult( example ); if ( RestrictedTypes.filterPrivileged( ).apply( loadBalancer ) ) { final ConnectionSettings connectionSettings = request.getLoadBalancerAttributes( ).getConnectionSettings( ); if ( connectionSettings != null ) { loadBalancer.setConnectionIdleTimeout( connectionSettings.getIdleTimeout( ) ); } final CrossZoneLoadBalancing crossZoneLb = request.getLoadBalancerAttributes().getCrossZoneLoadBalancing(); if( crossZoneLb != null) loadBalancer.setCrossZoneLoadbalancingEnabled(crossZoneLb.getEnabled()); return TypeMappers.transform( loadBalancer, LoadBalancerAttributes.class ); } else { throw new NoSuchElementException( ); } } catch ( NoSuchElementException e ) { throw Exceptions.toUndeclared( new AccessPointNotFoundException( ) ); } catch ( TransactionException e ) { throw Exceptions.toUndeclared( e ); } } }; /************ Verify AccessLog attributes ************/ final AccessLog accessLog = request.getLoadBalancerAttributes().getAccessLog(); if (accessLog != null) { final boolean accessLogEnabled = accessLog.getEnabled(); if (accessLogEnabled) { final String bucketName = accessLog.getS3BucketName(); if (! EucalyptusActivityTasks.getInstance().bucketExists(ctx.getAccount(), bucketName)) { throw new InvalidConfigurationRequestException(String.format("S3Bucket: %s does not exist", bucketName)); } final Integer emitInterval = com.google.common.base.Objects.firstNonNull(accessLog.getEmitInterval(), 60); if(emitInterval < 5 || emitInterval > 60) { throw new InvalidConfigurationRequestException("Access log's emit interval must be between 5 and 60 minutes"); } } if (accessLog.getS3BucketPrefix() != null) { try{ String escapedPrefix = new ObjectMapper().writeValueAsString(accessLog.getS3BucketPrefix()); escapedPrefix = escapedPrefix.replaceAll("^\"|\"$", ""); accessLog.setS3BucketPrefix(escapedPrefix); }catch(final Exception ex) { throw new InvalidConfigurationRequestException("Invalid characters in bucket prefix text string"); } } } /************ END Verify AccessLog attributes ************/ try{ if(! LoadBalancingWorkflows.modifyLoadBalancerAttributesSync(accountNumber, request.getLoadBalancerName(), request.getLoadBalancerAttributes())) throw new Exception("Workflow for modifying attributes has failed"); LoadBalancingWorkflows.updateLoadBalancer(accountNumber, request.getLoadBalancerName()); }catch(final Exception e){ final String reason = e.getCause()!=null && e.getCause().getMessage()!=null ? e.getCause().getMessage() : "internal error"; throw new InternalFailure400Exception(String.format("Failed to modify attributes: %s",reason), e); } final LoadBalancerAttributes attributes = Entities.asTransaction( LoadBalancer.class, modifyAttributes ).apply( request.getLoadBalancerName( ) ); reply.getModifyLoadBalancerAttributesResult().setLoadBalancerAttributes( attributes ); return reply; } public DescribeLoadBalancerAttributesResponseType describeLoadBalancerAttributes( final DescribeLoadBalancerAttributesType request ) throws EucalyptusCloudException { final DescribeLoadBalancerAttributesResponseType reply = request.getReply(); final Context ctx = Contexts.lookup( ); final String accountNumber = ctx.getAccount( ).getAccountNumber( ); final Function<String, LoadBalancerAttributes> lookupAttributes = new Function<String, LoadBalancerAttributes>( ) { @Override public LoadBalancerAttributes apply( final String identifier ) { try { final LoadBalancer loadBalancer = lookupAuthorizedByNameOrDnsName( accountNumber, identifier ); return TypeMappers.transform( loadBalancer, LoadBalancerAttributes.class ); } catch ( Exception e ) { throw Exceptions.toUndeclared( e ); } } }; try { final LoadBalancerAttributes attributes = Entities.asTransaction( LoadBalancer.class, lookupAttributes ).apply( request.getLoadBalancerName() ); reply.getDescribeLoadBalancerAttributesResult().setLoadBalancerAttributes( attributes ); } catch ( RuntimeException e ) { Exceptions.findAndRethrow( e, LoadBalancingException.class ); throw e; } return reply; } /** * Lookup by name and verify permissions. */ @Nonnull private static LoadBalancer lookupAuthorizedByNameOrDnsName( final String accountNumber, final String name ) throws LoadBalancingException { LoadBalancer loadBalancer; try { loadBalancer = LoadBalancers.getLoadbalancer( accountNumber, name ); } catch( final NoSuchElementException e ) { try { loadBalancer = LoadBalancers.getLoadBalancerByDnsName( name ); } catch( final NoSuchElementException e2 ) { throw new AccessPointNotFoundException( ); } catch( final Exception e2 ){ LOG.error( "Failed to find loadbalancer by DNS name" + accountNumber + ":" + name , e2 ); throw new InternalFailureException( "Failed to find loadbalancer " + name ); } } catch( final Exception e ){ LOG.error( "Failed to find loadbalancer " + accountNumber + ":" + name , e ); throw new InternalFailureException( "Failed to find loadbalancer " + name ); } if( !LoadBalancingMetadatas.filterPrivileged( ).apply( loadBalancer ) ) { throw new AccessPointNotFoundException( ); } return loadBalancer; } private static Predicate<String> isReservedTagPrefix( ) { final Collection<Predicate<String>> predicates = Lists.newArrayList( ); for ( final String prefix : reservedPrefixes ) { predicates.add( com.eucalyptus.util.Strings.startsWith( prefix ) ); } return Predicates.or( predicates ); } private static LoadBalancingException handleException( final Exception e ) throws LoadBalancingException { final LoadBalancingException cause = Exceptions.findCause( e, LoadBalancingException.class ); if ( cause != null ) { throw cause; } final AuthQuotaException quotaCause = Exceptions.findCause( e, AuthQuotaException.class ); if ( quotaCause != null ) { throw new TooManyAccessPointsException(); } final AuthException authCause = Exceptions.findCause(e, AuthException.class); if(authCause != null) { throw new InternalFailure400Exception(authCause.getMessage()); } LOG.error( e, e ); final InternalFailureException exception = new InternalFailureException( String.valueOf(e.getMessage()) ); if ( Contexts.lookup( ).hasAdministrativePrivileges() ) { exception.initCause( e ); } throw exception; } }