/*************************************************************************
* 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.
*
* 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.compute.metadata;
import java.net.InetSocketAddress;
import java.util.Set;
import org.apache.log4j.Logger;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelEvent;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelUpstreamHandler;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import com.eucalyptus.bootstrap.Bootstrap;
import com.eucalyptus.component.annotation.ComponentPart;
import com.eucalyptus.component.id.Eucalyptus;
import com.eucalyptus.context.Contexts;
import com.eucalyptus.context.ServiceContext;
import com.eucalyptus.http.MappingHttpRequest;
import com.eucalyptus.records.Logs;
import com.eucalyptus.system.Threads;
import com.eucalyptus.ws.server.FilteredPipeline;
import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSet;
@ComponentPart( Eucalyptus.class )
public class MetadataPipeline extends FilteredPipeline implements ChannelUpstreamHandler {
private static final String ERROR_STRING = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" +
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" +
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" +
"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" +
" <head>\n" +
" <title>404 - Not Found</title>\n" +
" </head>\n" +
" <body>\n" +
" <h1>404 - Not Found</h1>\n" +
" </body>\n" +
"</html>\n";
/**
* Metadata versions are not necessarily API versions
*/
private static final Set<String> VERSION = ImmutableSet.of( "1.0", "2007-01-19", "2007-03-01", "2007-08-29",
"2007-10-10", "2007-12-15", "2008-02-01", "2008-09-01", "2009-04-04",
"2011-01-01", "2011-05-01", "2012-01-12", "2014-02-25", "latest" );
private static final String INSTANCE_ID_HEADER = "Euca-Instance-Id";
private static Logger LOG = Logger.getLogger( MetadataPipeline.class );
public MetadataPipeline( ) {
super( );
}
@Override
public boolean checkAccepts( HttpRequest message ) {
return
message.getUri( ).matches( "/latest(/.*)*|/\\d\\d\\d\\d-\\d\\d-\\d\\d/.*|/1.0/.*" ) ||
("/".equals( message.getUri( ) ) && "169.254.169.254".equals( message.getHeader( HttpHeaders.Names.HOST ) ) );
}
@Override
public String getName( ) {
return "metadata-pipeline";
}
@Override
public void handleUpstream( ChannelHandlerContext ctx, ChannelEvent e ) throws Exception {
if ( e instanceof MessageEvent && ( ( MessageEvent ) e ).getMessage( ) instanceof MappingHttpRequest ) {
final MappingHttpRequest request = ( MappingHttpRequest ) ( ( MessageEvent ) e ).getMessage( );
final String uri = request.getUri( );
final InetSocketAddress remoteAddr = ( ( InetSocketAddress ) ctx.getChannel( ).getRemoteAddress( ) );
String remoteHostOrInstanceId = remoteAddr.getAddress( ).getHostAddress( );
if ( "127.0.0.1".equals( remoteHostOrInstanceId ) &&
request.containsHeader( INSTANCE_ID_HEADER ) &&
request.getHeader( INSTANCE_ID_HEADER ).matches( "i-[0-9a-fA-F]{8}(?:[0-9a-fA-F]{9})?" ) ) {
remoteHostOrInstanceId = request.getHeader( INSTANCE_ID_HEADER );
}
final String newUri;
if ( uri.startsWith( "/latest/" ) ) {
newUri = uri.replaceAll( "/latest[/]+", remoteHostOrInstanceId + ":" );
} else if ( uri.startsWith( "/1.0/" ) ) {
newUri = uri.replaceAll( "/1.0[/]+", remoteHostOrInstanceId + ":" );
} else {
newUri = uri.replaceAll( "/\\d\\d\\d\\d-\\d\\d-\\d\\d[/]+", remoteHostOrInstanceId + ":" );
}
LOG.trace( "Trying to get metadata: " + newUri );
Object reply = "".getBytes( );
Exception replyEx = null;
if ( uri.equals( "/" ) || uri.isEmpty( ) ){
reply = Joiner.on('\n').join( VERSION).getBytes( Charsets.UTF_8 );
} else {
try {
if ( Bootstrap.isShuttingDown( ) ) {
reply = "System shutting down".getBytes( );
} else if ( !Bootstrap.isFinished( ) ) {
reply = "System is still starting up".getBytes( );
} else {
reply = ServiceContext.send( "eucalyptus-vmmetadata-request", newUri ).get( );
}
} catch ( Exception e1 ) {
Logs.extreme( ).debug( e1, e1 );
replyEx = e1;
} finally {
Contexts.clear( request.getCorrelationId( ) );
}
}
if ( Logs.extreme( ).isDebugEnabled( ) ) {
Logs.extreme( ).debug( "VmMetadata reply info: " + reply + " " + replyEx );
}
HttpResponse response;
if ( replyEx == null && reply instanceof byte[] ) {
response = new DefaultHttpResponse( request.getProtocolVersion( ), HttpResponseStatus.OK );
response.setHeader( HttpHeaders.Names.CONTENT_TYPE, "text/plain" );
ChannelBuffer buffer = ChannelBuffers.wrappedBuffer( ( byte[] ) reply );
response.setContent( buffer );
response.addHeader( HttpHeaders.Names.CONTENT_LENGTH, Integer.toString( buffer.readableBytes( ) ) );
} else {
response = new DefaultHttpResponse( request.getProtocolVersion( ), HttpResponseStatus.NOT_FOUND );
response.setHeader( HttpHeaders.Names.CONTENT_TYPE, "text/plain" );
ChannelBuffer buffer = ChannelBuffers.wrappedBuffer( ERROR_STRING.getBytes( ) );
response.setContent( buffer );
response.addHeader( HttpHeaders.Names.CONTENT_LENGTH, Integer.toString( buffer.readableBytes( ) ) );
}
response.setHeader( HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE );
ctx.getChannel( ).write( response ).addListener( ChannelFutureListener.CLOSE );
} else {
ctx.sendUpstream( e );
}
}
@Override
public ChannelPipeline addHandlers( ChannelPipeline pipeline ) {
pipeline.addLast( "instance-metadata", this );
return pipeline;
}
}