package com.eucalyptus.address; import java.net.Inet4Address; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.List; import java.util.NoSuchElementException; import org.apache.log4j.Logger; import com.eucalyptus.bootstrap.Component; import com.eucalyptus.cluster.Cluster; import com.eucalyptus.cluster.VmInstance; import com.eucalyptus.cluster.VmInstances; import com.eucalyptus.entities.EntityWrapper; import com.eucalyptus.util.Exceptions; import com.eucalyptus.util.LogUtil; import com.eucalyptus.util.NotEnoughResourcesAvailable; import com.eucalyptus.vm.VmState; import com.google.common.collect.Lists; import edu.ucsb.eucalyptus.cloud.exceptions.ExceptionList; public abstract class AbstractSystemAddressManager { static Logger LOG = Logger.getLogger( AbstractSystemAddressManager.class ); public Address allocateNext( String userId ) throws NotEnoughResourcesAvailable { Address addr = Addresses.getInstance( ).enableFirst( ).allocate( userId ); LOG.debug( "Allocated address for public addressing: " + addr.toString( ) ); if ( addr == null ) { LOG.debug( LogUtil.header( Addresses.getInstance( ).toString( ) ) ); throw new NotEnoughResourcesAvailable( ExceptionList.ERR_SYS_INSUFFICIENT_ADDRESS_CAPACITY ); } return addr; } public abstract void assignSystemAddress( final VmInstance vm ) throws NotEnoughResourcesAvailable; public abstract List<Address> getReservedAddresses( ); public abstract void inheritReservedAddresses( List<Address> previouslyReservedAddresses ); public abstract List<Address> allocateSystemAddresses( String cluster, int count ) throws NotEnoughResourcesAvailable; public void update( Cluster cluster, List<ClusterAddressInfo> ccList ) { if ( !cluster.getState( ).isAddressingInitialized( ) ) { Helper.loadStoredAddresses( cluster ); cluster.getState( ).setAddressingInitialized( true ); } for ( ClusterAddressInfo addrInfo : ccList ) { try { Address address = Helper.lookupOrCreate( cluster, addrInfo ); if ( address.isAssigned( ) ) { if ( Address.UNALLOCATED_USERID.equals( address.getUserId( ) ) ) { Helper.markAsAllocated( cluster, addrInfo, address ); } try { VmInstance vm = VmInstances.getInstance( ).lookupByInstanceIp( addrInfo.getInstanceIp( ) ); cluster.getState( ).clearOrphan( addrInfo ); } catch ( NoSuchElementException e ) { InetAddress addr = null; try { addr = Inet4Address.getByName( addrInfo.getInstanceIp( ) ); } catch ( UnknownHostException e1 ) { LOG.debug( e1, e1 ); } if ( addr == null || !addr.isLoopbackAddress( ) ) { cluster.getState( ).handleOrphan( addrInfo ); } } } else if ( address.isAllocated( ) && Address.UNALLOCATED_USERID.equals( address.getUserId( ) ) && !address.isPending( ) ) { Helper.markAsAllocated( cluster, addrInfo, address ); } } catch ( Throwable e ) { LOG.debug( e, e ); } } } protected static class Helper { protected static Address lookupOrCreate( Cluster cluster, ClusterAddressInfo addrInfo ) { Address addr = null; VmInstance vm = null; try { addr = Addresses.getInstance( ).lookupDisabled( addrInfo.getAddress( ) ); LOG.trace( "Found address in the inactive set cache: " + addr ); } catch ( NoSuchElementException e1 ) { try { addr = Addresses.getInstance( ).lookup( addrInfo.getAddress( ) ); LOG.trace( "Found address in the active set cache: " + addr ); } catch ( NoSuchElementException e ) {} } Helper.checkUniqueness( addrInfo ); if ( addrInfo.hasMapping( ) ) { vm = Helper.maybeFindVm( addrInfo.getAddress( ), addrInfo.getInstanceIp( ) ); if ( addr != null && vm != null ) { Helper.ensureAllocated( addr, vm ); cluster.getState( ).clearOrphan( addrInfo ); } else if ( addr != null && vm != null && vm.getState( ).ordinal( ) > VmState.RUNNING.ordinal( ) ) { cluster.getState( ).handleOrphan( addrInfo ); } else if ( addr != null && vm == null ) { cluster.getState( ).handleOrphan( addrInfo ); } else if ( addr == null && vm != null ) { addr = new Address( addrInfo.getAddress( ), cluster.getName( ), Component.eucalyptus.name( ), vm.getInstanceId( ), vm.getPrivateAddress( ) ); cluster.getState( ).clearOrphan( addrInfo ); } else if( addr == null && vm == null ) { addr = new Address( addrInfo.getAddress( ), cluster.getName( ) ); cluster.getState().handleOrphan( addrInfo ); } } else { if( addr != null && addr.isAssigned( ) && !addr.isPending( ) ) { cluster.getState( ).handleOrphan( addrInfo ); } else if( addr != null && !addr.isAssigned( ) && !addr.isPending( ) && addr.isSystemOwned( ) ) { try { addr.release( ); } catch ( Exception ex ) { LOG.error( ex ); } } else if( addr != null && Address.Transition.system.equals( addr.getTransition( ) ) ) { cluster.getState( ).handleOrphan( addrInfo ); } else if( addr == null ) { addr = new Address( addrInfo.getAddress( ), cluster.getName( ) ); Helper.clearVmState( addrInfo ); } } return addr; } private static void markAsAllocated( Cluster cluster, ClusterAddressInfo addrInfo, Address address ) { try { if( !address.isPending( ) ) { for ( VmInstance vm : VmInstances.getInstance( ).listValues( ) ) { if ( addrInfo.getInstanceIp( ).equals( vm.getPrivateAddress( ) ) && VmState.RUNNING.equals( vm.getState( ) ) ) { LOG.warn( "Out of band address state change: " + LogUtil.dumpObject( addrInfo ) + " address=" + address + " vm=" + vm ); if( !address.isAllocated( ) ) { address.pendingAssignment( ).assign( vm.getInstanceId( ), vm.getPrivateAddress( ) ).clearPending( ); } else { address.assign( vm.getInstanceId( ), vm.getPrivateAddress( ) ).clearPending( ); } cluster.getState( ).clearOrphan( addrInfo ); return; } } } } catch ( IllegalStateException e ) { LOG.error( e ); } } private static void clearAddressCachedState( Address addr ) { try { if( !addr.isPending( ) ) { addr.unassign( ).clearPending( ); } } catch ( Throwable t ) { LOG.trace( t, t ); } } private static void clearVmState( ClusterAddressInfo addrInfo ) { try { VmInstance vm = VmInstances.getInstance( ).lookupByPublicIp( addrInfo.getAddress( ) ); vm.updatePublicAddress( vm.getPrivateAddress( ) ); } catch ( NoSuchElementException e ) { } } private static VmInstance maybeFindVm( String publicIp, String privateIp ) { VmInstance vm = null; try { vm = VmInstances.getInstance( ).lookupByInstanceIp( privateIp ); LOG.trace( "Candidate vm which claims this address: " + vm.getInstanceId( ) + " " + vm.getState( ) + " " + publicIp ); if ( publicIp.equals( vm.getPublicAddress( ) ) ) { LOG.trace( "Found vm which claims this address: " + vm.getInstanceId( ) + " " + vm.getState( ) + " " + publicIp ); return vm; } } catch ( NoSuchElementException e ) {} return null; } private static void ensureAllocated( Address addr, VmInstance vm ) { if ( !addr.isAllocated( ) && !addr.isPending( ) ) { try { if ( !addr.isAssigned( ) && !addr.isPending( ) ) { addr.pendingAssignment( ); try { addr.assign( vm.getInstanceId( ), vm.getPrivateAddress( ) ).clearPending( ); } catch ( Throwable e1 ) { LOG.debug( e1, e1 ); } } } catch ( Throwable e1 ) { LOG.debug( e1, e1 ); } } else if ( !addr.isAssigned( ) ) { try { addr.assign( vm.getInstanceId( ), vm.getPrivateAddress( ) ).clearPending( ); } catch ( Throwable e1 ) { LOG.debug( e1, e1 ); } } else { LOG.debug( "Address usage checked: " + addr ); } } private static void checkUniqueness( ClusterAddressInfo addrInfo ) { int vmCount = VmInstances.getInstance( ).countByPublicIp( addrInfo.getAddress( ) ); if ( vmCount > 1 ) { String vmList = ""; for ( VmInstance v : VmInstances.getInstance( ).listValues( ) ) { if ( addrInfo.getAddress( ).equals( v.getPublicAddress( ) ) && ( VmState.PENDING.equals( v.getState( ) ) || VmState.RUNNING.equals( v.getState( ) ) ) ) { vmList += " " + v.getInstanceId( ) + "(" + v.getState( ) + ")"; } } LOG.error( "Found " + vmCount + " vms with the same address: " + addrInfo + " -> " + vmList ); //TODO: handle reconciling state. } } protected static void loadStoredAddresses( Cluster cluster ) { try { EntityWrapper<Address> db = new EntityWrapper<Address>( ); Address clusterAddr = new Address( ); clusterAddr.setCluster( cluster.getName( ) ); List<Address> addrList = Lists.newArrayList( ); try { addrList = db.query( clusterAddr ); db.commit( ); } catch ( Exception e1 ) { db.rollback( ); } for ( Address addr : addrList ) { try { LOG.info( "Restoring persistent address info for: " + addr ); Addresses.getInstance( ).lookup( addr.getName( ) ); addr.init( ); } catch ( Throwable e ) { addr.init( ); } } } catch ( Throwable e ) { LOG.debug( e, e ); } } } }