/************************************************************************* * 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.component; import java.io.Serializable; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentMap; import javax.annotation.Nullable; import org.apache.log4j.Logger; import com.eucalyptus.auth.Accounts; import com.eucalyptus.auth.principal.UserPrincipal; import com.eucalyptus.bootstrap.BootstrapArgs; import com.eucalyptus.component.annotation.AdminService; import com.eucalyptus.component.annotation.AwsServiceName; import com.eucalyptus.component.annotation.ComponentApi; import com.eucalyptus.component.annotation.PublicComponentAccounts; import com.eucalyptus.component.annotation.ComponentDatabase; import com.eucalyptus.component.annotation.DatabaseNamingStrategy; import com.eucalyptus.component.annotation.FaultLogPrefix; import com.eucalyptus.component.annotation.GenerateKeys; import com.eucalyptus.component.annotation.InternalService; import com.eucalyptus.component.annotation.Partition; import com.eucalyptus.auth.policy.annotation.PolicyVendor; import com.eucalyptus.component.annotation.PublicService; import com.eucalyptus.component.annotation.ServiceNames; import com.eucalyptus.component.id.Eucalyptus; import com.eucalyptus.empyrean.Empyrean; import com.eucalyptus.system.Ats; import com.eucalyptus.util.Classes; import com.eucalyptus.auth.principal.FullName; import com.eucalyptus.util.HasFullName; import com.eucalyptus.util.HasName; import com.eucalyptus.ws.StackConfiguration.BasicTransport; import com.eucalyptus.ws.TransportDefinition; import com.eucalyptus.ws.WebServices; import com.eucalyptus.ws.client.ClientChannelInitializers; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; public abstract class ComponentId implements HasName<ComponentId>, HasFullName<ComponentId>, Serializable { private static final long serialVersionUID = 1L; private static Logger LOG = Logger.getLogger( ComponentId.class ); private String capitalizedName; private final Ats ats; private final Partition partitionInfo; private final GenerateKeys keyInfo; private final String vendorName; protected ComponentId( final String name ) { this( ); this.capitalizedName = ( name == null ? this.getClass( ).getSimpleName( ) : name ); } protected ComponentId( ) { this.capitalizedName = this.getClass( ).getSimpleName( ); this.ats = Ats.from( this ); this.partitionInfo = this.ats.get( Partition.class ); if ( this.ats.has( GenerateKeys.class ) ) { this.keyInfo = this.ats.get( GenerateKeys.class ); } else { this.keyInfo = null; } this.vendorName = ( !this.ats.has( PolicyVendor.class ) ? "euca" : this.ats.get( PolicyVendor.class ).value( ) ); } /** * Maps the componentId to an optional set of account Ids * @return */ public Optional<? extends Iterable<UserPrincipal>> getPublicComponentAccounts() { PublicComponentAccounts accnts = this.getClass().getAnnotation(PublicComponentAccounts.class); if (accnts != null) { return Optional.of(Iterables.transform(Lists.newArrayList(accnts.value()), new Function<String, UserPrincipal>() { @Nullable @Override public UserPrincipal apply(@Nullable String s) { try { return Accounts.lookupSystemAccountByAlias(s); } catch (Exception e) { return null; } } })); } return Optional.absent(); } public List<? extends TransportDefinition> getTransports( ) { return Lists.newArrayList( BasicTransport.HTTP ); } public boolean isUseServiceHostName( ) { return false; } public String getServicePath( final String... pathParts ) { return "/services/" + this.capitalizedName; } public String getInternalServicePath( final String... pathParts ) { return "/internal/" + this.capitalizedName; } public Map<String,String> getServiceQueryParameters() { return Collections.emptyMap(); } public final String getVendorName( ) { return this.vendorName; } public final String name( ) { return this.capitalizedName.toLowerCase(); } @Override public final String getName( ) { return this.name(); } @Override public final FullName getFullName( ) { return ComponentFullName.getInstance(ServiceConfigurations.createEphemeral(this), this.getPartition(), this.name()); } @Override public String getPartition( ) { return this.partitionParent( ).name(); } public final boolean isRootService( ) { return this.partitionParent( ).equals( this ); } public final boolean isAncestor( final Class<? extends ComponentId> compId ) { if ( this.isCloudLocal( ) && Eucalyptus.class.equals( compId ) ) { return true; } else if ( this.isAlwaysLocal( ) && Empyrean.class.equals( compId ) ) { return true; } else { for ( ComponentId deps = this; ( deps != null ) && !deps.equals( deps.partitionParent( ) ); deps = deps.partitionParent( ) ) { if ( compId.equals( deps.getClass( ) ) ) { return true; } } } return false; } final ComponentId partitionParent( ) { if ( this.partitionInfo == null ) { return this; } else if ( this.partitionInfo.value( ).length == 0 ) { return this; } else if ( Arrays.asList( this.partitionInfo.value( ) ).contains( Empyrean.class ) ) { return Empyrean.INSTANCE; } else if ( Arrays.asList( this.partitionInfo.value( ) ).contains( this.getClass( ) ) ) { return this; } else if ( Arrays.asList( this.partitionInfo.value( ) ).contains( Eucalyptus.class ) && !this.partitionInfo.manyToOne( ) ) { return Eucalyptus.INSTANCE; } else { return ComponentIds.lookup( this.partitionInfo.value( )[0] ); } } public boolean isPartitioned( ) { return this.isRegisterable( ) && !this.equals( this.partitionParent( ) ); } public Boolean isCloudLocal( ) { return Eucalyptus.INSTANCE.isRelated( ).apply( this ); } public final Boolean isAlwaysLocal( ) { return Empyrean.INSTANCE.isRelated( ).apply( this ); } public Predicate<ComponentId> isRelated( ) { return new Predicate<ComponentId>( ) { @Override public boolean apply( final ComponentId input ) { return ComponentId.this.equals( input ) || ( input.partitionInfo != null && Arrays.asList( input.partitionInfo.value( ) ).contains( ComponentId.this.getClass( ) ) ); } }; } public Boolean hasCredentials( ) { return this.ats.has( GenerateKeys.class ); } private static final ConcurrentMap<String, Class<ChannelInitializer<?>>> clientChannelInitializers = Maps.newConcurrentMap( ); public Bootstrap getClientBootstrap( ) { return WebServices.clientBootstrap( ); } public final ChannelInitializer<?> getClientChannelInitializer( ) { ChannelInitializer<?> channelInitializer; for ( final Class c : Classes.ancestors( this ) ) { if ( ( channelInitializer = ClientChannelInitializers.lookup( c ) ) != null ) { return channelInitializer; } } throw new IllegalStateException( "No channel initializer" ); } public final String getCapitalizedName( ) { return this.capitalizedName; } public final String getFaultLogPrefix( ) { if ( Ats.from( this ).has( FaultLogPrefix.class ) ) { String value = Ats.from( this ).get( FaultLogPrefix.class ).value( ); return (value != null && !"".equals(value) ) ? value : this.name( ); } else { return Ats.from( Empyrean.class ).get( FaultLogPrefix.class ).value( );//this requires that Empyrean.class is annotated as @FaultLogging("cloud") } } public Integer getPort( ) { return 8773; } public String getChannelName( ) { return String.format( "%s-request", getName( ) ); } public String getServiceModelFileName( ) { return String.format("%s-model.xml", this.getName()); } public Set<String> getCertificateUsages( ) { return Collections.emptySet( ); } public X509Certificate getCertificate( final String usage ) { return null; } @Override public final int compareTo( final ComponentId that ) { return this.name( ).compareTo( that.name( ) ); } @Override public final int hashCode( ) { final int prime = 31; int result = 1; result = prime * result + ( ( this.name( ) == null ) ? 0 : this.name( ).hashCode( ) ); return result; } @Override public final boolean equals( final Object obj ) { if ( this == obj ) return true; if ( obj == null ) return false; if ( this.getClass( ) != obj.getClass( ) ) return false; final ComponentId other = ( ComponentId ) obj; if ( this.name( ) == null ) { if ( other.name( ) != null ) return false; } else if ( !this.name( ).equals( other.name( ) ) ) return false; return true; } public boolean runLimitedServices( ) { return false; } @Override public String toString( ) { final StringBuilder builder = new StringBuilder( ); builder.append( this.getFullName( ) ).append( " " ); builder.append( this.name( ) ).append( ":" ); if ( this.isPartitioned( ) ) { builder.append( "partitioned:" ); } else { builder.append( "unpartitioned:" ); } if ( this.isCloudLocal( ) ) { builder.append( "cloudLocal:" ); } else if ( this.isAlwaysLocal( ) ) { builder.append( "alwaysLocal:" ); } return builder.toString(); } public final boolean isInternal( ) { return this.ats.has( InternalService.class ) || !this.isAdminService( ) || !this.isPublicService( ) || ( this.partitionParent( ).equals( Empyrean.INSTANCE ) && !this.isRegisterable( ) ) || ( this.partitionParent( ).equals( Eucalyptus.INSTANCE ) && !this.isRegisterable( ) ); } /** * @return true if does not require internal system privileges, false otherwise. */ public boolean isPublicService( ) { return this.ats.has( PublicService.class ); } /** * @return true if does not require internal system privileges, false otherwise. */ public boolean isAdminService( ) { return this.ats.has(AdminService.class); } /** * @return true if this component represents the api for another component */ public boolean isApi( ) { return this.ats.has( ComponentApi.class ) && this.ats.get( ComponentApi.class ).value( ).equals( getClass( ) ); } /** * @return true if this component implements the api defined by another component */ public boolean hasApi( ) { return this.ats.has( ComponentApi.class ) && !isApi( ); } /** * @return true if this component implements the api defined by another component */ public boolean hasApi( Class<? extends ComponentId> api ) { return hasApi( ) && this.ats.get( ComponentApi.class ).value( ).equals( api ); } /** * @return the api component class, which will be this components class if !hasApi() */ public Class<? extends ComponentId> toApiClass( ) { return hasApi( ) ? this.ats.get( ComponentApi.class ).value( ) : getClass( ); } /** * Does this component support impersonation. * * @return true if impersonation is supported. */ public boolean isImpersonationSupported( ) { return isPublicService( ); } /** * @return */ public boolean isRegisterable( ) { return !( ServiceBuilders.lookup( this ) instanceof DummyServiceBuilder ); } /** * Temporarily this includes only a registerability check. * * @param config * @return */ public boolean isDistributedService( ) { return this.isRegisterable( ); } /** * Can the component be run locally (i.e., is the needed code available) * * @param component TODO * @return true if the component could be run locally. */ public Boolean isAvailableLocally( ) { return this.isAlwaysLocal( ) || ( this.isCloudLocal( ) && BootstrapArgs.isCloudController( ) ) || this.checkComponentParts( ); } /** * Temporary work around allowing components to opt out of DNS. * * @deprecated */ @Deprecated public boolean isDnsSupported( ) { return true; } public String getAwsServiceName( ) { if ( this.ats.has( AwsServiceName.class ) ) { return this.ats.get( AwsServiceName.class ).value( ); } else { return "eucalyptus"; } } /** * Get any additional DNS host labels for this component. */ public Set<String> getServiceNames( ) { Set<String> names = Collections.emptySet( ); if ( this.ats.has( ServiceNames.class ) ) { names = ImmutableSet.copyOf( this.ats.get( ServiceNames.class ).value( ) ); } else if ( this.ats.has( AwsServiceName.class ) ) { names = Collections.singleton( getAwsServiceName( ) ); } return names; } /** * Get all DNS host labels for this component. */ public Set<String> getAllServiceNames( ) { final Set<String> names = Sets.newLinkedHashSet( ); names.add( name( ) ); names.addAll( getServiceNames( ) ); return names; } public DatabaseNamingStrategy getDatabaseNamingStrategy( ) { if ( this.ats.has( ComponentDatabase.class ) ) { return this.ats.get( ComponentDatabase.class ).namingStrategy( ); } else { return DatabaseNamingStrategy.Schema; } } public Boolean isManyToOnePartition( ) { return this.ats.has( Partition.class ) && this.ats.get( Partition.class ).manyToOne( ); } private boolean checkComponentParts( ) { return true;//TODO:GRZE:add checks to ensure full component state is present // try { // return ComponentMessages.lookup( this.getComponentId( ).getClass( ) ) != null; // } catch ( NoSuchElementException ex ) { // return false; // } } }