diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java
index 7887fbc..136227a 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java
@@ -1077,7 +1077,10 @@ public static boolean isAclEnabled(Configuration conf) {
public static final String YARN_LOG_SERVER_URL =
YARN_PREFIX + "log.server.url";
-
+
+ public static final String YARN_LOG_SERVER_WEBSERVICE_URL =
+ YARN_PREFIX + "log.server.web-service.url";
+
public static final String YARN_TRACKING_URL_GENERATOR =
YARN_PREFIX + "tracking.url.generator";
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml
index 1e929a8..4ca46f9 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml
@@ -2650,6 +2650,14 @@
+ URL for log aggregation server web service
+
+ yarn.log.server.web-service.url
+
+
+
+
+
RM Application Tracking URL
yarn.tracking.url.generator
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/webapp/NMWebServices.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/webapp/NMWebServices.java
index a59e010..0663823 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/webapp/NMWebServices.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/webapp/NMWebServices.java
@@ -23,6 +23,7 @@
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
@@ -50,6 +51,7 @@
import org.apache.hadoop.http.JettyUtils;
import org.apache.hadoop.yarn.api.records.ApplicationId;
import org.apache.hadoop.yarn.api.records.ContainerId;
+import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.exceptions.YarnException;
import org.apache.hadoop.yarn.factories.RecordFactory;
import org.apache.hadoop.yarn.factory.providers.RecordFactoryProvider;
@@ -75,6 +77,8 @@
import org.apache.hadoop.yarn.webapp.NotFoundException;
import org.apache.hadoop.yarn.webapp.WebApp;
import org.apache.hadoop.yarn.webapp.util.WebAppUtils;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.utils.URLEncodedUtils;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -87,6 +91,7 @@
private WebApp webapp;
private static RecordFactory recordFactory = RecordFactoryProvider
.getRecordFactory(null);
+ private final String redirectWSUrl;
private @javax.ws.rs.core.Context
HttpServletRequest request;
@@ -103,6 +108,8 @@ public NMWebServices(final Context nm, final ResourceView view,
this.nmContext = nm;
this.rview = view;
this.webapp = webapp;
+ this.redirectWSUrl = this.nmContext.getConf().get(
+ YarnConfiguration.YARN_LOG_SERVER_WEBSERVICE_URL);
}
private void init() {
@@ -270,6 +277,9 @@ public Response getContainerLogsInfo(
} catch (IOException ex) {
// Something wrong with we tries to access the remote fs for the logs.
// Skip it and do nothing
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(ex.getMessage());
+ }
}
GenericEntity> meta = new GenericEntity>(containersLogsInfo){};
@@ -280,7 +290,13 @@ public Response getContainerLogsInfo(
resp.header("X-Content-Type-Options", "nosniff");
return resp.build();
} catch (Exception ex) {
- throw new WebApplicationException(ex);
+ if (redirectWSUrl == null || redirectWSUrl.isEmpty()) {
+ throw new WebApplicationException(ex);
+ }
+ // redirect the request to the configured log server
+ String redirectURI = "/containers/" + containerIdStr
+ + "/logs";
+ return createRedirectResponse(request, redirectWSUrl, redirectURI);
}
}
@@ -377,7 +393,14 @@ public Response getLogs(
logFile = ContainerLogsUtils.getContainerLogFile(
containerId, filename, request.getRemoteUser(), nmContext);
} catch (NotFoundException ex) {
- return Response.status(Status.NOT_FOUND).entity(ex.getMessage()).build();
+ if (redirectWSUrl == null || redirectWSUrl.isEmpty()) {
+ return Response.status(Status.NOT_FOUND).entity(ex.getMessage())
+ .build();
+ }
+ // redirect the request to the configured log server
+ String redirectURI = "/containers/" + containerIdStr
+ + "/logs/" + filename;
+ return createRedirectResponse(request, redirectWSUrl, redirectURI);
} catch (YarnException ex) {
return Response.serverError().entity(ex.getMessage()).build();
}
@@ -464,4 +487,47 @@ private long parseLongParam(String bytes) {
}
return Long.parseLong(bytes);
}
+
+ private String removeSpecifiedQueryParameter(HttpServletRequest request,
+ String parameterName) {
+ String queryString = request.getQueryString();
+ if (queryString != null && !queryString.isEmpty()) {
+ String reqEncoding = request.getCharacterEncoding();
+ if (reqEncoding == null || reqEncoding.isEmpty()) {
+ reqEncoding = "ISO-8859-1";
+ }
+ Charset encoding = Charset.forName(reqEncoding);
+ List params = URLEncodedUtils.parse(queryString, encoding);
+ Iterator paramIterator = params.iterator();
+ while(paramIterator.hasNext()) {
+ NameValuePair current = paramIterator.next();
+ if (current.getName().equals(parameterName)) {
+ paramIterator.remove();
+ }
+ }
+ return URLEncodedUtils.format(params, encoding);
+ }
+ return null;
+ }
+
+ private Response createRedirectResponse(HttpServletRequest request,
+ String redirectWSUrlPrefix, String uri) {
+ // redirect the request to the configured log server
+ StringBuilder redirectPath = new StringBuilder();
+ if (redirectWSUrlPrefix.endsWith("/")) {
+ redirectWSUrlPrefix = redirectWSUrlPrefix.substring(0,
+ redirectWSUrlPrefix.length() - 1);
+ }
+ redirectPath.append(redirectWSUrlPrefix + uri);
+ // append all the request query parameters except nodeId parameter
+ String requestParams = removeSpecifiedQueryParameter(request,
+ YarnWebServiceParams.NM_ID);
+ if (requestParams != null && !requestParams.isEmpty()) {
+ redirectPath.append("?" + requestParams);
+ }
+ ResponseBuilder response = Response.status(
+ HttpServletResponse.SC_TEMPORARY_REDIRECT);
+ response.header("Location", redirectPath.toString());
+ return response.build();
+ }
}
diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/webapp/TestNMWebServices.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/webapp/TestNMWebServices.java
index 7764ceb..78b1a0b 100644
--- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/webapp/TestNMWebServices.java
+++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/test/java/org/apache/hadoop/yarn/server/nodemanager/webapp/TestNMWebServices.java
@@ -20,6 +20,7 @@
import static org.apache.hadoop.yarn.webapp.WebServicesTestUtils.assertResponseStatusCode;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -27,7 +28,11 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URL;
import java.util.List;
+import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.MediaType;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -59,6 +64,7 @@
import org.apache.hadoop.yarn.server.nodemanager.webapp.WebServer.NMWebApp;
import org.apache.hadoop.yarn.server.security.ApplicationACLsManager;
import org.apache.hadoop.yarn.server.utils.BuilderUtils;
+import org.apache.hadoop.yarn.server.webapp.YarnWebServiceParams;
import org.apache.hadoop.yarn.server.webapp.dao.ContainerLogsInfo;
import org.apache.hadoop.yarn.util.YarnVersionInfo;
import org.apache.hadoop.yarn.webapp.GenericExceptionHandler;
@@ -97,6 +103,7 @@
private static ApplicationACLsManager aclsManager;
private static LocalDirsHandlerService dirsHandler;
private static WebApp nmWebApp;
+ private static final String logServiceWSAddress = "test:1234";
private static final File testRootDir = new File("target",
TestNMWebServices.class.getSimpleName());
@@ -115,6 +122,8 @@ protected void configureServlets() {
conf.setBoolean(YarnConfiguration.LOG_AGGREGATION_ENABLED, true);
conf.set(YarnConfiguration.NM_REMOTE_APP_LOG_DIR,
testRemoteLogDir.getAbsolutePath());
+ conf.set(YarnConfiguration.YARN_LOG_SERVER_WEBSERVICE_URL,
+ logServiceWSAddress);
dirsHandler = new LocalDirsHandlerService();
NodeHealthCheckerService healthChecker = new NodeHealthCheckerService(
NodeManager.getNodeHealthScriptRunner(conf), dirsHandler);
@@ -351,6 +360,58 @@ public void testContainerLogsWithOldAPI() throws IOException, JSONException{
testContainerLogs(r, containerId);
}
+ @Test (timeout = 10000)
+ public void testNMRedirect() {
+ ApplicationId noExistAppId = ApplicationId.newInstance(
+ System.currentTimeMillis(), 2000);
+ ApplicationAttemptId noExistAttemptId = ApplicationAttemptId.newInstance(
+ noExistAppId, 150);
+ ContainerId noExistContainerId = ContainerId.newContainerId(
+ noExistAttemptId, 250);
+ String fileName = "syslog";
+ WebResource r = resource();
+
+ // check the old api
+ URI requestURI = r.path("ws").path("v1").path("node")
+ .path("containerlogs").path(noExistContainerId.toString())
+ .path(fileName).queryParam("user.name", "user")
+ .queryParam(YarnWebServiceParams.NM_ID, "localhost:1111")
+ .getURI();
+ String redirectURL = getRedirectURL(requestURI.toString());
+ assertTrue(redirectURL != null);
+ assertTrue(redirectURL.contains(logServiceWSAddress));
+ assertTrue(redirectURL.contains(noExistContainerId.toString()));
+ assertTrue(redirectURL.contains("/logs/" + fileName));
+ assertTrue(redirectURL.contains("user.name=" + "user"));
+ assertFalse(redirectURL.contains(YarnWebServiceParams.NM_ID));
+
+ // check the new api
+ requestURI = r.path("ws").path("v1").path("node")
+ .path("containers").path(noExistContainerId.toString())
+ .path("logs").path(fileName).queryParam("user.name", "user")
+ .queryParam(YarnWebServiceParams.NM_ID, "localhost:1111")
+ .getURI();
+ redirectURL = getRedirectURL(requestURI.toString());
+ assertTrue(redirectURL != null);
+ assertTrue(redirectURL.contains(logServiceWSAddress));
+ assertTrue(redirectURL.contains(noExistContainerId.toString()));
+ assertTrue(redirectURL.contains("/logs/" + fileName));
+ assertTrue(redirectURL.contains("user.name=" + "user"));
+ assertFalse(redirectURL.contains(YarnWebServiceParams.NM_ID));
+
+ requestURI = r.path("ws").path("v1").path("node")
+ .path("containers").path(noExistContainerId.toString())
+ .path("logs").queryParam("user.name", "user")
+ .queryParam(YarnWebServiceParams.NM_ID, "localhost:1111")
+ .getURI();
+ redirectURL = getRedirectURL(requestURI.toString());
+ assertTrue(redirectURL != null);
+ assertTrue(redirectURL.contains(logServiceWSAddress));
+ assertTrue(redirectURL.contains(noExistContainerId.toString()));
+ assertTrue(redirectURL.contains("user.name=" + "user"));
+ assertFalse(redirectURL.contains(YarnWebServiceParams.NM_ID));
+ }
+
private void testContainerLogs(WebResource r, ContainerId containerId)
throws IOException {
final String containerIdStr = containerId.toString();
@@ -451,13 +512,12 @@ private void testContainerLogs(WebResource r, ContainerId containerId)
+ WebAppUtils.listSupportedLogContentType(), responseText);
assertEquals(400, response.getStatus());
- // ask for file that doesn't exist
+ // ask for file that doesn't exist and it will re-direct to
+ // the log server
response = r.path("uhhh")
.accept(MediaType.TEXT_PLAIN).get(ClientResponse.class);
- assertEquals(Status.NOT_FOUND.getStatusCode(),
+ Assert.assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
response.getStatus());
- responseText = response.getEntity(String.class);
- assertTrue(responseText.contains("Cannot find this log on the local disk."));
// Get container log files' name
WebResource r1 = resource();
@@ -630,4 +690,21 @@ private String getLogContext(String fullMessage) {
int postfixIndex = fullMessage.indexOf(postfix);
return fullMessage.substring(prefixIndex, postfixIndex);
}
-}
+
+ private static String getRedirectURL(String url) {
+ String redirectUrl = null;
+ try {
+ HttpURLConnection conn = (HttpURLConnection) new URL(url)
+ .openConnection();
+ // do not automatically follow the redirection
+ // otherwise we get too many redirections exception
+ conn.setInstanceFollowRedirects(false);
+ if(conn.getResponseCode() == HttpServletResponse.SC_TEMPORARY_REDIRECT) {
+ redirectUrl = conn.getHeaderField("Location");
+ }
+ } catch (Exception e) {
+ // throw new RuntimeException(e);
+ }
+ return redirectUrl;
+ }
+}
\ No newline at end of file