/************************************************************************* * 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.network; import com.eucalyptus.bootstrap.Bootstrap; import com.eucalyptus.bootstrap.Databases; import com.eucalyptus.bootstrap.Hosts; import com.eucalyptus.cluster.Clusters; import com.eucalyptus.cluster.NetworkInfo; import com.eucalyptus.cluster.common.internal.Cluster; import com.eucalyptus.component.Topology; import com.eucalyptus.component.id.Eucalyptus; import com.eucalyptus.compute.common.internal.network.NetworkGroup; import com.eucalyptus.compute.common.internal.vpc.DhcpOptionSet; import com.eucalyptus.compute.common.internal.vpc.InternetGateway; import com.eucalyptus.compute.common.internal.vpc.NatGateway; import com.eucalyptus.compute.common.internal.vpc.NetworkAcl; import com.eucalyptus.compute.common.internal.vpc.NetworkInterface; import com.eucalyptus.compute.common.internal.vpc.RouteTable; import com.eucalyptus.compute.common.internal.vpc.Vpc; import com.eucalyptus.compute.common.internal.vpc.Subnet; import com.eucalyptus.compute.vpc.EventFiringVpcRouteStateInvalidator; import com.eucalyptus.compute.vpc.RouteKey; import com.eucalyptus.compute.vpc.VpcRouteStateInvalidator; import com.eucalyptus.entities.EntityCache; import com.eucalyptus.event.ClockTick; import com.eucalyptus.event.Listeners; import com.eucalyptus.event.EventListener; import com.eucalyptus.network.NetworkInfoBroadcasts.NatGatewayNetworkView; import com.eucalyptus.network.applicator.ApplicatorException; import com.eucalyptus.network.applicator.Applicators; import com.eucalyptus.network.config.NetworkConfiguration; import com.eucalyptus.network.config.NetworkConfigurations; import com.eucalyptus.network.NetworkInfoBroadcasts.VmInstanceNetworkView; import com.eucalyptus.network.NetworkInfoBroadcasts.NetworkGroupNetworkView; import com.eucalyptus.network.NetworkInfoBroadcasts.VpcNetworkView; import com.eucalyptus.network.NetworkInfoBroadcasts.SubnetNetworkView; import com.eucalyptus.network.NetworkInfoBroadcasts.DhcpOptionSetNetworkView; import com.eucalyptus.network.NetworkInfoBroadcasts.NetworkAclNetworkView; import com.eucalyptus.network.NetworkInfoBroadcasts.RouteTableNetworkView; import com.eucalyptus.network.NetworkInfoBroadcasts.InternetGatewayNetworkView; import com.eucalyptus.network.NetworkInfoBroadcasts.NetworkInterfaceNetworkView; import com.eucalyptus.network.NetworkInfoBroadcasts.NetworkInfoSource; import com.eucalyptus.network.NetworkInfoBroadcasts.VersionedNetworkView; import com.eucalyptus.system.Threads; import com.eucalyptus.util.HasName; import com.eucalyptus.util.LockResource; import com.eucalyptus.util.SemaphoreResource; import com.eucalyptus.util.TypeMappers; import com.eucalyptus.compute.common.internal.vm.VmInstance; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.google.common.hash.Funnel; import com.google.common.hash.HashFunction; import com.google.common.hash.Hasher; import com.google.common.hash.PrimitiveSink; import com.google.common.io.BaseEncoding; import com.google.common.primitives.Ints; import org.apache.log4j.Logger; import org.hibernate.criterion.Restrictions; import javax.annotation.Nullable; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import static com.eucalyptus.compute.common.internal.vm.VmInstance.VmStateSet.TORNDOWN; import static com.google.common.hash.Hashing.goodFastHash; /** * */ @SuppressWarnings( { "Guava", "Convert2Lambda", "StaticPseudoFunctionalStyleMethod" } ) public class NetworkInfoBroadcaster { private static final Logger logger = Logger.getLogger( NetworkInfoBroadcaster.class ); private static final AtomicLong lastBroadcastTime = new AtomicLong( 0L ); private static final Lock lastBroadcastTimeLock = new ReentrantLock( ); private static final Semaphore activeBroadcastSemaphore = new Semaphore( 1 ); private static final EntityCache<VmInstance,NetworkInfoBroadcasts.VmInstanceNetworkView> instanceCache = new EntityCache<>( VmInstance.named(null), Restrictions.not( VmInstance.criterion( TORNDOWN.array( ) ) ), Sets.newHashSet( "networkGroups" ), Sets.newHashSet( "bootRecord.machineImage", "bootRecord.vmType" ), TypeMappers.lookup( VmInstance.class, VmInstanceNetworkView.class ) ); private static final EntityCache<NetworkGroup,NetworkGroupNetworkView> securityGroupCache = new EntityCache<>( NetworkGroup.withNaturalId( null ), TypeMappers.lookup( NetworkGroup.class, NetworkGroupNetworkView.class ) ); private static final EntityCache<Vpc,VpcNetworkView> vpcCache = new EntityCache<>( Vpc.exampleWithOwner( null ), TypeMappers.lookup( Vpc.class, VpcNetworkView.class ) ); private static final EntityCache<Subnet,SubnetNetworkView> subnetCache = new EntityCache<>( Subnet.exampleWithOwner( null ), TypeMappers.lookup( Subnet.class, SubnetNetworkView.class ) ); private static final EntityCache<DhcpOptionSet,DhcpOptionSetNetworkView> dhcpOptionsCache = new EntityCache<>( DhcpOptionSet.exampleWithOwner( null ), TypeMappers.lookup( DhcpOptionSet.class, DhcpOptionSetNetworkView.class ) ); private static final EntityCache<NetworkAcl,NetworkAclNetworkView> networkAclCache = new EntityCache<>( NetworkAcl.exampleWithOwner( null ), TypeMappers.lookup( NetworkAcl.class, NetworkAclNetworkView.class ) ); private static final EntityCache<RouteTable,RouteTableNetworkView> routeTableCache = new EntityCache<>( RouteTable.exampleWithOwner( null ), TypeMappers.lookup( RouteTable.class, RouteTableNetworkView.class ) ); private static final EntityCache<InternetGateway,InternetGatewayNetworkView> internetGatewayCache = new EntityCache<>( InternetGateway.exampleWithOwner( null ), TypeMappers.lookup( InternetGateway.class, InternetGatewayNetworkView.class ) ); private static final EntityCache<NetworkInterface,NetworkInterfaceNetworkView> networkInterfaceCache = new EntityCache<>( NetworkInterface.exampleWithOwner( null ), TypeMappers.lookup( NetworkInterface.class, NetworkInterfaceNetworkView.class ) ); private static final EntityCache<NatGateway,NatGatewayNetworkView> natGatewayCache = new EntityCache<>( NatGateway.exampleWithOwner( null ), TypeMappers.lookup( NatGateway.class, NatGatewayNetworkView.class ) ); private static final VpcRouteStateInvalidator vpcRouteStateInvalidator = new EventFiringVpcRouteStateInvalidator( ); private static NetworkInfoSource cacheSource( ) { final Supplier<Iterable<VmInstanceNetworkView>> instanceSupplier = Suppliers.memoize( instanceCache ); final Supplier<Iterable<NetworkGroupNetworkView>> securityGroupSupplier = Suppliers.memoize( securityGroupCache ); final Supplier<Iterable<VpcNetworkView>> vpcSupplier = Suppliers.memoize( vpcCache ); final Supplier<Iterable<SubnetNetworkView>> subnetSupplier = Suppliers.memoize( subnetCache ); final Supplier<Iterable<DhcpOptionSetNetworkView>> dhcpOptionsSupplier = Suppliers.memoize( dhcpOptionsCache ); final Supplier<Iterable<NetworkAclNetworkView>> networkAclSupplier = Suppliers.memoize( networkAclCache ); final Supplier<Iterable<RouteTableNetworkView>> routeTableSupplier = Suppliers.memoize( routeTableCache ); final Supplier<Iterable<InternetGatewayNetworkView>> internetGatewaySupplier = Suppliers.memoize( internetGatewayCache ); final Supplier<Iterable<NetworkInterfaceNetworkView>> networkInterfaceSupplier = Suppliers.memoize( networkInterfaceCache ); final Supplier<Iterable<NatGatewayNetworkView>> natGatewaySupplier = Suppliers.memoize( natGatewayCache ); return new NetworkInfoSource( ) { @Override public Iterable<VmInstanceNetworkView> getInstances( ) { return instanceSupplier.get( ); } @Override public Iterable<NetworkGroupNetworkView> getSecurityGroups( ) { return securityGroupSupplier.get( ); } @Override public Iterable<VpcNetworkView> getVpcs( ) { return vpcSupplier.get( ); } @Override public Iterable<SubnetNetworkView> getSubnets( ) { return subnetSupplier.get( ); } @Override public Iterable<DhcpOptionSetNetworkView> getDhcpOptionSets( ) { return dhcpOptionsSupplier.get( ); } @Override public Iterable<NetworkAclNetworkView> getNetworkAcls( ) { return networkAclSupplier.get( ); } @Override public Iterable<RouteTableNetworkView> getRouteTables( ) { return routeTableSupplier.get( ); } @Override public Iterable<InternetGatewayNetworkView> getInternetGateways( ) { return internetGatewaySupplier.get( ); } @Override public Iterable<NetworkInterfaceNetworkView> getNetworkInterfaces( ) { return networkInterfaceSupplier.get( ); } @Override public Iterable<NatGatewayNetworkView> getNatGateways( ) { return natGatewaySupplier.get( ); } @Override public Map<String, Iterable<? extends VersionedNetworkView>> getView() { return ImmutableMap.<String, Iterable<? extends VersionedNetworkView>>builder( ) .put( "instance", getInstances( ) ) .put( "security-group", getSecurityGroups( ) ) .put( "vpc", getVpcs( ) ) .put( "subnet", getSubnets( ) ) .put( "dhcp-option-set", getDhcpOptionSets( ) ) .put( "network-acl", getNetworkAcls( ) ) .put( "route-table", getRouteTables( ) ) .put( "internet-gateway", getInternetGateways( ) ) .put( "network-interface", getNetworkInterfaces( ) ) .put( "nat-gateway", getNatGateways( ) ) .build( ); } }; } public static void requestNetworkInfoBroadcast( ) { final long requestedTime = System.currentTimeMillis( ); final Callable<Void> broadcastRequest = new Callable<Void>( ) { @SuppressWarnings( "unused" ) @Override public Void call( ) throws Exception { boolean shouldBroadcast = false; boolean shouldRetryWithDelay = false; try ( final LockResource lock = LockResource.lock( lastBroadcastTimeLock ) ) { final long currentTime = System.currentTimeMillis( ); final long lastBroadcast = lastBroadcastTime.get( ); if ( requestedTime >= lastBroadcast && lastBroadcast + TimeUnit.SECONDS.toMillis( NetworkGroups.MIN_BROADCAST_INTERVAL ) < currentTime && activeBroadcastSemaphore.availablePermits( ) > 0 ) { if ( lastBroadcastTime.compareAndSet( lastBroadcast, currentTime ) ) { shouldBroadcast = true; } else { // re-evaluate broadcastTask( this ); } } else if ( requestedTime >= lastBroadcastTime.get() ) { shouldRetryWithDelay = true; } } if ( shouldBroadcast ) { try { broadcastNetworkInfo( ); } catch( Exception e ) { logger.error( "Error broadcasting network information", e ); } } else if ( shouldRetryWithDelay && !Bootstrap.isShuttingDown( ) ) { Thread.sleep( 100 ); // pause and re-evaluate to allow for min time between broadcasts broadcastTask( this ); } return null; } }; broadcastTask( broadcastRequest ); } private static void broadcastTask( Callable<Void> task ) { Threads.enqueue( Eucalyptus.class, NetworkInfoBroadcaster.class, 5, task ); } @SuppressWarnings( { "UnnecessaryQualifiedReference", "WeakerAccess", "unused" } ) static void broadcastNetworkInfo( ) { try ( final SemaphoreResource semaphore = SemaphoreResource.acquire( activeBroadcastSemaphore ) ) { // populate with info directly from configuration final Optional<NetworkConfiguration> networkConfiguration = NetworkConfigurations.getNetworkConfiguration( ); final List<Cluster> clusters = Clusters.list( ); final List<Cluster> otherClusters = Clusters.listDisabled( ); final NetworkInfoSource source = cacheSource( ); final Set<String> dirtyPublicAddresses = PublicAddresses.dirtySnapshot( ); final Set<RouteKey> invalidStateRoutes = Sets.newHashSetWithExpectedSize( 50 ); final int sourceFingerprint = fingerprint( source, clusters, dirtyPublicAddresses, NetworkGroups.NETWORK_CONFIGURATION ); final NetworkInfo info = NetworkInfoBroadcasts.buildNetworkConfiguration( networkConfiguration, source, Suppliers.ofInstance( clusters ), Suppliers.ofInstance( otherClusters ), new Supplier<String>( ) { @Override public String get() { return Topology.lookup( Eucalyptus.class ).getInetAddress( ).getHostAddress( ); } }, new Function<List<String>, List<String>>( ) { @Nullable @Override public List<String> apply( final List<String> defaultServers ) { return NetworkConfigurations.loadSystemNameservers( defaultServers ); } }, dirtyPublicAddresses, invalidStateRoutes ); info.setVersion( BaseEncoding.base16( ).lowerCase( ).encode( Ints.toByteArray( sourceFingerprint ) ) ); if ( !invalidStateRoutes.isEmpty( ) ) { vpcRouteStateInvalidator.accept( invalidStateRoutes ); } Applicators.apply( clusters, info ); } catch ( ApplicatorException e ) { logger.error( "Error during network broadcast", e ); } } @SuppressWarnings( "serial" ) private static int fingerprint( final NetworkInfoSource source, final List<Cluster> clusters, final Set<String> dirtyPublicAddresses, final String networkConfiguration ) { final HashFunction hashFunction = goodFastHash( 32 ); final Hasher hasher = hashFunction.newHasher( ); final Funnel<VersionedNetworkView> versionedItemFunnel = new Funnel<VersionedNetworkView>() { @SuppressWarnings( "NullableProblems" ) @Override public void funnel( final VersionedNetworkView o, final PrimitiveSink primitiveSink ) { primitiveSink.putString( o.getId( ), StandardCharsets.UTF_8 ); primitiveSink.putChar( '=' ); primitiveSink.putInt( o.getVersion( ) ); } }; for ( final Map.Entry<String,Iterable<? extends VersionedNetworkView>> entry : source.getView( ).entrySet( ) ) { hasher.putString( entry.getKey( ), StandardCharsets.UTF_8 ); for ( final VersionedNetworkView item : entry.getValue( ) ) { hasher.putObject( item, versionedItemFunnel ); } } hasher.putString( Joiner.on( ',' ).join( Sets.newTreeSet( Iterables.transform( clusters, HasName.GET_NAME ) ) ), StandardCharsets.UTF_8 ); hasher.putString( Joiner.on( ',' ).join( Sets.newTreeSet( dirtyPublicAddresses ) ), StandardCharsets.UTF_8 ); hasher.putInt( networkConfiguration.hashCode( ) ); return hasher.hash( ).asInt( ); } @SuppressWarnings( { "WeakerAccess", "unused" } ) public static class NetworkInfoBroadcasterEventListener implements EventListener<ClockTick> { private final int intervalTicks = 3; private volatile int counter = 0; public static void register( ) { Listeners.register( ClockTick.class, new NetworkInfoBroadcasterEventListener( ) ); } @SuppressWarnings("UnnecessaryQualifiedReference") @Override public void fireEvent( final ClockTick event ) { if ( counter++%intervalTicks == 0 && Topology.isEnabledLocally( Eucalyptus.class ) && Hosts.isCoordinator( ) && Bootstrap.isOperational( ) && !Databases.isVolatile( ) ) { requestNetworkInfoBroadcast( ); } } } }