/************************************************************************* * 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. * * This file may incorporate work covered under the following copyright * and permission notice: * * Software License Agreement (BSD License) * * Copyright (c) 2008, Regents of the University of California * All rights reserved. * * Redistribution and use of this software in source and binary forms, * with or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE * THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL, * COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE, * AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING * IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, * SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, * WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION, * REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO * IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT * NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS. ************************************************************************/ package com.eucalyptus.vm.dns; import static com.eucalyptus.util.dns.DnsResolvers.DnsRequest; import java.net.InetAddress; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.log4j.Logger; import org.hibernate.criterion.Conjunction; import org.hibernate.criterion.Example; import org.hibernate.criterion.Restrictions; import org.xbill.DNS.Name; import org.xbill.DNS.Record; import com.eucalyptus.bootstrap.Bootstrap; import com.eucalyptus.bootstrap.Databases; import com.eucalyptus.component.Components; import com.eucalyptus.component.ServiceConfiguration; import com.eucalyptus.cluster.common.ClusterController; import com.eucalyptus.compute.common.internal.vm.VmInstance; import com.eucalyptus.compute.common.internal.vpc.Vpc; import com.eucalyptus.compute.common.internal.vpc.VpcMetadataNotFoundException; import com.eucalyptus.compute.common.internal.vpc.Vpcs; import com.eucalyptus.compute.common.network.Networking; import com.eucalyptus.compute.common.network.NetworkingFeature; import com.eucalyptus.compute.vpc.persist.PersistenceVpcs; import com.eucalyptus.configurable.ConfigurableClass; import com.eucalyptus.configurable.ConfigurableField; import com.eucalyptus.entities.Entities; import com.eucalyptus.entities.TransactionResource; import com.eucalyptus.network.IPRange; import com.eucalyptus.network.config.Cluster; import com.eucalyptus.network.config.NetworkConfiguration; import com.eucalyptus.network.config.NetworkConfigurations; import com.eucalyptus.util.Cidr; import com.eucalyptus.util.Classes; import com.eucalyptus.util.FUtils; import com.eucalyptus.util.Subnets; import com.eucalyptus.util.Subnets.SystemSubnetPredicate; import com.eucalyptus.util.ThrowingFunction; import com.eucalyptus.util.dns.DnsResolvers; import com.eucalyptus.util.dns.DnsResolvers.DnsResolver; import com.eucalyptus.util.dns.DnsResolvers.DnsResponse; import com.eucalyptus.util.dns.DnsResolvers.RequestType; import com.eucalyptus.util.dns.DomainNameRecords; import com.google.common.base.Function; import com.google.common.base.MoreObjects; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.net.InetAddresses; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @ConfigurableClass( root = "dns.split_horizon", description = "Options controlling Split-Horizon DNS resolution." ) public abstract class SplitHorizonResolver extends DnsResolver { private static final Logger LOG = Logger.getLogger( SplitHorizonResolver.class ); private static final Supplier<Boolean> privateNetworksManagedSupplier = Suppliers.memoizeWithExpiration( PrivateNetworksManaged.INSTANCE, 15, TimeUnit.SECONDS ); private static final Supplier<Predicate<InetAddress>> publicAddressPredicateSupplier = Suppliers.memoizeWithExpiration( PublicAddresses.INSTANCE, 15, TimeUnit.SECONDS ); private static final Supplier<Iterable<Cidr>> clusterSubnetsSupplier = Suppliers.memoizeWithExpiration( ClusterSubnets.INSTANCE, 1, TimeUnit.MINUTES ); @ConfigurableField( description = "Enable the split-horizon DNS resolution for internal instance public DNS name queries. " + "Note: dns.enable must also be 'true'", initial = "true" ) public static Boolean enabled = Boolean.TRUE; private static final LoadingCache<VmDnsCacheKey, Optional<VmDnsInfo>> instanceCache = CacheBuilder.newBuilder( ) .maximumSize( 25_000 ) .refreshAfterWrite( 5, TimeUnit.SECONDS ) .build( new VmDnsCacheLoader( ) ); private static final LoadingCache<String,Optional<VpcDnsInfo>> vpcCache = CacheBuilder.newBuilder( ) .refreshAfterWrite( 1, TimeUnit.MINUTES ) .build( new VpcDnsCacheLoader( ) ); /** * Resolve the network (cidr) for an instance (i.e. a VPC or EC2-Classic) */ public static Optional<Cidr> lookupNetwork( final InetAddress address ) { final Optional<VmDnsInfo> vmInfo = privateNetworksManagedSupplier.get() ? lookupPublic( address ) : lookupAny( address ); if ( vmInfo.isPresent( ) ) { final String vpcId = vmInfo.get( ).getVpcId( ); if ( vpcId != null ) { return vpcCache.getUnchecked( vpcId ).transform( VpcDnsInfo.cidr( ) ); } else { final InetAddress privateAddress = InetAddresses.forString( vmInfo.get( ).getPrivateIp( ) ); for ( final Cidr cidr : clusterSubnetsSupplier.get( ) ) { if ( cidr.apply( privateAddress ) ) { return Optional.of( cidr ); } } } } return Optional.absent( ); } /** * Test whether the address is one which belongs to an instance or is external. * * The checks are as follows: * 1. If the system has not yet fully bootstrapped, return false. * 2. If the address is a registered public address, return true. * 3. If the address falls w/in a subnet owned by a registered cluster, return true. * 4. Otherwise, return false. */ @SystemSubnetPredicate public static class SystemInternalSubnet implements Predicate<InetAddress> { /** * @see com.google.common.base.Predicate#apply(Object) * @return true if the address is internal */ @Override public boolean apply( final InetAddress input ) { if ( !Bootstrap.isOperational( ) ) { return false; } else if ( publicAddressPredicateSupplier.get( ).apply( input ) ) { return true; } else { return lookupPublic( input ).isPresent( ) || !privateNetworksManagedSupplier.get( ) && Predicates.or( clusterSubnetsSupplier.get( ) ).apply( input ); } } } /** * Do the split-horizon DNS lookup. The request here is necessarily from an internal instance * because {@link SplitHorizonResolver#checkAccepts(DnsRequest)} only allows for * source addresses which are system internal. * * The procedure is to: * 1. Check we can parse the subdomain; otherwise fail w/ UNKNOWN * 2. Parse out the ip address; otherwise fail w/ NXDOMAIN * 3. Verify the existence of an instance for the indicate ip; otherwise fail w/ NXDOMAIN * 4. Construct the response record accordingly; otherwise fail w/ NXDOMAIN * * @see DnsResolvers#findRecords(org.xbill.DNS.Message, DnsResolvers.DnsRequest) */ @Override public DnsResponse lookupRecords( DnsRequest request ) { final Record query = request.getQuery( ); try{ if ( RequestType.PTR.apply( query ) ) { final InetAddress ip = DomainNameRecords.inAddrArpaToInetAddress( query.getName( ) ); final Optional<VmDnsInfo> vmInfo = lookupAny( ip ); if ( vmInfo.isPresent( ) ) { final String hostAddress = ip.getHostAddress( ); if ( hostAddress.equals( vmInfo.get( ).getPrivateIp( ) ) && ip.isSiteLocalAddress()) { final Name dnsName = InstanceDomainNames.fromInetAddress( InstanceDomainNames.INTERNAL, ip ); return DnsResponse.forName( query.getName( ) ).answer( DomainNameRecords.ptrRecord( dnsName, ip ) ); } else if ( hostAddress.equals( vmInfo.get( ).getPublicIp( ) ) ) { final Name dnsName = InstanceDomainNames.fromInetAddress( InstanceDomainNames.EXTERNAL, ip ); return DnsResponse.forName( query.getName( ) ).answer( DomainNameRecords.ptrRecord( dnsName, ip ) ); }else { return null; } } else { // reverse lookup of non-EUCA ip will be resolved by recursive resolver return null; } } }catch( final Exception ex ){ LOG.debug(ex); } return DnsResponse.forName( query.getName( ) ).nxdomain( ); } /** * Enforces that this resolver is only used under the following conditions: * 1. The system is currently operational (e.g., database access is safe) * 2. This resolver is enabled * 3. The source ip address is system controlled; either a public address or in a vnet subnet * 4. The request name is a subdomain request for the subdomains the system should respond * * @see com.eucalyptus.util.dns.DnsResolvers#findRecords(org.xbill.DNS.Message, DnsRequest) */ @Override public boolean checkAccepts( final DnsRequest request ) { final Record query = request.getQuery( ); if ( !Bootstrap.isOperational( ) || !enabled ) { return false; } else if ( InstanceDomainNames.isInstanceDomainName( query.getName( ) ) ) { return true; } else if ( RequestType.PTR.apply( query ) ) { return true; } return false; } private static Optional<VmDnsInfo> lookupAny( InetAddress ip ) { return lookupPrivate( ip ).or( lookupPublic( ip ) ); } private static Optional<VmDnsInfo> lookupPrivate( InetAddress ip ) { return instanceCache.getUnchecked( VmDnsCacheKey.forPrivateAddress( ip ) ); } private static Optional<VmDnsInfo> lookupPublic( InetAddress ip ) { return instanceCache.getUnchecked( VmDnsCacheKey.forPublicAddress( ip ) ); } @Override public String toString( ) { return this.getClass( ).getSimpleName( ); } @SuppressWarnings( "EqualsWhichDoesntCheckParameterClass" ) @Override public boolean equals( Object obj ) { return this.getClass( ).equals( MoreObjects.firstNonNull( Classes.typeOf( obj ), Object.class ) ); } public static class InternalARecordResolver extends SplitHorizonResolver { @Override public DnsResponse lookupRecords( DnsRequest request ) { final Record query = request.getQuery( ); if( RequestType.PTR.apply( query ) ) return super.lookupRecords( request ); try { final Name name = query.getName( ); final Name instanceDomain = InstanceDomainNames.lookupInstanceDomain( name ); final InetAddress ip = InstanceDomainNames.toInetAddress( name.relativize( instanceDomain ) ); if ( ( privateNetworksManagedSupplier.get( ) && ip.isSiteLocalAddress( ) ) || lookupPrivate( ip ).isPresent( ) ) { if ( RequestType.A.apply( query ) ) { final Record rec = DomainNameRecords.addressRecord( name, ip ); return DnsResponse.forName( name ).answer( rec ); } else { return DnsResponse.forName( name ).answer( Lists.<Record>newArrayList() ); } } } catch ( Exception ex ) { LOG.debug( ex ); } return super.lookupRecords( request ); } @Override public boolean checkAccepts( DnsRequest request ) { final Record query = request.getQuery( ); return RequestType.PTR.apply( query ) ? super.checkAccepts( request ) : super.checkAccepts( request ) && ( InstanceDomainNames.isInstanceSubdomain( query.getName( ) ) && !query.getName( ).subdomain( InstanceDomainNames.EXTERNAL.get( ) ) ); } } /** * Handle instance public DNS name lookup from instances */ public static class HorizonARecordResolver extends SplitHorizonResolver { @Override public boolean checkAccepts( DnsRequest request ) { final Record query = request.getQuery( ); final InetAddress source = request.getRemoteAddress( ); return RequestType.PTR.apply( query ) ? super.checkAccepts( request ) : super.checkAccepts( request ) && lookupNetwork( source ).isPresent( ) && query.getName( ).subdomain( InstanceDomainNames.EXTERNAL.get( ) ); } @Override public DnsResponse lookupRecords( DnsRequest request ) { final Record query = request.getQuery( ); if( RequestType.PTR.apply( query ) ) return super.lookupRecords( request ); try { final Name name = query.getName( ); final InetAddress requestIp = InstanceDomainNames.toInetAddress( name.relativize( InstanceDomainNames.EXTERNAL.get( ) ) ); final Optional<Cidr> instanceNetwork = lookupNetwork( request.getRemoteAddress( ) ); final Optional<VmDnsInfo> vmInfo = lookupPublic( requestIp ); if ( vmInfo.isPresent( ) ) { final InetAddress instanceAddress = instanceNetwork.isPresent( ) && instanceNetwork.get( ).contains( vmInfo.get( ).getPrivateIp( ) ) ? InetAddresses.forString( vmInfo.get( ).getPrivateIp( ) ) : requestIp; if ( RequestType.A.apply(query) ) { final Record rec = DomainNameRecords.addressRecord( name, instanceAddress ); return DnsResponse.forName( name ).answer( rec ); } else { return DnsResponse.forName( name ).answer( Lists.<Record>newArrayList() ); } } } catch ( Exception ex ) { LOG.debug( ex ); } return super.lookupRecords( request ); } } /** * Handle instance public DNS name lookup from external hosts */ public static class ExternalARecordResolver extends SplitHorizonResolver { @Override public boolean checkAccepts( DnsRequest request ) { final Record query = request.getQuery( ); final InetAddress source = request.getRemoteAddress( ); return RequestType.PTR.apply( query ) ? super.checkAccepts( request ) : super.checkAccepts( request ) && !lookupNetwork( source ).isPresent( ) && query.getName( ).subdomain( InstanceDomainNames.EXTERNAL.get( ) ); } @Override public DnsResponse lookupRecords( DnsRequest request ) { final Record query = request.getQuery( ); if( RequestType.PTR.apply( query ) ) return super.lookupRecords( request ); try { final Name name = query.getName( ); final InetAddress requestIp = InstanceDomainNames.toInetAddress( name.relativize( InstanceDomainNames.EXTERNAL.get( ) ) ); if ( lookupPublic( requestIp ).isPresent( ) ) { if ( RequestType.A.apply( query ) ) { final Record rec = DomainNameRecords.addressRecord( name, requestIp ); return DnsResponse.forName( name ).answer( rec ); } else { return DnsResponse.forName( name ).answer( Lists.<Record>newArrayList( ) ); } } } catch ( Exception ex ) { LOG.debug( ex ); } return super.lookupRecords( request ); } } private static final class VmDnsCacheLoader extends CacheLoader<VmDnsCacheKey, Optional<VmDnsInfo>> { @Override public Optional<VmDnsInfo> load( @Nonnull final VmDnsCacheKey key ) { return key.load( Optional.<VmDnsInfo>absent( ) ); } @Override public ListenableFuture<Optional<VmDnsInfo>> reload( final VmDnsCacheKey key, final Optional<VmDnsInfo> oldValue ) { if ( Databases.isVolatile( ) ) { return Futures.immediateFuture( oldValue ); } else { return Futures.immediateFuture( key.load( oldValue ) ); } } } private static final class VpcDnsCacheLoader extends CacheLoader<String, Optional<VpcDnsInfo>> { @Override public Optional<VpcDnsInfo> load( @Nonnull final String vpcId ) { try { final Vpcs vpcs = new PersistenceVpcs( ); return vpcs.lookupByName( null, vpcId, new Function<Vpc, Optional<VpcDnsInfo>>( ) { @Override public Optional<VpcDnsInfo> apply( final Vpc vpc ) { try { return Optional.of( new VpcDnsInfo( vpc.getDisplayName( ), Cidr.parse( vpc.getCidr( ) ) ) ); } catch ( IllegalArgumentException e ) { return Optional.absent( ); } } } ); } catch ( VpcMetadataNotFoundException e ) { LOG.debug( "VPC not found for instance network " + vpcId ); } catch ( Throwable t ) { LOG.error( "Error finding VPC for instance network " + vpcId, t ); } return Optional.absent( ); } @Override public ListenableFuture<Optional<VpcDnsInfo>> reload( final String key, final Optional<VpcDnsInfo> oldValue ) { if ( Databases.isVolatile( ) ) { return Futures.immediateFuture( oldValue ); } else { return Futures.immediateFuture( load( key ).or( oldValue ) ); } } } private static abstract class VmDnsCacheKey { @Nonnull public static VmDnsCacheKey forPublicAddress( InetAddress address ) { return new VmPublicIpDnsCacheKey( address.getHostAddress( ) ); } @Nonnull public static VmDnsCacheKey forPrivateAddress( InetAddress address ) { return new VmPrivateIpDnsCacheKey( address.getHostAddress( ) ); } Optional<VmDnsInfo> load( Optional<VmDnsInfo> currentValue ) { try ( TransactionResource db = Entities.readOnlyDistinctTransactionFor( VmInstance.class ) ) { final VmInstance example = example( ); final Conjunction conjunction = Restrictions.conjunction( ); if ( currentValue.isPresent( ) ) { // Load either a different VM than the current one // or the current one if it has been modified conjunction.add( Restrictions.or( Restrictions.and( Restrictions.eq( "naturalId", currentValue.get( ).uuid ), Restrictions.ne( "version", currentValue.get( ).version ) ), Restrictions.ne( "naturalId", currentValue.get( ).uuid ) ) ); } final VmInstance vm = ( VmInstance ) Entities.createCriteriaUnique( VmInstance.class ) .add( Example.create( example ) ) .add( conjunction ) .uniqueResult( ); if ( vm == null ) { return currentValue; } else if ( VmInstance.VmStateSet.RUN.apply( vm ) ) { return Optional.of( new VmDnsInfo( vm.getNaturalId( ), vm.getDisplayName( ), vm.getVersion( ), vm.getPrivateAddress( ), vm.hasPublicAddress( ) ? vm.getPublicAddress( ) : null, vm.getVpcId( ) ) ); } } catch ( Exception ex ) { LOG.error( ex, ex ); } return Optional.absent( ); } abstract VmInstance example( ); } private static final class VmPublicIpDnsCacheKey extends VmDnsCacheKey { private final String ip; public VmPublicIpDnsCacheKey( final String ip ) { this.ip = ip; } @Override VmInstance example( ) { return VmInstance.exampleWithPublicIp( ip ); } @Override public boolean equals( final Object o ) { if ( this == o ) return true; if ( o == null || getClass( ) != o.getClass( ) ) return false; final VmPublicIpDnsCacheKey that = (VmPublicIpDnsCacheKey) o; return Objects.equals( ip, that.ip ); } @Override public int hashCode() { return Objects.hash( ip ); } } private static final class VmPrivateIpDnsCacheKey extends VmDnsCacheKey { private final String ip; public VmPrivateIpDnsCacheKey( final String ip ) { this.ip = ip; } @Override VmInstance example( ) { return VmInstance.exampleWithPrivateIp( ip ); } @Override public boolean equals( final Object o ) { if ( this == o ) return true; if ( o == null || getClass( ) != o.getClass( ) ) return false; final VmPrivateIpDnsCacheKey that = (VmPrivateIpDnsCacheKey) o; return Objects.equals( ip, that.ip ); } @Override public int hashCode() { return Objects.hash( ip ); } } static final class VmDnsInfo { private final String uuid; private final String id; private final Integer version; private final String privateIp; @Nullable private final String publicIp; @Nullable private final String vpcId; public VmDnsInfo( final String uuid, final String id, final Integer version, final String privateIp, final String publicIp, final String vpcId ) { this.uuid = uuid; this.id = id; this.version = version; this.privateIp = privateIp; this.publicIp = publicIp; this.vpcId = vpcId; } public String getPrivateIp( ) { return privateIp; } @Nullable public String getPublicIp( ) { return publicIp; } @Nullable public String getVpcId( ) { return vpcId; } } private static final class VpcDnsInfo { private final String id; private final Cidr cidr; public VpcDnsInfo( final String id, final Cidr cidr ) { this.id = id; this.cidr = cidr; } public Cidr getCidr( ) { return cidr; } static Function<VpcDnsInfo,Cidr> cidr( ) { return VpcDnsInfoProperties.CIDR; } enum VpcDnsInfoProperties implements Function<VpcDnsInfo,Cidr> { CIDR { @Override public Cidr apply( final VpcDnsInfo vpcDnsInfo ) { return vpcDnsInfo.getCidr( ); } }, } } private enum PrivateNetworksManaged implements Supplier<Boolean> { INSTANCE { @Override public Boolean get() { try { return Networking.getInstance( ).supports( NetworkingFeature.SiteLocalManaged ); } catch ( Exception e ) { return false; } } } } private enum PublicAddresses implements Supplier<Predicate<InetAddress>> { INSTANCE { @Override public Predicate<InetAddress> get() { Predicate<InetAddress> predicate = Predicates.alwaysFalse( ); final Optional<NetworkConfiguration> config = NetworkConfigurations.getNetworkConfiguration( ); if ( config.isPresent( ) ) try { final List<IPRange> ranges = ImmutableList.copyOf( Optional.presentInstances( Iterables.transform( config.get( ).getPublicIps( ), IPRange.parse( ) ) ) ); predicate = new Predicate<InetAddress>( ) { @Override public boolean apply( @Nullable final InetAddress inetAddress ) { final IPRange addressAsRange = IPRange.parse( inetAddress.getHostAddress( ) ); boolean contained = false; for ( final IPRange range : ranges ) { contained = range.contains( addressAsRange ); if ( contained ) break; } return contained; } }; } catch ( Exception e ) { LOG.error( "Error building public address predicate", e ); } return predicate; } } } private enum ClusterSubnets implements Supplier<Iterable<Cidr>> { INSTANCE { private final AtomicReference<Iterable<Cidr>> lastLoaded = new AtomicReference<Iterable<Cidr>>( Collections.<Cidr>emptyList( ) ); @Override public Iterable<Cidr> get( ) { final Optional<NetworkConfiguration> configurationOptional = NetworkConfigurations.getNetworkConfiguration( ); if ( !configurationOptional.isPresent( ) || Databases.isVolatile( ) ) { return lastLoaded.get( ); } else try { final Set<String> clusters = Components.services( ClusterController.class ) .map( ServiceConfiguration::getPartition ) .toJavaSet( ); final NetworkConfiguration configuration = NetworkConfigurations.explode( configurationOptional.get( ), clusters ); final List<Cidr> cidrs = ImmutableList.copyOf( Optional.presentInstances( configuration.getClusters( ) .stream( ) .map( Cluster::getSubnet ) .filter( Objects::nonNull ) .map( FUtils.optional( ThrowingFunction.undeclared( subnet -> Subnets.cidr( subnet.getSubnet( ), subnet.getNetmask( ) ) ) ) ) .collect( Collectors.toList( ) ) ) ); lastLoaded.set( cidrs ); return cidrs; } catch ( final Throwable e ) { LOG.error( "Error reloading cluster information, using cached value", e ); return lastLoaded.get( ); } } } } }