com.eucalyptus.loadbalancing.dns.LoadBalancerResolver.java Source code

Java tutorial

Introduction

Here is the source code for com.eucalyptus.loadbalancing.dns.LoadBalancerResolver.java

Source

/*************************************************************************
 * 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.dns;

import static com.eucalyptus.loadbalancing.activities.LoadBalancerAutoScalingGroup.LoadBalancerAutoScalingGroupEntityTransform.*;
import static com.eucalyptus.loadbalancing.activities.LoadBalancerServoInstance.LoadBalancerServoInstanceCoreView;
import static com.eucalyptus.util.dns.DnsResolvers.DnsRequest;
import static com.eucalyptus.util.dns.DnsResolvers.DnsResponse;

import java.net.InetAddress;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import org.apache.log4j.Logger;
import org.xbill.DNS.Name;
import org.xbill.DNS.Record;

import com.eucalyptus.bootstrap.Bootstrap;
import com.eucalyptus.configurable.ConfigurableClass;
import com.eucalyptus.configurable.ConfigurableField;
import com.eucalyptus.entities.Entities;
import com.eucalyptus.entities.TransactionResource;
import com.eucalyptus.loadbalancing.LoadBalancer;
import com.eucalyptus.loadbalancing.LoadBalancerDnsRecord;
import com.eucalyptus.loadbalancing.LoadBalancers;
import com.eucalyptus.loadbalancing.activities.LoadBalancerAutoScalingGroup.LoadBalancerAutoScalingGroupCoreView;
import com.eucalyptus.util.Pair;
import com.eucalyptus.util.dns.DnsResolvers;
import com.eucalyptus.util.dns.DomainNameRecords;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.net.InetAddresses;

/**
 *
 */
@ConfigurableClass(root = "services.loadbalancing", description = "Parameters controlling loadbalancing")
public class LoadBalancerResolver extends DnsResolvers.DnsResolver {

    private static final Logger logger = Logger.getLogger(LoadBalancerResolver.class);
    private static final int QUERY_ANSWER_EXPIRE_AFTER_SEC = 5;
    private static final LoadingCache<Name, List<String>> cachedAnswers = CacheBuilder.newBuilder()
            .maximumSize(1000).expireAfterWrite(QUERY_ANSWER_EXPIRE_AFTER_SEC, TimeUnit.SECONDS)
            .build(new CacheLoader<Name, List<String>>() {
                @Override
                public List<String> load(final Name name) throws Exception {
                    return resolveName(name);
                }
            });

    /// the LoadingCache is used to set the hard limit on memory usage
    private static final LoadingCache<Name, IpPermutation> nameToIpPermutations = CacheBuilder.newBuilder()
            .maximumSize(1000).expireAfterAccess(1, TimeUnit.HOURS).build(new CacheLoader<Name, IpPermutation>() {
                @Override
                public IpPermutation load(Name name) throws Exception {
                    final List<String> ips = cachedAnswers.get(name);
                    return new IpPermutation(ips);
                }
            });

    @ConfigurableField(description = "Enable the load balancing DNS resolver.  Note: dns.enable must also be 'true'")
    public static Boolean dns_resolver_enabled = Boolean.TRUE;

    @Override
    public boolean checkAccepts(final DnsRequest request) {
        final Record query = request.getQuery();
        if (!Bootstrap.isOperational() || !dns_resolver_enabled) {
            return false;
        } else if (query.getName().subdomain(LoadBalancerDomainName.getLoadBalancerSubdomain())) {
            return true;
        }
        return false;
    }

    private static List<String> resolveName(final Name name) {
        final Name hostName = name.relativize(LoadBalancerDomainName.getLoadBalancerSubdomain());
        final Optional<LoadBalancerDomainName> domainName = LoadBalancerDomainName.findMatching(hostName);
        final Set<String> ips = Sets.newTreeSet();
        if (domainName.isPresent()) {
            final Pair<String, String> accountNamePair = domainName.get().toScopedLoadBalancerName(hostName);
            try (final TransactionResource tx = Entities.transactionFor(LoadBalancer.class)) {
                final LoadBalancer loadBalancer = LoadBalancers.getLoadbalancer(accountNamePair.getLeft(),
                        accountNamePair.getRight());
                final Predicate<LoadBalancerServoInstanceCoreView> canResolve = new Predicate<LoadBalancerServoInstanceCoreView>() {
                    @Override
                    public boolean apply(LoadBalancerServoInstanceCoreView arg0) {
                        return arg0.canResolveDns();
                    }
                };

                final List<LoadBalancerServoInstanceCoreView> servos = Lists.newArrayList();
                for (final LoadBalancerAutoScalingGroupCoreView group : loadBalancer.getAutoScaleGroups()) {
                    servos.addAll(INSTANCE.apply(group).getServos());
                }
                final Function<LoadBalancerServoInstanceCoreView, String> ipExtractor = loadBalancer
                        .getScheme() == LoadBalancer.Scheme.Internal ? LoadBalancerServoInstanceCoreView.privateIp()
                                : LoadBalancerServoInstanceCoreView.address();
                Iterables.addAll(ips, Iterables.transform(Collections2.filter(servos, canResolve), ipExtractor));
            }
        }

        List<String> ipList = Lists.newArrayList(ips);
        Collections.sort(ipList);
        return ipList;
    }

    private static class IpPermutation {
        private List<String> ips = null;
        private List<List<String>> ipPermutations = null;
        private int idx = 0;

        private IpPermutation(final List<String> ips) {
            this.ips = ips;
            ipPermutations = Lists.newArrayList(Collections2.permutations(ips));
            Collections.shuffle(ipPermutations);
        }

        public synchronized List<String> getNext() {
            try {
                final List<String> next = ipPermutations.get(idx);
                if (++idx >= ipPermutations.size())
                    idx = 0;
                return next;
            } catch (final Exception ex) {
                return Lists.newArrayList();
            }
        }

        public boolean isPermuatationFrom(final List<String> ips) {
            return this.ips.equals(ips);
        }
    }

    private static List<String> getIps(final Name name) throws ExecutionException {
        final List<String> ips = cachedAnswers.get(name);
        final IpPermutation old = nameToIpPermutations.get(name);
        if (!old.isPermuatationFrom(ips))
            nameToIpPermutations.invalidate(name);
        return nameToIpPermutations.get(name).getNext();
    }

    @Override
    public DnsResponse lookupRecords(final DnsRequest request) {
        final Record query = request.getQuery();
        try {
            final Name name = query.getName();
            final List<String> ips = getIps(name);
            final List<Record> records = Lists.newArrayList();
            for (String ip : ips) {
                final InetAddress inetAddress = InetAddresses.forString(ip);
                records.add(DomainNameRecords.addressRecord(name, inetAddress,
                        LoadBalancerDnsRecord.getLoadbalancerTTL()));
            }
            if (DnsResolvers.RequestType.A.apply(query))
                return DnsResponse.forName(name).answer(records);
            else
                return DnsResponse.forName(name).answer(Lists.<Record>newArrayList());
        } catch (Exception ex) {

            logger.debug(ex);
        }
        return DnsResponse.forName(query.getName()).nxdomain();
    }
}