MAPREDUCE-3350. Per-app RM page should have the list of application-attempts like on the app JHS page (Jonathon Eagles via tgraves)
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1346047 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
1283ab51a9
commit
358eda0bfa
|
@ -559,6 +559,9 @@ Release 0.23.3 - UNRELEASED
|
|||
MAPREDUCE-4302. NM goes down if error encountered during log aggregation
|
||||
(Daryn Sharp via bobby)
|
||||
|
||||
MAPREDUCE-3350. Per-app RM page should have the list of application-attempts
|
||||
like on the app JHS page (Jonathon Eagles via tgraves)
|
||||
|
||||
Release 0.23.2 - UNRELEASED
|
||||
|
||||
INCOMPATIBLE CHANGES
|
||||
|
|
|
@ -135,10 +135,11 @@ public class HsJobBlock extends HtmlBlock {
|
|||
th(_TH, "Node").
|
||||
th(_TH, "Logs").
|
||||
_();
|
||||
boolean odd = false;
|
||||
for (AMInfo amInfo : amInfos) {
|
||||
AMAttemptInfo attempt = new AMAttemptInfo(amInfo,
|
||||
job.getId(), job.getUserName(), "", "");
|
||||
table.tr().
|
||||
table.tr((odd = !odd) ? _ODD : _EVEN).
|
||||
td(String.valueOf(attempt.getAttemptId())).
|
||||
td(new Date(attempt.getStartTime()).toString()).
|
||||
td().a(".nodelink", url("http://", attempt.getNodeHttpAddress()),
|
||||
|
|
|
@ -20,6 +20,8 @@ package org.apache.hadoop.yarn.server.resourcemanager.rmapp;
|
|||
|
||||
import java.util.Collection;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.hadoop.yarn.api.protocolrecords.FinishApplicationMasterRequest;
|
||||
import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
|
||||
import org.apache.hadoop.yarn.api.records.FinalApplicationStatus;
|
||||
|
@ -93,6 +95,13 @@ public interface RMApp extends EventHandler<RMAppEvent> {
|
|||
*/
|
||||
RMAppAttempt getCurrentAppAttempt();
|
||||
|
||||
/**
|
||||
* {@link RMApp} can have multiple application attempts {@link RMAppAttempt}.
|
||||
* This method returns the all {@link RMAppAttempt}s for the RMApp.
|
||||
* @return all {@link RMAppAttempt}s for the RMApp.
|
||||
*/
|
||||
Map<ApplicationAttemptId, RMAppAttempt> getAppAttempts();
|
||||
|
||||
/**
|
||||
* To get the status of an application in the RM, this method can be used.
|
||||
* If full access is not allowed then the following fields in the report
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.apache.hadoop.yarn.server.resourcemanager.rmapp;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
|
@ -311,6 +312,17 @@ public class RMAppImpl implements RMApp {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ApplicationAttemptId, RMAppAttempt> getAppAttempts() {
|
||||
this.readLock.lock();
|
||||
|
||||
try {
|
||||
return Collections.unmodifiableMap(this.attempts);
|
||||
} finally {
|
||||
this.readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ApplicationStore getApplicationStore() {
|
||||
return this.appStore;
|
||||
|
|
|
@ -145,9 +145,15 @@ public interface RMAppAttempt extends EventHandler<RMAppAttemptEvent> {
|
|||
*/
|
||||
ApplicationSubmissionContext getSubmissionContext();
|
||||
|
||||
/*
|
||||
/**
|
||||
* Get application container and resource usage information.
|
||||
* @return an ApplicationResourceUsageReport object.
|
||||
*/
|
||||
ApplicationResourceUsageReport getApplicationResourceUsageReport();
|
||||
|
||||
/**
|
||||
* the start time of the application.
|
||||
* @return the start time of the application.
|
||||
*/
|
||||
long getStartTime();
|
||||
}
|
||||
|
|
|
@ -119,6 +119,7 @@ public class RMAppAttemptImpl implements RMAppAttempt {
|
|||
private int rpcPort;
|
||||
private String origTrackingUrl = "N/A";
|
||||
private String proxiedTrackingUrl = "N/A";
|
||||
private long startTime = 0;
|
||||
|
||||
// Set to null initially. Will eventually get set
|
||||
// if an RMAppAttemptUnregistrationEvent occurs
|
||||
|
@ -543,6 +544,8 @@ public class RMAppAttemptImpl implements RMAppAttempt {
|
|||
public void transition(RMAppAttemptImpl appAttempt,
|
||||
RMAppAttemptEvent event) {
|
||||
|
||||
appAttempt.startTime = System.currentTimeMillis();
|
||||
|
||||
// Register with the ApplicationMasterService
|
||||
appAttempt.masterService
|
||||
.registerAppAttempt(appAttempt.applicationAttemptId);
|
||||
|
@ -912,4 +915,14 @@ public class RMAppAttemptImpl implements RMAppAttempt {
|
|||
return RMAppAttemptState.RUNNING;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartTime() {
|
||||
this.readLock.lock();
|
||||
try {
|
||||
return this.startTime;
|
||||
} finally {
|
||||
this.readLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,13 @@ package org.apache.hadoop.yarn.server.resourcemanager.webapp;
|
|||
|
||||
import static org.apache.hadoop.yarn.util.StringHelper.join;
|
||||
import static org.apache.hadoop.yarn.webapp.YarnWebParams.APPLICATION_ID;
|
||||
import static org.apache.hadoop.yarn.webapp.view.JQueryUI._EVEN;
|
||||
import static org.apache.hadoop.yarn.webapp.view.JQueryUI._INFO_WRAP;
|
||||
import static org.apache.hadoop.yarn.webapp.view.JQueryUI._ODD;
|
||||
import static org.apache.hadoop.yarn.webapp.view.JQueryUI._TH;
|
||||
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
|
@ -29,14 +36,18 @@ import org.apache.hadoop.yarn.api.records.ApplicationAccessType;
|
|||
import org.apache.hadoop.yarn.api.records.ApplicationId;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.RMContext;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMApp;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttempt;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppAttemptInfo;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppInfo;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager;
|
||||
import org.apache.hadoop.yarn.server.security.ApplicationACLsManager;
|
||||
import org.apache.hadoop.yarn.util.Apps;
|
||||
import org.apache.hadoop.yarn.util.Times;
|
||||
import org.apache.hadoop.yarn.webapp.hamlet.Hamlet;
|
||||
import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.DIV;
|
||||
import org.apache.hadoop.yarn.webapp.hamlet.Hamlet.TABLE;
|
||||
import org.apache.hadoop.yarn.webapp.view.HtmlBlock;
|
||||
import org.apache.hadoop.yarn.webapp.view.InfoBlock;
|
||||
import org.apache.hadoop.yarn.webapp.ResponseInfo;
|
||||
|
||||
public class AppBlock extends HtmlBlock {
|
||||
|
||||
|
@ -88,7 +99,7 @@ public class AppBlock extends HtmlBlock {
|
|||
|
||||
setTitle(join("Application ", aid));
|
||||
|
||||
ResponseInfo info = info("Application Overview").
|
||||
info("Application Overview").
|
||||
_("User:", app.getUser()).
|
||||
_("Name:", app.getName()).
|
||||
_("State:", app.getState()).
|
||||
|
@ -99,12 +110,40 @@ public class AppBlock extends HtmlBlock {
|
|||
_("Tracking URL:", !app.isTrackingUrlReady() ?
|
||||
"#" : app.getTrackingUrlPretty(), app.getTrackingUI()).
|
||||
_("Diagnostics:", app.getNote());
|
||||
if (app.amContainerLogsExist()) {
|
||||
info._("AM container logs:", app.getAMContainerLogs(), app.getAMContainerLogs());
|
||||
} else {
|
||||
info._("AM container logs:", "");
|
||||
|
||||
Collection<RMAppAttempt> attempts = rmApp.getAppAttempts().values();
|
||||
String amString =
|
||||
attempts.size() == 1 ? "ApplicationMaster" : "ApplicationMasters";
|
||||
|
||||
DIV<Hamlet> div = html.
|
||||
_(InfoBlock.class).
|
||||
div(_INFO_WRAP);
|
||||
// MRAppMasters Table
|
||||
TABLE<DIV<Hamlet>> table = div.table("#app");
|
||||
table.
|
||||
tr().
|
||||
th(amString).
|
||||
_().
|
||||
tr().
|
||||
th(_TH, "Attempt Number").
|
||||
th(_TH, "Start Time").
|
||||
th(_TH, "Node").
|
||||
th(_TH, "Logs").
|
||||
_();
|
||||
|
||||
boolean odd = false;
|
||||
for (RMAppAttempt attempt : attempts) {
|
||||
AppAttemptInfo attemptInfo = new AppAttemptInfo(attempt);
|
||||
table.tr((odd = !odd) ? _ODD : _EVEN).
|
||||
td(String.valueOf(attemptInfo.getAttemptId())).
|
||||
td(Times.format(attemptInfo.getStartTime())).
|
||||
td().a(".nodelink", url("http://", attemptInfo.getNodeHttpAddress()),
|
||||
attemptInfo.getNodeHttpAddress())._().
|
||||
td().a(".logslink", url(attemptInfo.getLogsLink()), "logs")._().
|
||||
_();
|
||||
}
|
||||
|
||||
html._(InfoBlock.class);
|
||||
table._();
|
||||
div._();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ import javax.ws.rs.ext.Provider;
|
|||
import javax.xml.bind.JAXBContext;
|
||||
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppInfo;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppAttemptInfo;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppAttemptsInfo;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppsInfo;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.CapacitySchedulerInfo;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.CapacitySchedulerQueueInfo;
|
||||
|
@ -53,7 +55,8 @@ public class JAXBContextResolver implements ContextResolver<JAXBContext> {
|
|||
private final Set<Class> types;
|
||||
|
||||
// you have to specify all the dao classes here
|
||||
private final Class[] cTypes = { AppInfo.class, ClusterInfo.class,
|
||||
private final Class[] cTypes = { AppInfo.class, AppAttemptInfo.class,
|
||||
AppAttemptsInfo.class, ClusterInfo.class,
|
||||
CapacitySchedulerQueueInfo.class, FifoSchedulerInfo.class,
|
||||
SchedulerTypeInfo.class, NodeInfo.class, UserMetricsInfo.class,
|
||||
CapacitySchedulerInfo.class, ClusterMetricsInfo.class,
|
||||
|
|
|
@ -45,11 +45,14 @@ import org.apache.hadoop.yarn.factory.providers.RecordFactoryProvider;
|
|||
import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMApp;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMAppState;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttempt;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.rmnode.RMNode;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ResourceScheduler;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CSQueue;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fifo.FifoScheduler;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppAttemptInfo;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppAttemptsInfo;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppInfo;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppsInfo;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.CapacitySchedulerInfo;
|
||||
|
@ -385,4 +388,31 @@ public class RMWebServices {
|
|||
return new AppInfo(app, hasAccess(app, hsr));
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/apps/{appid}/appattempts")
|
||||
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||
public AppAttemptsInfo getAppAttempts(@PathParam("appid") String appId) {
|
||||
|
||||
init();
|
||||
if (appId == null || appId.isEmpty()) {
|
||||
throw new NotFoundException("appId, " + appId + ", is empty or null");
|
||||
}
|
||||
ApplicationId id;
|
||||
id = ConverterUtils.toApplicationId(recordFactory, appId);
|
||||
if (id == null) {
|
||||
throw new NotFoundException("appId is null");
|
||||
}
|
||||
RMApp app = rm.getRMContext().getRMApps().get(id);
|
||||
if (app == null) {
|
||||
throw new NotFoundException("app with id: " + appId + " not found");
|
||||
}
|
||||
|
||||
AppAttemptsInfo appAttemptsInfo = new AppAttemptsInfo();
|
||||
for (RMAppAttempt attempt : app.getAppAttempts().values()) {
|
||||
AppAttemptInfo attemptInfo = new AppAttemptInfo(attempt);
|
||||
appAttemptsInfo.add(attemptInfo);
|
||||
}
|
||||
|
||||
return appAttemptsInfo;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.apache.hadoop.yarn.server.resourcemanager.applicationsmanager;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.yarn.MockApps;
|
||||
|
@ -189,6 +190,10 @@ public abstract class MockAsm extends MockApps {
|
|||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
@Override
|
||||
public Map<ApplicationAttemptId, RMAppAttempt> getAppAttempts() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
@Override
|
||||
public ApplicationStore getApplicationStore() {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
|
|
@ -20,6 +20,9 @@ package org.apache.hadoop.yarn.server.resourcemanager.rmapp;
|
|||
|
||||
import java.util.Collection;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
|
||||
import org.apache.hadoop.yarn.api.records.FinalApplicationStatus;
|
||||
import org.apache.hadoop.yarn.api.records.ApplicationId;
|
||||
|
@ -112,6 +115,14 @@ public class MockRMApp implements RMApp {
|
|||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<ApplicationAttemptId, RMAppAttempt> getAppAttempts() {
|
||||
Map<ApplicationAttemptId, RMAppAttempt> attempts =
|
||||
new LinkedHashMap<ApplicationAttemptId, RMAppAttempt>();
|
||||
attempts.put(attempt.getAppAttemptId(), attempt);
|
||||
return attempts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RMAppAttempt getCurrentAppAttempt() {
|
||||
return attempt;
|
||||
|
|
|
@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue;
|
|||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
|
@ -31,13 +32,18 @@ import javax.xml.parsers.DocumentBuilderFactory;
|
|||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
import org.apache.hadoop.yarn.api.records.FinalApplicationStatus;
|
||||
import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.MockAM;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.MockNM;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.MockRM;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.RMContext;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMApp;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMAppEvent;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMAppEventType;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMAppFailedAttemptEvent;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMAppState;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttempt;
|
||||
import org.apache.hadoop.yarn.server.security.ApplicationACLsManager;
|
||||
import org.apache.hadoop.yarn.webapp.GenericExceptionHandler;
|
||||
import org.apache.hadoop.yarn.webapp.WebServicesTestUtils;
|
||||
|
@ -73,7 +79,9 @@ public class TestRMWebServicesApps extends JerseyTest {
|
|||
bind(JAXBContextResolver.class);
|
||||
bind(RMWebServices.class);
|
||||
bind(GenericExceptionHandler.class);
|
||||
rm = new MockRM(new Configuration());
|
||||
Configuration conf = new Configuration();
|
||||
conf.setInt(YarnConfiguration.RM_AM_MAX_RETRIES, 2);
|
||||
rm = new MockRM(conf);
|
||||
bind(ResourceManager.class).toInstance(rm);
|
||||
bind(RMContext.class).toInstance(rm.getRMContext());
|
||||
bind(ApplicationACLsManager.class).toInstance(
|
||||
|
@ -835,4 +843,234 @@ public class TestRMWebServicesApps extends JerseyTest {
|
|||
amContainerLogs.endsWith("/" + app.getUser()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAppAttempts() throws JSONException, Exception {
|
||||
rm.start();
|
||||
MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
|
||||
RMApp app1 = rm.submitApp(1024, "testwordcount", "user1");
|
||||
amNodeManager.nodeHeartbeat(true);
|
||||
testAppAttemptsHelper(app1.getApplicationId().toString(), app1,
|
||||
MediaType.APPLICATION_JSON);
|
||||
rm.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleAppAttempts() throws JSONException, Exception {
|
||||
rm.start();
|
||||
MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
|
||||
RMApp app1 = rm.submitApp(1024, "testwordcount", "user1");
|
||||
amNodeManager.nodeHeartbeat(true);
|
||||
int maxRetries = rm.getConfig().getInt(YarnConfiguration.RM_AM_MAX_RETRIES,
|
||||
YarnConfiguration.DEFAULT_RM_AM_MAX_RETRIES);
|
||||
int retriesLeft = maxRetries;
|
||||
while (--retriesLeft > 0) {
|
||||
RMAppEvent event =
|
||||
new RMAppFailedAttemptEvent(app1.getApplicationId(),
|
||||
RMAppEventType.ATTEMPT_FAILED, "");
|
||||
app1.handle(event);
|
||||
rm.waitForState(app1.getApplicationId(), RMAppState.ACCEPTED);
|
||||
amNodeManager.nodeHeartbeat(true);
|
||||
}
|
||||
assertEquals("incorrect number of attempts", maxRetries,
|
||||
app1.getAppAttempts().values().size());
|
||||
testAppAttemptsHelper(app1.getApplicationId().toString(), app1,
|
||||
MediaType.APPLICATION_JSON);
|
||||
rm.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAppAttemptsSlash() throws JSONException, Exception {
|
||||
rm.start();
|
||||
MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
|
||||
RMApp app1 = rm.submitApp(1024);
|
||||
amNodeManager.nodeHeartbeat(true);
|
||||
testAppAttemptsHelper(app1.getApplicationId().toString() + "/", app1,
|
||||
MediaType.APPLICATION_JSON);
|
||||
rm.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAppAttemtpsDefault() throws JSONException, Exception {
|
||||
rm.start();
|
||||
MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
|
||||
RMApp app1 = rm.submitApp(1024);
|
||||
amNodeManager.nodeHeartbeat(true);
|
||||
testAppAttemptsHelper(app1.getApplicationId().toString() + "/", app1, "");
|
||||
rm.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidAppAttempts() throws JSONException, Exception {
|
||||
rm.start();
|
||||
MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
|
||||
rm.submitApp(1024);
|
||||
amNodeManager.nodeHeartbeat(true);
|
||||
WebResource r = resource();
|
||||
|
||||
try {
|
||||
r.path("ws").path("v1").path("cluster").path("apps")
|
||||
.path("application_invalid_12").accept(MediaType.APPLICATION_JSON)
|
||||
.get(JSONObject.class);
|
||||
fail("should have thrown exception on invalid appid");
|
||||
} catch (UniformInterfaceException ue) {
|
||||
ClientResponse response = ue.getResponse();
|
||||
|
||||
assertEquals(Status.BAD_REQUEST, response.getClientResponseStatus());
|
||||
assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
|
||||
JSONObject msg = response.getEntity(JSONObject.class);
|
||||
JSONObject exception = msg.getJSONObject("RemoteException");
|
||||
assertEquals("incorrect number of elements", 3, exception.length());
|
||||
String message = exception.getString("message");
|
||||
String type = exception.getString("exception");
|
||||
String classname = exception.getString("javaClassName");
|
||||
WebServicesTestUtils.checkStringMatch("exception message",
|
||||
"For input string: \"invalid\"", message);
|
||||
WebServicesTestUtils.checkStringMatch("exception type",
|
||||
"NumberFormatException", type);
|
||||
WebServicesTestUtils.checkStringMatch("exception classname",
|
||||
"java.lang.NumberFormatException", classname);
|
||||
|
||||
} finally {
|
||||
rm.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonexistAppAttempts() throws JSONException, Exception {
|
||||
rm.start();
|
||||
MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
|
||||
rm.submitApp(1024, "testwordcount", "user1");
|
||||
amNodeManager.nodeHeartbeat(true);
|
||||
WebResource r = resource();
|
||||
|
||||
try {
|
||||
r.path("ws").path("v1").path("cluster").path("apps")
|
||||
.path("application_00000_0099").accept(MediaType.APPLICATION_JSON)
|
||||
.get(JSONObject.class);
|
||||
fail("should have thrown exception on invalid appid");
|
||||
} catch (UniformInterfaceException ue) {
|
||||
ClientResponse response = ue.getResponse();
|
||||
|
||||
assertEquals(Status.NOT_FOUND, response.getClientResponseStatus());
|
||||
assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
|
||||
|
||||
JSONObject msg = response.getEntity(JSONObject.class);
|
||||
JSONObject exception = msg.getJSONObject("RemoteException");
|
||||
assertEquals("incorrect number of elements", 3, exception.length());
|
||||
String message = exception.getString("message");
|
||||
String type = exception.getString("exception");
|
||||
String classname = exception.getString("javaClassName");
|
||||
WebServicesTestUtils.checkStringMatch("exception message",
|
||||
"java.lang.Exception: app with id: application_00000_0099 not found",
|
||||
message);
|
||||
WebServicesTestUtils.checkStringMatch("exception type",
|
||||
"NotFoundException", type);
|
||||
WebServicesTestUtils.checkStringMatch("exception classname",
|
||||
"org.apache.hadoop.yarn.webapp.NotFoundException", classname);
|
||||
} finally {
|
||||
rm.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public void testAppAttemptsHelper(String path, RMApp app, String media)
|
||||
throws JSONException, Exception {
|
||||
WebResource r = resource();
|
||||
ClientResponse response = r.path("ws").path("v1").path("cluster")
|
||||
.path("apps").path(path).path("appattempts").accept(media)
|
||||
.get(ClientResponse.class);
|
||||
assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType());
|
||||
JSONObject json = response.getEntity(JSONObject.class);
|
||||
assertEquals("incorrect number of elements", 1, json.length());
|
||||
JSONObject jsonAppAttempts = json.getJSONObject("appAttempts");
|
||||
assertEquals("incorrect number of elements", 1, jsonAppAttempts.length());
|
||||
JSONArray jsonArray = jsonAppAttempts.getJSONArray("appAttempt");
|
||||
|
||||
Collection<RMAppAttempt> attempts = app.getAppAttempts().values();
|
||||
assertEquals("incorrect number of elements", attempts.size(),
|
||||
jsonArray.length());
|
||||
|
||||
// Verify these parallel arrays are the same
|
||||
int i = 0;
|
||||
for (RMAppAttempt attempt : attempts) {
|
||||
verifyAppAttemptsInfo(jsonArray.getJSONObject(i), attempt);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAppAttemptsXML() throws JSONException, Exception {
|
||||
rm.start();
|
||||
MockNM amNodeManager = rm.registerNode("amNM:1234", 2048);
|
||||
RMApp app1 = rm.submitApp(1024, "testwordcount", "user1");
|
||||
amNodeManager.nodeHeartbeat(true);
|
||||
WebResource r = resource();
|
||||
ClientResponse response = r.path("ws").path("v1").path("cluster")
|
||||
.path("apps").path(app1.getApplicationId().toString())
|
||||
.path("appattempts").accept(MediaType.APPLICATION_XML)
|
||||
.get(ClientResponse.class);
|
||||
assertEquals(MediaType.APPLICATION_XML_TYPE, response.getType());
|
||||
String xml = response.getEntity(String.class);
|
||||
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
InputSource is = new InputSource();
|
||||
is.setCharacterStream(new StringReader(xml));
|
||||
Document dom = db.parse(is);
|
||||
NodeList nodes = dom.getElementsByTagName("appAttempts");
|
||||
assertEquals("incorrect number of elements", 1, nodes.getLength());
|
||||
NodeList attempt = dom.getElementsByTagName("appAttempt");
|
||||
assertEquals("incorrect number of elements", 1, attempt.getLength());
|
||||
verifyAppAttemptsXML(attempt, app1.getCurrentAppAttempt());
|
||||
rm.stop();
|
||||
}
|
||||
|
||||
public void verifyAppAttemptsXML(NodeList nodes, RMAppAttempt appAttempt)
|
||||
throws JSONException, Exception {
|
||||
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
Element element = (Element) nodes.item(i);
|
||||
|
||||
verifyAppAttemptInfoGeneric(appAttempt,
|
||||
WebServicesTestUtils.getXmlInt(element, "id"),
|
||||
WebServicesTestUtils.getXmlLong(element, "startTime"),
|
||||
WebServicesTestUtils.getXmlString(element, "containerId"),
|
||||
WebServicesTestUtils.getXmlString(element, "nodeHttpAddress"),
|
||||
WebServicesTestUtils.getXmlString(element, "nodeId"),
|
||||
WebServicesTestUtils.getXmlString(element, "logsLink"));
|
||||
}
|
||||
}
|
||||
|
||||
public void verifyAppAttemptsInfo(JSONObject info, RMAppAttempt appAttempt)
|
||||
throws JSONException, Exception {
|
||||
|
||||
assertEquals("incorrect number of elements", 6, info.length());
|
||||
|
||||
verifyAppAttemptInfoGeneric(appAttempt, info.getInt("id"),
|
||||
info.getLong("startTime"), info.getString("containerId"),
|
||||
info.getString("nodeHttpAddress"), info.getString("nodeId"),
|
||||
info.getString("logsLink"));
|
||||
}
|
||||
|
||||
public void verifyAppAttemptInfoGeneric(RMAppAttempt appAttempt, int id,
|
||||
long startTime, String containerId, String nodeHttpAddress, String nodeId,
|
||||
String logsLink)
|
||||
throws JSONException, Exception {
|
||||
|
||||
assertEquals("id doesn't match", appAttempt.getAppAttemptId()
|
||||
.getAttemptId(), id);
|
||||
assertEquals("startedTime doesn't match", appAttempt.getStartTime(),
|
||||
startTime);
|
||||
WebServicesTestUtils.checkStringMatch("containerId", appAttempt
|
||||
.getMasterContainer().getId().toString(), containerId);
|
||||
WebServicesTestUtils.checkStringMatch("nodeHttpAddress", appAttempt
|
||||
.getMasterContainer().getNodeHttpAddress(), nodeHttpAddress);
|
||||
WebServicesTestUtils.checkStringMatch("nodeId", appAttempt
|
||||
.getMasterContainer().getNodeId().toString(), nodeId);
|
||||
assertTrue("logsLink doesn't match",
|
||||
logsLink.startsWith("http://"));
|
||||
assertTrue("logsLink doesn't contain user info",
|
||||
logsLink.endsWith("/" + appAttempt.getSubmissionContext().getUser()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1236,6 +1236,130 @@ _01_000001</amContainerLogs>
|
|||
</app>
|
||||
+---+
|
||||
|
||||
* Cluster Application Attempts API
|
||||
|
||||
With the application attempts API, you can obtain a collection of resources that represent an application attempt. When you run a GET operation on this resource, you obtain a collection of App Attempt Objects.
|
||||
|
||||
** URI
|
||||
|
||||
------
|
||||
* http://<rm http address:port>/ws/v1/cluster/apps/{appid}/appattempts
|
||||
------
|
||||
|
||||
** HTTP Operations Supported
|
||||
|
||||
------
|
||||
* GET
|
||||
------
|
||||
|
||||
** Query Parameters Supported
|
||||
|
||||
------
|
||||
None
|
||||
------
|
||||
|
||||
** Elements of the <appAttempts> object
|
||||
|
||||
When you make a request for the list of app attempts, the information will be returned as an array of app attempt objects.
|
||||
|
||||
appAttempts:
|
||||
|
||||
*---------------+--------------+-------------------------------+
|
||||
|| Item || Data Type || Description |
|
||||
*---------------+--------------+-------------------------------+
|
||||
| appAttempt | array of app attempt objects(JSON)/zero or more app attempt objects(XML) | The collection of app attempt objects |
|
||||
*---------------+--------------+--------------------------------+
|
||||
|
||||
** Elements of the <appAttempt> object
|
||||
|
||||
*---------------+--------------+-------------------------------+
|
||||
|| Item || Data Type || Description |
|
||||
*---------------+--------------+-------------------------------+
|
||||
| id | string | The app attempt id |
|
||||
*---------------+--------------+--------------------------------+
|
||||
| nodeId | string | The node id of the node the attempt ran on|
|
||||
*---------------+--------------+--------------------------------+
|
||||
| nodeHttpAddress | string | The node http address of the node the attempt ran on|
|
||||
*---------------+--------------+--------------------------------+
|
||||
| logsLink | string | The http link to the app attempt logs |
|
||||
*---------------+--------------+--------------------------------+
|
||||
| containerId | string | The id of the container for the app attempt |
|
||||
*---------------+--------------+--------------------------------+
|
||||
| startTime | long | The start time of the attempt (in ms since epoch)|
|
||||
*---------------+--------------+--------------------------------+
|
||||
|
||||
** Response Examples
|
||||
|
||||
<<JSON response>>
|
||||
|
||||
HTTP Request:
|
||||
|
||||
------
|
||||
GET http://<rm http address:port>/ws/v1/cluster/apps/application_1326821518301_0005/appattempts
|
||||
------
|
||||
|
||||
Response Header:
|
||||
|
||||
+---+
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Transfer-Encoding: chunked
|
||||
Server: Jetty(6.1.26)
|
||||
+---+
|
||||
|
||||
Response Body:
|
||||
|
||||
+---+
|
||||
{
|
||||
"appAttempts" : {
|
||||
"appAttempt" : [
|
||||
{
|
||||
"nodeId" : "host.domain.com:8041",
|
||||
"nodeHttpAddress" : "host.domain.com:8042",
|
||||
"startTime" : 1326381444693,
|
||||
"id" : 1,
|
||||
"logsLink" : "http://host.domain.com:8042/node/containerlogs/container_1326821518301_0005_01_000001/user1",
|
||||
"containerId" : "container_1326821518301_0005_01_000001"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+---+
|
||||
|
||||
<<XML response>>
|
||||
|
||||
HTTP Request:
|
||||
|
||||
------
|
||||
GET http://<rm http address:port>/ws/v1/cluster/apps/application_1326821518301_0005/appattempts
|
||||
Accept: application/xml
|
||||
------
|
||||
|
||||
Response Header:
|
||||
|
||||
+---+
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/xml
|
||||
Content-Length: 575
|
||||
Server: Jetty(6.1.26)
|
||||
+---+
|
||||
|
||||
Response Body:
|
||||
|
||||
+---+
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<appAttempts>
|
||||
<appttempt>
|
||||
<nodeHttpAddress>host.domain.com:8042</nodeHttpAddress>
|
||||
<nodeId>host.domain.com:8041</nodeId>
|
||||
<id>1</id>
|
||||
<startTime>1326381444693</startTime>
|
||||
<containerId>container_1326821518301_0005_01_000001</containerId>
|
||||
<logsLink>http://host.domain.com:8042/node/containerlogs/container_1326821518301_0005_01_000001/user1</logsLink>
|
||||
</appAttempt>
|
||||
</appAttempts>
|
||||
+---+
|
||||
|
||||
* Cluster Nodes API
|
||||
|
||||
With the Nodes API, you can obtain a collection of resources, each of which represents a node. When you run a GET operation on this resource, you obtain a collection of Node Objects.
|
||||
|
|
Loading…
Reference in New Issue