Skip to content

Commit

Permalink
Add filtering on resource properties for regions (EUCA-4676)
Browse files Browse the repository at this point in the history
Region filtering by properties is supported by the new
RegionFilterSupport class. The RegionFilterSupportTest is added to
provide coverage for all Predicate filters. The ClusterEndpoint is
updated to use filters when describing regions. Framework classes are
updated to allow for non AbstractPersistent/CloudMetadata resources.
  • Loading branch information
sjones4 committed Jan 11, 2013
1 parent 600c794 commit 9b149c9
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 22 deletions.
Expand Up @@ -64,7 +64,6 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -83,11 +82,14 @@
import com.eucalyptus.component.id.Eucalyptus;
import com.eucalyptus.component.id.Walrus;
import com.eucalyptus.context.Contexts;
import com.eucalyptus.tags.Filter;
import com.eucalyptus.tags.FilterSupport;
import com.eucalyptus.tags.Filters;
import com.eucalyptus.vm.VmType;
import com.eucalyptus.vm.VmTypes;
import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
Expand Down Expand Up @@ -284,21 +286,80 @@ public List<ClusterInfoType> apply( String serviceTag ) {

public DescribeRegionsResponseType DescribeRegions( final DescribeRegionsType request ) {//TODO:GRZE:URGENT fix the behaviour here.
final DescribeRegionsResponseType reply = ( DescribeRegionsResponseType ) request.getReply( );
final Collection<String> regions = Objects.firstNonNull(request.getRegions(), Collections.<String>emptyList());
for ( final Class<? extends ComponentId> componentIdClass : ImmutableList.of(Eucalyptus.class, Walrus.class) ) {
try {
final Component component = Components.lookup( componentIdClass );
final String region = component.getComponentId( ).name();
if ( regions.isEmpty() || regions.contains( region ) ) {
final NavigableSet<ServiceConfiguration> configs = component.services( );
if ( !configs.isEmpty( ) && Component.State.ENABLED.equals( configs.first( ).lookupState( ) ) ) {
reply.getRegionInfo( ).add( new RegionInfoType( region, ServiceUris.remotePublicify( configs.first( ) ).toASCIIString( ) ) );
}
final String region = component.getComponentId( ).name();
final List<Region> regions = Lists.newArrayList();
final NavigableSet<ServiceConfiguration> configs = component.services( );
if ( !configs.isEmpty( ) && Component.State.ENABLED.equals( configs.first( ).lookupState( ) ) ) {
regions.add( new Region( region, ServiceUris.remotePublicify( configs.first() ).toASCIIString() ) );
}

final Filter filter = Filters.generate( request.getFilterSet(), Region.class );
final Predicate<Object> requested = Predicates.and(
filterByName( request.getRegions() ),
filter.asPredicate() );
for ( final Region item : Iterables.filter( regions, requested ) ) {
reply.getRegionInfo( ).add( new RegionInfoType( item.getDisplayName(), item.getEndpointUrl() ) );
}
} catch ( NoSuchElementException ex ) {
LOG.error( ex, ex );
}
}
return reply;
}

/**
* This should be Predicate<Region> but JDK6 can't handle the resulting Predicate<? super Region>
*/
private static Predicate<Object> filterByName( final Collection<String> requestedIdentifiers ) {
return new Predicate<Object>( ) {
@Override
public boolean apply( Object region ) {
return requestedIdentifiers == null || requestedIdentifiers.isEmpty( ) || requestedIdentifiers.contains( ((Region)region).getDisplayName() );
}
};
}

protected static class Region {
private final String displayName;
private final String endpointUrl;

protected Region( final String displayName, final String endpointUrl ) {
this.displayName = displayName;
this.endpointUrl = endpointUrl;
}

public String getDisplayName() {
return displayName;
}

public String getEndpointUrl() {
return endpointUrl;
}
}

private enum RegionFunctions implements Function<Region,String> {
REGION_NAME {
@Override
public String apply( final Region region ) {
return region.getDisplayName();
}
},
ENDPOINT_URL {
@Override
public String apply( final Region region ) {
return region.getEndpointUrl();
}
}
}

public static class RegionFilterSupport extends FilterSupport<Region> {
public RegionFilterSupport() {
super( builderFor( Region.class )
.withStringProperty( "endpoint", RegionFunctions.ENDPOINT_URL )
.withStringProperty( "region-name", RegionFunctions.REGION_NAME ) );
}
}
}
Expand Up @@ -38,11 +38,9 @@
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;
import com.eucalyptus.auth.login.AuthenticationException;
import com.eucalyptus.cloud.CloudMetadata;
import com.eucalyptus.context.Context;
import com.eucalyptus.context.Contexts;
import com.eucalyptus.crypto.util.Timestamps;
import com.eucalyptus.entities.AbstractPersistent;
import com.google.common.base.CharMatcher;
import com.google.common.base.Function;
import com.google.common.base.Functions;
Expand All @@ -58,9 +56,9 @@
/**
* Filter support class overridden for each resource that supports filtering.
*/
public abstract class FilterSupport<RT extends AbstractPersistent & CloudMetadata> {
public abstract class FilterSupport<RT> {

private static final ConcurrentMap<Class<? extends CloudMetadata>,FilterSupport> supportByClass = Maps.newConcurrentMap();
private static final ConcurrentMap<Class<?>,FilterSupport> supportByClass = Maps.newConcurrentMap();

private final Class<RT> resourceClass;
private Class<? extends Tag> tagClass;
Expand Down Expand Up @@ -93,7 +91,7 @@ protected FilterSupport( @Nonnull final Builder<RT> builder ) {
* @param <RT> The resource type
* @return The builder to use
*/
protected static <RT extends AbstractPersistent & CloudMetadata> Builder<RT> builderFor( final Class<RT> resourceClass ) {
protected static <RT> Builder<RT> builderFor( final Class<RT> resourceClass ) {
return new Builder<RT>( resourceClass );
}

Expand All @@ -102,7 +100,7 @@ protected static <RT extends AbstractPersistent & CloudMetadata> Builder<RT> bui
*
* @param <RT> The resource type
*/
protected static class Builder<RT extends AbstractPersistent & CloudMetadata> {
protected static class Builder<RT> {
private final Class<RT> resourceClass;
private final Map<String,Function<? super String,Predicate<? super RT>>> predicateFunctions =
Maps.newHashMap();
Expand Down Expand Up @@ -553,7 +551,7 @@ public Filter generate( final Map<String, Set<String>> filters,
return new Filter( aliases, conjunction, Predicates.and( and ), tagPresent );
}

public static FilterSupport forResource( @Nonnull final Class<? extends CloudMetadata> metadataClass ) {
public static FilterSupport forResource( @Nonnull final Class<?> metadataClass ) {
return supportByClass.get( metadataClass );
}

Expand Down Expand Up @@ -631,7 +629,7 @@ public boolean apply( final T resource ) {

@SuppressWarnings( "unchecked" )
static void registerFilterSupport( @Nonnull final FilterSupport filterSupport ) {
supportByClass.put( (Class<? extends CloudMetadata>) filterSupport.getResourceClass(), filterSupport );
supportByClass.put( (Class<?>) filterSupport.getResourceClass(), filterSupport );
}

private Predicate<Object> typedPredicate( final Predicate<? super RT> predicate ) {
Expand Down
Expand Up @@ -22,7 +22,6 @@
import java.util.Map;
import java.util.Set;
import javax.annotation.Nonnull;
import com.eucalyptus.cloud.CloudMetadata;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
Expand All @@ -42,7 +41,7 @@ public class Filters {
*/
@Nonnull
public static Filter generate( final Iterable<edu.ucsb.eucalyptus.msgs.Filter> filters,
final Class<? extends CloudMetadata> resourceType ) {
final Class<?> resourceType ) {
final Filter filter;

final FilterSupport support = FilterSupport.forResource( resourceType );
Expand Down
@@ -0,0 +1,56 @@
/*************************************************************************
* Copyright 2009-2012 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.cluster

import org.junit.Test
import com.eucalyptus.tags.FilterSupportTest

/**
* Unit tests for snapshot filter support
*/
class RegionFilterSupportTest extends FilterSupportTest.InstanceTest<ClusterEndpoint.Region> {

@Test
void testFilteringSupport() {
assertValid( new ClusterEndpoint.RegionFilterSupport() )
}

@Test
void testPredicateFilters() {
assertMatch( true, "region-name", "eucalyptus", new ClusterEndpoint.Region( "eucalyptus", "http://eucalyptus.com" ) )
assertMatch( false, "region-name", "eucalyptus", new ClusterEndpoint.Region( "eucalyptus-west", "http://eucalyptus.com" ) )
assertMatch( false, "region-name", "eucalyptus", new ClusterEndpoint.Region( null, null ) )

assertMatch( true, "endpoint", "http://eucalyptus.com", new ClusterEndpoint.Region( "eucalyptus", "http://eucalyptus.com" ) )
assertMatch( false, "endpoint", "http://eucalyptus.com", new ClusterEndpoint.Region( "eucalyptus", "http://eucalyptus.com/foo" ) )
assertMatch( false, "endpoint", "http://eucalyptus.com", new ClusterEndpoint.Region( null, null ) )
}

@Test
void testWildcardPredicateFilter() {
assertMatch( true, "region-name", "eu*", new ClusterEndpoint.Region( "eucalyptus", "http://eucalyptus.com" ) )
assertMatch( false, "region-name", "eur*", new ClusterEndpoint.Region( "eucalyptus", "http://eucalyptus.com" ) )
}

void assertMatch( final boolean expectedMatch, final String filterKey, final String filterValue, final ClusterEndpoint.Region target ) {
super.assertMatch( new ClusterEndpoint.RegionFilterSupport(), expectedMatch, filterKey, filterValue, target )
}

}
Expand Up @@ -23,8 +23,6 @@ import static org.junit.Assert.*
import org.junit.Test
import org.springframework.util.ReflectionUtils
import java.lang.reflect.Field
import com.eucalyptus.entities.AbstractPersistent
import com.eucalyptus.cloud.CloudMetadata
import com.google.common.base.CharMatcher
import com.eucalyptus.system.Ats
import javax.persistence.OneToMany
Expand Down Expand Up @@ -80,7 +78,7 @@ class FilterSupportTest {
result.toString()
}

static abstract class InstanceTest<RT extends AbstractPersistent & CloudMetadata> {
static abstract class InstanceTest<RT> {
void assertValidKeys( final FilterSupport<RT> filterSupport ) {
CharMatcher upperMatcher = CharMatcher.JAVA_UPPER_CASE;
CharMatcher spaceMatcher = CharMatcher.WHITESPACE;
Expand Down

0 comments on commit 9b149c9

Please sign in to comment.