/************************************************************************* * Copyright 2009-2013 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.empyrean.configuration; import static com.eucalyptus.util.Parameters.checkParam; import static org.hamcrest.Matchers.notNullValue; import java.util.Collection; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Future; import com.eucalyptus.component.Topology; import com.google.common.base.MoreObjects; import com.google.common.base.Strings; import com.google.common.base.Throwables; import org.apache.log4j.Logger; import com.eucalyptus.component.ComponentId; import com.eucalyptus.component.ComponentIds; import com.eucalyptus.component.ServiceBuilder; import com.eucalyptus.component.ServiceBuilders; import com.eucalyptus.component.ServiceConfiguration; import com.eucalyptus.component.ServiceConfigurations; import com.eucalyptus.component.annotation.Description; import com.eucalyptus.component.annotation.ServiceOperation; import com.eucalyptus.component.events.ServiceEvents; import com.eucalyptus.component.groups.ServiceGroup; import com.eucalyptus.component.groups.ServiceGroupBuilder; import com.eucalyptus.component.groups.ServiceGroupConfiguration; import com.eucalyptus.component.groups.ServiceGroups; import com.eucalyptus.empyrean.ServiceId; import com.eucalyptus.system.Ats; import com.eucalyptus.util.EucalyptusCloudException; import com.eucalyptus.util.Exceptions; import com.eucalyptus.util.TypeMappers; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; /** * @author chris grzegorczyk <grze@eucalyptus.com> */ public class ServiceRegistrationManager { private static Logger LOG = Logger.getLogger( ServiceRegistrationManager.class ); private static final String TYPE_MAP_PROP = "com.eucalyptus.empyrean.registration.map."; private static ComponentId getMappedComponentId( final String type, final boolean require ) { if ( !require && Strings.isNullOrEmpty( type ) ) return null; return ComponentIds.lookup( System.getProperty( TYPE_MAP_PROP + type, type ) ); } @ServiceOperation( hostDispatch = true ) public enum RegisterService implements Function<RegisterServiceType, RegisterServiceResponseType> { INSTANCE; public static Collection<Future<ServiceConfiguration>> register( String partition, String name, String hostName, Integer port, ComponentId componentId ) throws EucalyptusCloudException { final Collection<Future<ServiceConfiguration>> registered = Lists.newArrayList( ); final ServiceBuilder<? extends ServiceConfiguration> builder = ServiceBuilders.lookup( componentId ); final ServiceConfiguration config = builder.newInstance( partition, name, hostName, port ); if ( ServiceGroups.isGroup( config ) ) { final ServiceGroupConfiguration groupConfig = ( ServiceGroupConfiguration ) config; final ServiceGroupBuilder<ServiceGroupConfiguration> groupBuilder = ( ServiceGroupBuilder<ServiceGroupConfiguration> ) builder; final Collection<ServiceConfiguration> configsToRegister = groupBuilder.onRegister( groupConfig ); //do registrations final Future<ServiceConfiguration> groupFuture = ServiceEvents.registerFunction().apply( groupConfig ); registered.add( groupFuture ); try {//require that the group op succeeds before doing the members groupFuture.get(); registered.addAll( Collections2.transform( configsToRegister, ServiceEvents.registerFunction() ) ); } catch ( Exception e ) { } } else { registered.add( ServiceEvents.registerFunction( ).apply( config ) ); } return registered; } @Override public RegisterServiceResponseType apply( final RegisterServiceType request ) { final RegisterServiceResponseType reply = request.getReply( ); final String partition = request.getPartition( ); final String name = request.getName( ); final String hostName = request.getHost( ); Integer port = null; try { /** * Check all the parameters. */ ComponentId componentId = getMappedComponentId( request.getType( ), true ); ServiceBuilders.lookup( componentId );//NOTE: this is an existence test which can fail w/ an exception. checkParam( "Name must not be null: " + request, name, notNullValue( ) ); checkParam( "Hostname must not be null: " + request, hostName, notNullValue( ) ); port = MoreObjects.firstNonNull( request.getPort( ), componentId.getPort( ) ); /** * Do the thing. */ final Collection<Future<ServiceConfiguration>> configurations = register( partition, name, hostName, port, componentId ); final Function<ServiceConfiguration, ServiceId> typeMapper = TypeMappers.lookup( ServiceConfiguration.class, ServiceId.class ); for ( Future<ServiceConfiguration> regResult : configurations ) { try { reply.getRegisteredServices( ).add( typeMapper.apply( regResult.get( ) ) ); } catch ( Exception e ) { reply.set_return( Boolean.FALSE ); reply.getStatusMessages().add( Throwables.getRootCause( e ).getMessage() ); } } } catch ( final Exception ex ) { reply.set_return( false ); reply.getStatusMessages( ).add( "Failed to register " + request.getType( ) + ": " + name + " at host: " + hostName + ( port == null ? "" : ":" + port ) + " because of : " + ex.getMessage( ) ); } return reply; } } @ServiceOperation( hostDispatch = true ) public enum DeregisterService implements Function<DeregisterServiceType, DeregisterServiceResponseType> { INSTANCE; private static Collection<Future<ServiceConfiguration>> deregister( ComponentId componentId, String name ) throws EucalyptusCloudException { final List<Future<ServiceConfiguration>> deregistered = Lists.newArrayList( ); try { final ServiceBuilder<? extends ServiceConfiguration> builder = ServiceBuilders.lookup( componentId ); final ServiceConfiguration config = ServiceConfigurations.lookupByName( componentId.getClass( ), name ); if ( ServiceGroups.isGroup( config ) ) { final ServiceGroupConfiguration groupConfig = ( ServiceGroupConfiguration ) config; final ServiceGroupBuilder<ServiceGroupConfiguration> groupBuilder = ( ServiceGroupBuilder<ServiceGroupConfiguration> ) builder; final Collection<ServiceConfiguration> configsToDeregister = groupBuilder.onDeregister( groupConfig ); //do registrations final Future<ServiceConfiguration> groupFuture = ServiceEvents.deregisterFunction().apply( groupConfig ); deregistered.add( groupFuture ); try {//require that the group op succeeds before doing the members groupFuture.get(); deregistered.addAll( Collections2.transform( configsToDeregister, ServiceEvents.deregisterFunction( ) ) ); } catch ( Exception e ) { e.printStackTrace(); } } else { deregistered.add( ServiceEvents.deregisterFunction( ).apply( config ) ); } } catch ( Exception e ) { LOG.error( e ); LOG.debug( e, e ); throw e; } return deregistered; } @Override public DeregisterServiceResponseType apply( final DeregisterServiceType request ) { final DeregisterServiceResponseType reply = request.getReply( ); final String name = request.getName( ); try { /** * Check all the parameters. */ checkParam( "Name must not be null: " + request, name, notNullValue( ) ); ComponentId requestComponentId = getMappedComponentId( request.getType( ), false ); ServiceConfiguration config = requestComponentId == null ? ServiceConfigurations.lookupByName( request.getName( ) ) : ServiceConfigurations.lookupByName( requestComponentId.getClass( ), request.getName( ) ); final ComponentId componentId = config.getComponentId( ); ServiceBuilders.lookup( componentId ); //NOTE: this is an existence test which can fail w/ an exception. Collection<Future<ServiceConfiguration>> configurations = Topology.doTopologyWork( new Callable<Collection<Future<ServiceConfiguration>>>(){ @Override public Collection<Future<ServiceConfiguration>> call( ) throws Exception { return deregister( componentId, name ); } } ); final Function<ServiceConfiguration, ServiceId> typeMapper = TypeMappers.lookup( ServiceConfiguration.class, ServiceId.class ); for ( Future<ServiceConfiguration> regResult : configurations ) { try { reply.getDeregisteredServices( ).add( typeMapper.apply( regResult.get( ) ) ); } catch ( Exception e ) { reply.set_return( Boolean.FALSE ); reply.getStatusMessages( ).add( Throwables.getRootCause( e ).getMessage() ); } } } catch ( final Exception ex ) { reply.set_return( false ); reply.getStatusMessages( ).add( "Failed to deregister " + request.getType( ) + ": " + name + " because of : " + ex.getMessage( ) ); } return reply; } } @ServiceOperation( hostDispatch = true ) public enum DescribeAvailableComponents implements Function<DescribeAvailableServiceTypesType, DescribeAvailableServiceTypesResponseType> { INSTANCE; @Override public DescribeAvailableServiceTypesResponseType apply( final DescribeAvailableServiceTypesType input ) { try { DescribeAvailableServiceTypesResponseType reply = input.getReply( ); for ( Class<? extends ComponentId> compId : ServiceBuilders.listRegisterableComponents( ) ) { final ComponentId componentId = ComponentIds.lookup( compId ); Predicate<ComponentId> filterIn = new Predicate<ComponentId>( ) { @Override public boolean apply( ComponentId input ) { return input.isRegisterable( ); } }; if ( !input.getVerbose( ) && !filterIn.apply( componentId ) ) { continue; } final AvailableComponentInfo compInfo = new AvailableComponentInfo( ); String description = componentId.getAwsServiceName( ) + " service implementation"; if ( Ats.from( componentId ).has( Description.class ) ) { description = Ats.from( componentId ).get( Description.class ).value( ); } if ( componentId.isPartitioned( ) && !componentId.isRegisterable( ) ) { description = "A sub component of " + componentId.getPartition( ) + " for: " + componentId.getCapitalizedName( ); } /** * Info about component */ compInfo.setComponentName( componentId.name( ) ); compInfo.setComponentCapitalizedName( componentId.getCapitalizedName( ) ); compInfo.setDescription( description ); compInfo.setHasCredentials( componentId.hasCredentials( ) ); //GRZE:YAWN: this above should be a component specific (read: @annotated) value and not a cobbled together generic string. /** * Info about its registration requirements */ compInfo.setRegisterable( componentId.isRegisterable( ) ); compInfo.setPartitioned( componentId.isPartitioned( ) ); compInfo.setPublicApiService( componentId.isPublicService( ) ); compInfo.setRequiresName( componentId.isPartitioned( ) );//GRZE: this condition will change going forward; they are not equivalent notions. /** * Info about service groups */ if ( componentId instanceof ServiceGroup ) { ServiceGroup sg = ( ServiceGroup ) componentId; for ( ComponentId m : sg.list( ) ) { compInfo.getServiceGroupMembers( ).add( m.name( ) ); } } for ( ComponentId m : ServiceGroups.listMembership( componentId ) ) { compInfo.getServiceGroups( ).add( m.name( ) ); } reply.getAvailable( ).add( compInfo ); } return reply; } catch ( final Exception ex ) { throw Exceptions.toUndeclared( ex ); } } } }