/************************************************************************* * Copyright 2009-2016 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; import static com.eucalyptus.loadbalancing.LoadBalancer.Scheme; import static com.eucalyptus.loadbalancing.common.LoadBalancingMetadata.LoadBalancerMetadata; import static com.eucalyptus.util.RestrictedTypes.QuantityMetricFunction; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.eucalyptus.compute.common.RunningInstancesItemType; import com.eucalyptus.loadbalancing.activities.LoadBalancerVersionException; import org.apache.log4j.Logger; import org.xbill.DNS.Name; import org.xbill.DNS.TextParseException; import com.eucalyptus.auth.euare.ServerCertificateType; import com.eucalyptus.auth.principal.UserFullName; import com.eucalyptus.context.Context; import com.eucalyptus.entities.Entities; import com.eucalyptus.entities.TransactionResource; import com.eucalyptus.loadbalancing.LoadBalancer.LoadBalancerCoreView; import com.eucalyptus.loadbalancing.LoadBalancerBackendInstance.LoadBalancerBackendInstanceCoreView; import com.eucalyptus.loadbalancing.LoadBalancerBackendServerDescription.LoadBalancerBackendServerDescriptionCoreView; import com.eucalyptus.loadbalancing.LoadBalancerBackendServerDescription.LoadBalancerBackendServerDescriptionEntityTransform; 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.STATE; import com.eucalyptus.loadbalancing.LoadBalancerZone.LoadBalancerZoneCoreView; import com.eucalyptus.loadbalancing.LoadBalancerZone.LoadBalancerZoneEntityTransform; import com.eucalyptus.loadbalancing.activities.EucalyptusActivityTasks; import com.eucalyptus.loadbalancing.activities.LoadBalancerServoInstance; import com.eucalyptus.loadbalancing.activities.LoadBalancerServoInstance.LoadBalancerServoInstanceCoreView; import com.eucalyptus.loadbalancing.activities.LoadBalancerServoInstance.LoadBalancerServoInstanceEntityTransform; import com.eucalyptus.loadbalancing.common.msgs.AvailabilityZones; import com.eucalyptus.loadbalancing.common.msgs.BackendInstance; import com.eucalyptus.loadbalancing.common.msgs.BackendInstances; import com.eucalyptus.loadbalancing.common.msgs.BackendServerDescription; import com.eucalyptus.loadbalancing.common.msgs.BackendServerDescriptions; import com.eucalyptus.loadbalancing.common.msgs.HealthCheck; import com.eucalyptus.loadbalancing.common.msgs.Listener; 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.LoadBalancerServoDescription; 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.dns.LoadBalancerDomainName; import com.eucalyptus.loadbalancing.service.AccessPointNotFoundException; import com.eucalyptus.loadbalancing.service.CertificateNotFoundException; import com.eucalyptus.loadbalancing.service.DuplicateAccessPointName; import com.eucalyptus.loadbalancing.service.DuplicateListenerException; import com.eucalyptus.loadbalancing.service.InternalFailure400Exception; import com.eucalyptus.loadbalancing.service.InternalFailureException; import com.eucalyptus.loadbalancing.service.InvalidConfigurationRequestException; import com.eucalyptus.loadbalancing.service.ListenerNotFoundException; import com.eucalyptus.loadbalancing.service.LoadBalancingException; import com.eucalyptus.loadbalancing.service.UnsupportedParameterException; import com.eucalyptus.loadbalancing.workflow.LoadBalancingActivitiesImpl; import com.eucalyptus.util.EucalyptusCloudException; import com.eucalyptus.util.Exceptions; import com.eucalyptus.auth.principal.OwnerFullName; import com.eucalyptus.util.Pair; import com.eucalyptus.util.TypeMappers; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Strings; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; import com.google.common.collect.Ordering; import com.google.common.collect.Sets; /** * @author Sang-Min Park */ public class LoadBalancers { private static Logger LOG = Logger.getLogger( LoadBalancers.class ); public static List<LoadBalancer> listLoadbalancers(){ try ( final TransactionResource db = Entities.transactionFor( LoadBalancer.class ) ) { return Entities.query(LoadBalancer.named()); }catch(final NoSuchElementException ex){ return Lists.newArrayList(); }catch(final Exception ex){ throw Exceptions.toUndeclared(ex); } } public static List<LoadBalancer> listLoadbalancers(final String accountNumber) { try ( final TransactionResource db = Entities.transactionFor( LoadBalancer.class ) ) { return Entities.query(LoadBalancer.ownedByAccount(accountNumber)); }catch(final NoSuchElementException ex){ return Lists.newArrayList(); }catch(final Exception ex){ throw Exceptions.toUndeclared(ex); } } // a loadbalancer is per-account resource; per-user access is governed by IAM policy @Nonnull public static LoadBalancer getLoadbalancer(final Context ctx, final String lbName){ return LoadBalancers.getLoadbalancer( ctx.getAccount().getAccountNumber(), lbName ); } public static LoadBalancer getLoadbalancer(final String accountNumber, final String lbName) { LoadBalancer lb = null; try (final TransactionResource db = Entities.transactionFor(LoadBalancer.class)) { lb = Entities.uniqueResult(LoadBalancer.namedByAccountId(accountNumber, lbName)); db.commit(); return lb; } catch (NoSuchElementException ex) { throw ex; } catch (Exception ex) { if (lb != null) return lb; else throw Exceptions.toUndeclared(ex); } } public static LoadBalancer getLoadbalancerCaseInsensitive(final String accountNumber, final String lbName) { for (final LoadBalancer lb : listLoadbalancers(accountNumber) ) { if (lb.getDisplayName().toLowerCase().equals(lbName.toLowerCase())) { return lb; } } throw new NoSuchElementException(); } public static String getLoadBalancerDnsName( final LoadBalancerCoreView loadBalancer ) { return getLoadBalancerDnsName( loadBalancer.getScheme(), loadBalancer.getDisplayName(), loadBalancer.getOwnerAccountNumber() ); } public static String getLoadBalancerDnsName( final LoadBalancer loadBalancer ) { return getLoadBalancerDnsName( loadBalancer.getScheme(), loadBalancer.getDisplayName(), loadBalancer.getOwnerAccountNumber() ); } private static String getLoadBalancerDnsName( @Nullable final Scheme scheme, @Nonnull final String displayName, @Nonnull final String accountNumber ) { return LoadBalancerDomainName.forScheme( scheme ).generate( displayName, accountNumber ); } public static LoadBalancer getLoadBalancerByDnsName( final String dnsName ) throws NoSuchElementException { try { final Name hostName = Name.fromString( dnsName, Name.root ).relativize( LoadBalancerDomainName.getLoadBalancerSubdomain() ); final Optional<LoadBalancerDomainName> domainName = LoadBalancerDomainName.findMatching( hostName ); if ( domainName.isPresent( ) ) { final Pair<String, String> accountNamePair = domainName.get( ).toScopedLoadBalancerName( hostName ); try { return LoadBalancers.getLoadbalancer( accountNamePair.getLeft( ), accountNamePair.getRight( ) ); } catch ( NoSuchElementException e ) { if ( domainName.get( ) == LoadBalancerDomainName.INTERNAL ) { // perhaps it was an external balancer named "internal-..." final Pair<String, String> externalAccountNamePair = LoadBalancerDomainName.EXTERNAL.toScopedLoadBalancerName( hostName ); return LoadBalancers.getLoadbalancer( externalAccountNamePair.getLeft( ), externalAccountNamePair.getRight( ) ); } else { throw e; } } } else { throw new NoSuchElementException(); } } catch ( TextParseException e ) { throw new NoSuchElementException( ); } } public static void checkVersion(final LoadBalancer lb, DeploymentVersion minVersion) throws LoadBalancerVersionException { if(lb.getLoadbalancerDeploymentVersion() == null || ! DeploymentVersion.getVersion( lb.getLoadbalancerDeploymentVersion()).isEqualOrLaterThan(minVersion)) { throw new LoadBalancerVersionException(minVersion); } } public static Predicate<LoadBalancer> v4_2_0 = (lb) -> { return versionOnOrLater(lb, DeploymentVersion.v4_2_0); }; public static Predicate<LoadBalancer> v4_3_0 = (lb) -> { return versionOnOrLater(lb, DeploymentVersion.v4_3_0); }; public static Predicate<LoadBalancer> v4_4_0 = (lb) -> { return versionOnOrLater(lb, DeploymentVersion.v4_4_0); }; private static boolean versionOnOrLater(final LoadBalancer lb, DeploymentVersion version) { if (lb.getLoadbalancerDeploymentVersion() == null) { return false; } else { return DeploymentVersion.getVersion( lb.getLoadbalancerDeploymentVersion()).isEqualOrLaterThan(version); } } public enum DeploymentVersion { v4_1_0, v4_2_0, // the version is checked from 4.2.0 v4_3_0, v4_4_0; public static DeploymentVersion Latest = v4_4_0; public String toVersionString(){ return this.name( ).substring( 1 ).replace( "_", "." ); } public static DeploymentVersion getVersion(final String version) { if( version == null || version.length() <= 0) return DeploymentVersion.v4_1_0; return DeploymentVersion.valueOf( "v" + version.replace( ".", "_" ) ); } public boolean isLaterThan(final DeploymentVersion other) { if(other==null) return false; String[] thisVersionDigits = this.name().substring(1).split("_"); String[] otherVersionDigits = other.name().substring(1).split("_"); for(int i=0; i<thisVersionDigits.length; i++){ int thisDigit = Integer.parseInt(thisVersionDigits[i]); int otherDigit = 0; if(i < otherVersionDigits.length) otherDigit = Integer.parseInt(otherVersionDigits[i]); if(thisDigit > otherDigit) return true; else if(thisDigit < otherDigit) return false; } return false; } public boolean isEqualOrLaterThan(final DeploymentVersion other) { return this.equals(other) || this.isLaterThan(other); } } public static LoadBalancer addLoadbalancer( final UserFullName user, final String lbName, final String vpcId, final Scheme scheme, final Map<String,String> securityGroupIdsToNames, final Map<String,String> tags ) throws LoadBalancingException { final List<LoadBalancer> accountLbs = LoadBalancers.listLoadbalancers(user.getAccountNumber()); for(final LoadBalancer lb : accountLbs) { if (lbName.toLowerCase().equals(lb.getDisplayName().toLowerCase())) throw new DuplicateAccessPointName( ); } /// EC2 classic if (vpcId == null) { ///FIXME: not a sane reference final String securityGroupName = LoadBalancingActivitiesImpl.getSecurityGroupName(user.getAccountNumber(), lbName); try ( final TransactionResource db = Entities.transactionFor(LoadBalancerSecurityGroup.class)) { try{ final List<LoadBalancerSecurityGroup> groups = Entities.query(LoadBalancerSecurityGroup.withState(STATE.OutOfService)); for(final LoadBalancerSecurityGroup group : groups) { if (securityGroupName.equals(group.getName())) { throw new InternalFailureException("Cleaning up the previous ELB with the same name. Retry in a few minutes."); } } }catch(final NoSuchElementException e ) { ; } } } try ( final TransactionResource db = Entities.transactionFor( LoadBalancer.class ) ) { try { if( Entities.uniqueResult( LoadBalancer.namedByAccountId( user.getAccountNumber(), lbName ) ) != null ) throw new DuplicateAccessPointName( ); } catch ( final NoSuchElementException e ) { final List<LoadBalancerSecurityGroupRef> refs = Lists.newArrayList( ); for ( final Map.Entry<String,String> groupIdToNameEntry : securityGroupIdsToNames.entrySet( ) ) { refs.add( new LoadBalancerSecurityGroupRef( groupIdToNameEntry.getKey( ), groupIdToNameEntry.getValue( ) ) ); } Collections.sort( refs, Ordering.natural( ).onResultOf( LoadBalancerSecurityGroupRef.groupId( ) ) ); final LoadBalancer lb = LoadBalancer.newInstance(user, lbName ); lb.setVpcId( vpcId ); lb.setScheme( scheme ); lb.setSecurityGroupRefs( refs ); lb.setTags( tags ); lb.setLoadbalancerDeploymentVersion(DeploymentVersion.Latest.toVersionString()); Entities.persist( lb ); db.commit( ); return lb; } }catch(LoadBalancingException ex){ throw ex; }catch ( Exception ex ) { LOG.error("failed to persist a new loadbalancer", ex); throw new LoadBalancingException("Failed to persist a new load-balancer because of: " + ex.getMessage(), ex); } throw new LoadBalancingException("Failed to create a new load-balancer instance"); } public static void deleteLoadbalancer(final UserFullName user, final String lbName) throws LoadBalancingException { Predicate<Void> delete = new Predicate<Void>(){ @Override public boolean apply(@Nullable Void arg0) { try{ final LoadBalancer toDelete = Entities.uniqueResult( LoadBalancer.named(user, lbName)); Entities.delete(toDelete); }catch(final Exception ex){ return false; } return true; } }; Entities.asTransaction(LoadBalancer.class, delete).apply(null); } public static void validateListener(final List<Listener> listeners) throws LoadBalancingException, EucalyptusCloudException{ validateListener(null, listeners); } public static void validateListener(final LoadBalancer lb, final List<Listener> listeners) throws LoadBalancingException, EucalyptusCloudException{ for(Listener listener : listeners){ if(!LoadBalancerListener.protocolSupported(listener)) throw new UnsupportedParameterException("The requested protocol is not supported"); if(!LoadBalancerListener.acceptable(listener)) throw new InvalidConfigurationRequestException("Invalid listener format"); if(!LoadBalancerListener.validRange(listener)) throw new InvalidConfigurationRequestException("Invalid port range"); if(!LoadBalancerListener.portAvailable(listener)) throw new EucalyptusCloudException("The specified port(s) " + LoadBalancerListener.RESTRICTED_PORTS + ", are restricted for use as a loadbalancer port."); final PROTOCOL protocol = PROTOCOL.valueOf(listener.getProtocol().toUpperCase()); if(protocol.equals(PROTOCOL.HTTPS) || protocol.equals(PROTOCOL.SSL)) { final String sslId = listener.getSSLCertificateId(); if(sslId==null || sslId.length()<=0) throw new InvalidConfigurationRequestException("SSLCertificateId is required for HTTPS or SSL protocol"); } // check the listener if(lb!=null && lb.hasListener( listener.getLoadBalancerPort() )){ final LoadBalancerListenerCoreView existing = lb.findListener( listener.getLoadBalancerPort() ); if ( existing.getInstancePort() != listener.getInstancePort() || !existing.getProtocol().name().toLowerCase().equals( listener.getProtocol().toLowerCase() ) || ( ( existing.getCertificateId() == null || !existing.getCertificateId().equals( listener.getSSLCertificateId() ) ) ) ) { throw new DuplicateListenerException(); } } } } public static void createLoadbalancerListener(final String lbName, final Context ctx , final List<Listener> listeners) throws LoadBalancingException, EucalyptusCloudException { LoadBalancer lb; try{ lb= LoadBalancers.getLoadbalancer(ctx, lbName); }catch(Exception ex){ throw new InternalFailure400Exception("unable to find the loadbalancer"); } validateListener(lb, listeners); final Predicate<LoadBalancer> creator = new Predicate<LoadBalancer>(){ @Override public boolean apply( LoadBalancer lb ) { for(Listener listener : listeners){ // check the listener try{ if(!lb.hasListener( listener.getLoadBalancerPort() )){ LoadBalancerListener.Builder builder = new LoadBalancerListener.Builder(lb, listener.getInstancePort(), listener.getLoadBalancerPort(), LoadBalancerListener.PROTOCOL.valueOf(listener.getProtocol().toUpperCase())); if(!Strings.isNullOrEmpty(listener.getInstanceProtocol())) builder.instanceProtocol(PROTOCOL.valueOf(listener.getInstanceProtocol())); if(!Strings.isNullOrEmpty(listener.getSSLCertificateId())) builder.withSSLCerntificate(listener.getSSLCertificateId()); Entities.persist(builder.build()); } }catch(Exception ex){ LOG.warn("failed to create the listener object", ex); } } return true; } }; Entities.asTransaction(LoadBalancerListener.class, creator).apply(lb); } public static void addZone( final String lbName, final Context ctx, final Collection<String> zones, final Map<String,String> zoneToSubnetIdMap ) throws LoadBalancingException { LoadBalancer lb; try{ lb = LoadBalancers.getLoadbalancer(ctx, lbName); }catch(Exception ex){ throw new AccessPointNotFoundException(); } try{ for( final String zone : zones ){ // check the listener try ( final TransactionResource db = Entities.transactionFor( LoadBalancerZone.class ) ) { try { final LoadBalancerZone sample = LoadBalancerZone.named( lb, zone ); final LoadBalancerZone exist = Entities.uniqueResult( sample ); exist.setState( LoadBalancerZone.STATE.InService ); } catch( final NoSuchElementException ex ) { final LoadBalancerZone newZone = LoadBalancerZone.create( lb, zone, zoneToSubnetIdMap.get( zone ) ); newZone.setState( LoadBalancerZone.STATE.InService ); Entities.persist( newZone ); } db.commit(); } catch( final Exception ex ){ LOG.error("failed to persist the zone "+zone, ex); throw ex; } } }catch(Exception ex){ throw new InternalFailure400Exception("Failed to persist the zone"); } } public static void removeZone(final String lbName, final Context ctx, final Collection<String> zones) throws LoadBalancingException{ LoadBalancer lb; try{ lb = LoadBalancers.getLoadbalancer(ctx, lbName); }catch(Exception ex){ throw new AccessPointNotFoundException(); } for(String zone : zones){ try ( final TransactionResource db = Entities.transactionFor( LoadBalancerZone.class ) ) { final LoadBalancerZone exist = Entities.uniqueResult(LoadBalancerZone.named(lb, zone)); Entities.delete(exist); db.commit(); }catch(NoSuchElementException ex){ LOG.debug(String.format("zone %s not found for %s", zone, lbName)); }catch(Exception ex){ LOG.error("failed to delete the zone "+zone, ex); } } } public static LoadBalancerZone findZone(final LoadBalancer lb, final String zoneName){ try ( final TransactionResource db = Entities.transactionFor( LoadBalancerZone.class ) ) { final LoadBalancerZone exist = Entities.uniqueResult(LoadBalancerZone.named(lb, zoneName)); db.commit(); return exist; }catch(NoSuchElementException ex){ throw ex; }catch(Exception ex){ throw Exceptions.toUndeclared(ex); } } public static List<LoadBalancerZoneCoreView> findZonesInService(final LoadBalancer lb){ final List<LoadBalancerZoneCoreView> inService = Lists.newArrayList(); for(final LoadBalancerZoneCoreView zone : lb.getZones()){ if(zone.getState().equals(LoadBalancerZone.STATE.InService)) inService.add(zone); } return inService; } public static LoadBalancerServoInstance lookupServoInstance(final String instanceId) throws LoadBalancingException { try ( final TransactionResource db = Entities.transactionFor( LoadBalancerServoInstance.class ) ) { LoadBalancerServoInstance sample = LoadBalancerServoInstance.named(instanceId); final LoadBalancerServoInstance exist = Entities.uniqueResult(sample); return exist; }catch(NoSuchElementException ex){ throw ex; }catch(Exception ex){ throw new LoadBalancingException("failed to query servo instances", ex); } } public static LoadBalancerBackendInstance lookupBackendInstance(final LoadBalancer lb, final String instanceId) { try ( final TransactionResource db = Entities.transactionFor( LoadBalancerBackendInstance.class ) ) { final LoadBalancerBackendInstance found = Entities.uniqueResult(LoadBalancerBackendInstance.named(lb, instanceId)); return found; }catch(final NoSuchElementException ex){ throw ex; }catch(final Exception ex){ throw Exceptions.toUndeclared(ex); } } public static void deleteBackendInstance(final LoadBalancer lb, final String instanceId) { try ( final TransactionResource db = Entities.transactionFor( LoadBalancerBackendInstance.class ) ) { final LoadBalancerBackendInstance toDelete = Entities.uniqueResult(LoadBalancerBackendInstance.named(lb, instanceId)); Entities.delete(toDelete); db.commit(); }catch(final NoSuchElementException ex){ throw ex; }catch(final Exception ex){ throw Exceptions.toUndeclared(ex); } } public static void unsetForeignKeys(final Context ctx, final String loadbalancer){ Predicate<LoadBalancerServoInstance> unsetServoInstanceKey = new Predicate<LoadBalancerServoInstance>(){ @Override public boolean apply(@Nullable LoadBalancerServoInstance arg0) { try{ final LoadBalancerServoInstance update = Entities.uniqueResult(arg0); //update.setSecurityGroup(null); update.setAvailabilityZone( null ); update.setAutoScalingGroup( null ); return true; }catch(final Exception ex){ return false; } } }; LoadBalancer lb; try{ lb = getLoadbalancer(ctx, loadbalancer); }catch(Exception ex){ return; } if(lb!=null){ if(lb.getZones()!=null){ for(final LoadBalancerZoneCoreView zoneView : lb.getZones()){ LoadBalancerZone zone; try{ zone = LoadBalancerZoneEntityTransform.INSTANCE.apply(zoneView); }catch(final Exception ex){ continue; } for(LoadBalancerServoInstanceCoreView servo : zone.getServoInstances()){ try{ final LoadBalancerServoInstance instance = LoadBalancerServoInstanceEntityTransform.INSTANCE.apply(servo); Entities.asTransaction(LoadBalancerServoInstance.class, unsetServoInstanceKey).apply(instance); }catch(Exception ex){ } } } } } } public static void setLoadBalancerListenerSSLCertificate(final LoadBalancer lb, final int lbPort, final String certArn) throws LoadBalancingException { final Collection<LoadBalancerListenerCoreView> listeners = lb.getListeners(); LoadBalancerListenerCoreView listener = null; for(final LoadBalancerListenerCoreView l : listeners){ if(l.getLoadbalancerPort() == lbPort){ listener = l; break; } } if(listener == null) throw new ListenerNotFoundException(); if(!(PROTOCOL.HTTPS.equals(listener.getProtocol()) || PROTOCOL.SSL.equals(listener.getProtocol()))) throw new InvalidConfigurationRequestException("Listener's protocol is not HTTPS or SSL"); checkSSLCertificate(lb.getOwnerAccountNumber(), certArn); updateIAMRolePolicy(lb.getOwnerAccountNumber(), lb.getDisplayName(), listener.getCertificateId(), certArn); try ( final TransactionResource db = Entities.transactionFor( LoadBalancerListener.class ) ) { final LoadBalancerListener update = Entities.uniqueResult(LoadBalancerListener.named(lb, lbPort)); update.setSSLCertificateId( certArn ); Entities.persist(update); db.commit(); }catch(final NoSuchElementException ex){ throw new ListenerNotFoundException(); }catch(final Exception ex){ throw Exceptions.toUndeclared(ex); } } private static void updateIAMRolePolicy(final String accountId, final String lbName, final String oldCertArn, final String newCertArn) throws LoadBalancingException{ final String prefix = String.format("arn:aws:iam::%s:server-certificate", accountId); final String oldCertName = oldCertArn.replace(prefix, "") .substring(oldCertArn.replace(prefix, "").lastIndexOf("/")+1); final String newCertName = newCertArn.replace(prefix, "") .substring(newCertArn.replace(prefix, "").lastIndexOf("/")+1); ////FIXME: not a sound reference final String roleName = String.format("%s-%s-%s", LoadBalancingActivitiesImpl.ROLE_NAME_PREFIX, accountId, lbName); final String oldPolicyName = String.format("%s-%s-%s-%s", LoadBalancingActivitiesImpl.SERVER_CERT_ROLE_POLICY_NAME_PREFIX, accountId, lbName, oldCertName); LoadBalancer lb; try{ lb= LoadBalancers.getLoadbalancer(accountId, lbName); }catch(Exception ex){ throw new LoadBalancingException("Failed to find the loadbalancer named " + lbName, ex); } try{ EucalyptusActivityTasks.getInstance().deleteRolePolicy(roleName, oldPolicyName, lb.useSystemAccount()); }catch(final Exception ex){ throw new LoadBalancingException("Failed to delete old role policy "+oldPolicyName, ex); } final String newPolicyName = String.format("%s-%s-%s-%s", LoadBalancingActivitiesImpl.SERVER_CERT_ROLE_POLICY_NAME_PREFIX, accountId, lbName, newCertName); final String newPolicyDoc = LoadBalancingActivitiesImpl.ROLE_SERVER_CERT_POLICY_DOCUMENT .replace("CERT_ARN_PLACEHOLDER", newCertArn); try{ EucalyptusActivityTasks.getInstance().putRolePolicy(roleName, newPolicyName, newPolicyDoc, lb.useSystemAccount()); }catch(final Exception ex){ throw new LoadBalancingException("Failed to add new role policy "+newPolicyName, ex); } } public static void checkSSLCertificate(final String accountNumber, final String certArn) throws LoadBalancingException { try{ final String prefix = String.format("arn:aws:iam::%s:server-certificate", accountNumber); if(!certArn.startsWith(prefix)) throw new CertificateNotFoundException(); final String pathAndName = certArn.replace(prefix, ""); final String certName = pathAndName.substring(pathAndName.lastIndexOf("/")+1); final ServerCertificateType cert = EucalyptusActivityTasks.getInstance().getServerCertificate(accountNumber, certName); if(cert==null) throw new CertificateNotFoundException(); if(!certArn.equals(cert.getServerCertificateMetadata().getArn())) throw new CertificateNotFoundException(); }catch(final Exception ex){ throw new CertificateNotFoundException(); } } //// WARNING: this method is database intensive call //// Do not invoke too frequently! public static LoadBalancerServoDescription getServoDescription( final String accountId, final String lbName, final String zone) throws LoadBalancingException { final LoadBalancer lb = getLoadbalancer(accountId, lbName); final LoadBalancerZone lbZone; try ( final TransactionResource db = Entities.transactionFor( LoadBalancerZone.class ) ) { try { final LoadBalancerZone sample = LoadBalancerZone.named( lb, zone ); lbZone = Entities.uniqueResult( sample ); }catch(final Exception ex) { throw new LoadBalancingException("No such availability zone is found in database"); } } List<LoadBalancerListener> lbListeners = Lists.newArrayList(); List<LoadBalancerBackendServerDescription> backendServers = Lists.newArrayList(); List<LoadBalancerPolicyDescription> lbPolicies = Lists.newArrayList(); try{ for(final LoadBalancerListenerCoreView listenerView: lb.getListeners()){ lbListeners.add(LoadBalancerListenerEntityTransform.INSTANCE.apply(listenerView)); } for(final LoadBalancerBackendServerDescriptionCoreView backendView : lb.getBackendServers()) { backendServers.add(LoadBalancerBackendServerDescriptionEntityTransform.INSTANCE.apply(backendView)); } }catch(final Exception ex) { throw new LoadBalancingException("Unexpected error while preparing loadbalancer description", ex); } return getServoDescription(lb, lbZone, lbListeners, backendServers, lbPolicies); } public static LoadBalancerServoDescription getServoDescription( final LoadBalancer lb, final LoadBalancerZone zone, final List<LoadBalancerListener> lbListeners, final List<LoadBalancerBackendServerDescription> backendServers, final List<LoadBalancerPolicyDescription> lbPolicies) { final String lbName = lb.getDisplayName(); final LoadBalancerServoDescription desc = new LoadBalancerServoDescription(); desc.setLoadBalancerName(lbName); /// loadbalancer name desc.setCreatedTime(lb.getCreationTimestamp());/// createdtime /// dns name desc.setDnsName(LoadBalancers.getLoadBalancerDnsName(lb)); // attributes desc.setLoadBalancerAttributes(TypeMappers.transform(lb, LoadBalancerAttributes.class)); /// backend instances in the same zone Collection<LoadBalancerBackendInstanceCoreView> backendInstancesInSameZone = Collections2.filter(zone.getBackendInstances(), new Predicate<LoadBalancerBackendInstanceCoreView>() { @Override public boolean apply(LoadBalancerBackendInstanceCoreView arg0) { return !LoadBalancerBackendInstance.STATE.Error.equals(arg0.getBackendState()) && !(arg0.getIpAddress() == null || arg0.getIpAddress().length() <= 0); } }); final boolean zoneHasAvailableInstance = backendInstancesInSameZone.stream().anyMatch(inst -> LoadBalancerBackendInstance.STATE.InService.equals(inst.getBackendState()) && !(inst.getIpAddress() == null || inst.getIpAddress().length() <= 0) ); // backend instances in cross-zone Collection<LoadBalancerBackendInstanceCoreView> crossZoneBackendInstances = Lists.newArrayList(); if (!zoneHasAvailableInstance || desc.getLoadBalancerAttributes().getCrossZoneLoadBalancing().getEnabled()) { // EUCA-13233: when a zone contains no available instance, cross-zone instances are always included crossZoneBackendInstances = Collections2.filter(lb.getBackendInstances(), new Predicate<LoadBalancerBackendInstanceCoreView>() { @Override public boolean apply(LoadBalancerBackendInstanceCoreView arg0) { // Instance's service state can only be determined in the same zone. Cross-zone instances are included only when InService final boolean inService = LoadBalancerBackendInstance.STATE.InService.equals(arg0.getBackendState()) && !(arg0.getIpAddress() == null || arg0.getIpAddress().length() <= 0); return inService && !zone.getName().equals(arg0.getPartition()); // different zone } }); } if (!backendInstancesInSameZone.isEmpty()) { desc.setBackendInstances(new BackendInstances()); desc.getBackendInstances().getMember().addAll( Collections2.transform(backendInstancesInSameZone, new Function<LoadBalancerBackendInstanceCoreView, BackendInstance>() { @Override public BackendInstance apply(final LoadBalancerBackendInstanceCoreView be) { final BackendInstance instance = new BackendInstance(); instance.setInstanceId(be.getInstanceId()); instance.setInstanceIpAddress(be.getIpAddress()); instance.setReportHealthCheck(true); return instance; } })); } if (!crossZoneBackendInstances.isEmpty()) { if (desc.getBackendInstances() == null) desc.setBackendInstances(new BackendInstances()); desc.getBackendInstances().getMember().addAll( Collections2.transform(crossZoneBackendInstances, new Function<LoadBalancerBackendInstanceCoreView, BackendInstance>() { @Override public BackendInstance apply(final LoadBalancerBackendInstanceCoreView be) { final BackendInstance instance = new BackendInstance(); instance.setInstanceId(be.getInstanceId()); instance.setInstanceIpAddress(be.getIpAddress()); // if the servo's zone != backend instance's, it does not report health check // only the servo in the same zone will change the instance's state instance.setReportHealthCheck(false); return instance; } })); } /// availability zones desc.setAvailabilityZones(new AvailabilityZones()); desc.getAvailabilityZones().getMember().add(zone.getName()); final Set<String> policiesOfListener = Sets.newHashSet(); final Set<String> policiesForBackendServer = Sets.newHashSet(); /// listeners if (lbListeners.size() > 0) { desc.setListenerDescriptions(new ListenerDescriptions()); desc.getListenerDescriptions().setMember(new ArrayList<>( Collections2.transform(lbListeners, new Function<LoadBalancerListener, ListenerDescription>() { @Override public ListenerDescription apply(final LoadBalancerListener lbListener) { ListenerDescription desc = new ListenerDescription(); Listener listener = new Listener(); listener.setLoadBalancerPort(lbListener.getLoadbalancerPort()); listener.setInstancePort(lbListener.getInstancePort()); if (lbListener.getInstanceProtocol() != PROTOCOL.NONE) listener.setInstanceProtocol(lbListener.getInstanceProtocol().name()); listener.setProtocol(lbListener.getProtocol().name()); if (lbListener.getCertificateId() != null) listener.setSSLCertificateId(lbListener.getCertificateId()); desc.setListener(listener); final PolicyNames pnames = new PolicyNames(); pnames.setMember(new ArrayList<>(Lists.transform(lbListener.getPolicies(), new Function<LoadBalancerPolicyDescriptionCoreView, String>() { @Override public String apply( LoadBalancerPolicyDescriptionCoreView arg0) { try { return arg0.getPolicyName(); // No other policy types are supported } catch (final Exception ex) { return ""; // No other policy types are supported } } }))); policiesOfListener.addAll(pnames.getMember()); desc.setPolicyNames(pnames); return desc; } }))); } /// backend server descriptions try { if (backendServers.size() > 0) { desc.setBackendServerDescriptions(new BackendServerDescriptions()); desc.getBackendServerDescriptions().setMember(new ArrayList<>( Collections2.transform(backendServers, new Function<LoadBalancerBackendServerDescription, BackendServerDescription>() { @Override public BackendServerDescription apply( LoadBalancerBackendServerDescription backend) { final BackendServerDescription desc = new BackendServerDescription(); desc.setInstancePort(backend.getInstancePort()); desc.setPolicyNames(new PolicyNames()); desc.getPolicyNames().setMember(new ArrayList<>( Collections2.transform(backend.getPolicyDescriptions(), new Function<LoadBalancerPolicyDescriptionCoreView, String>() { @Override public String apply( LoadBalancerPolicyDescriptionCoreView arg0) { return arg0.getPolicyName(); } }) )); policiesForBackendServer.addAll(desc.getPolicyNames().getMember()); return desc; } }) )); } } catch (final Exception ex) { ; } /// 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) { } // policies (EUCA-specific) final List<PolicyDescription> policies = Lists.newArrayList(); for (final LoadBalancerPolicyDescription lbPolicy : lbPolicies) { // for efficiency, add policies only if they are set for listeners // PublicKey policies should always be included bc it's referenced from BackendAuthenticationPolicyType if (policiesOfListener.contains(lbPolicy.getPolicyName()) || policiesForBackendServer.contains(lbPolicy.getPolicyName()) || "PublicKeyPolicyType".equals(lbPolicy.getPolicyTypeName())) policies.add(LoadBalancerPolicies.AsPolicyDescription.INSTANCE.apply(lbPolicy)); } final PolicyDescriptions policyDescs = new PolicyDescriptions(); policyDescs.setMember((ArrayList<PolicyDescription>) policies); desc.setPolicyDescriptions(policyDescs); return desc; } public static void checkWorkerCertificateExpiration(final LoadBalancer lb) throws LoadBalancingException{ try{ for (final LoadBalancerZoneCoreView lbZoneView : lb.getZones()) { final LoadBalancerZone lbZone = LoadBalancerZoneEntityTransform.INSTANCE.apply(lbZoneView); for(final LoadBalancerServoInstanceCoreView instance : lbZone.getServoInstances()) { if(LoadBalancerServoInstance.STATE.InService.equals(instance.getState())) { boolean expired = false; try { // Upgrade case: add expiration date to instance's launch time if (instance.getCertificateExpirationDate() == null) { final List<RunningInstancesItemType> instances = EucalyptusActivityTasks.getInstance().describeSystemInstances(Lists.newArrayList(instance.getInstanceId()), true); final Date launchDate = instances.get(0).getLaunchTime(); final Calendar cal = Calendar.getInstance(); cal.setTime(launchDate); cal.add(Calendar.DATE, Integer.parseInt(LoadBalancingWorkerProperties.EXPIRATION_DAYS)); try ( final TransactionResource db = Entities.transactionFor( LoadBalancerServoInstance.class ) ) { final LoadBalancerServoInstance entity = Entities.uniqueResult(LoadBalancerServoInstance.named(instance.getInstanceId())); entity.setCertificateExpiration(cal.getTime()); expired = entity.isCertificateExpired(); Entities.persist(entity); db.commit(); } } }catch(final Exception ex) { LOG.warn("Failed to update ELB worker's certificate expiration date", ex); } if (expired || instance.isCertificateExpired()) { throw new InternalFailureException(String.format("LoadBalancing worker(%s)'s certificate has expired. Contact Cloud Administrator.", instance.getInstanceId())); } } } } }catch(final LoadBalancingException ex) { throw ex; }catch(final Exception ex) { throw new LoadBalancingException("Error while checking loadbalancing worker's certificate expiration", ex); } } @QuantityMetricFunction( LoadBalancerMetadata.class ) public enum CountLoadBalancers implements Function<OwnerFullName, Long> { INSTANCE; @Override public Long apply( final OwnerFullName input ) { try ( final TransactionResource db = Entities.transactionFor( LoadBalancer.class ) ) { return Entities.count( LoadBalancer.named( input, null ) ); } } } }