diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/JAXBContextResolver.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/JAXBContextResolver.java index 5f50ed1..c577629 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/JAXBContextResolver.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/JAXBContextResolver.java @@ -34,12 +34,14 @@ 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.ApplicationStatisticsInfo; 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; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.CapacitySchedulerQueueInfoList; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ClusterInfo; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ClusterMetricsInfo; +import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.StatisticsItemInfo; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.FifoSchedulerInfo; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeInfo; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodesInfo; @@ -65,7 +67,8 @@ CapacitySchedulerInfo.class, ClusterMetricsInfo.class, SchedulerInfo.class, AppsInfo.class, NodesInfo.class, RemoteExceptionData.class, CapacitySchedulerQueueInfoList.class, - ResourceInfo.class, UsersInfo.class, UserInfo.class}; + ResourceInfo.class, UsersInfo.class, UserInfo.class, + ApplicationStatisticsInfo.class, StatisticsItemInfo.class}; public JAXBContextResolver() throws Exception { this.types = new HashSet(Arrays.asList(cTypes)); diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/RMWebServices.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/RMWebServices.java index a5c2f44..88a5eed 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/RMWebServices.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/RMWebServices.java @@ -22,7 +22,9 @@ import java.util.Arrays; import java.util.Collection; import java.util.EnumSet; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentMap; @@ -58,10 +60,12 @@ 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.ApplicationStatisticsInfo; 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.ClusterInfo; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ClusterMetricsInfo; +import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.StatisticsItemInfo; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.FairSchedulerInfo; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.FifoSchedulerInfo; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeInfo; @@ -70,6 +74,7 @@ import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.SchedulerTypeInfo; import org.apache.hadoop.yarn.server.security.ApplicationACLsManager; import org.apache.hadoop.yarn.util.ConverterUtils; +import org.apache.hadoop.yarn.util.StringHelper; import org.apache.hadoop.yarn.webapp.BadRequestException; import org.apache.hadoop.yarn.webapp.NotFoundException; @@ -303,53 +308,16 @@ public AppsInfo getApps(@Context HttpServletRequest hsr, "finishTimeEnd must be greater than finishTimeBegin"); } - Set appTypes = new HashSet(); - if (!applicationTypes.isEmpty()) { - for (String applicationType : applicationTypes) { - if (applicationType != null && !applicationType.trim().isEmpty()) { - if (applicationType.indexOf(",") == -1) { - appTypes.add(applicationType.trim()); - } else { - String[] types = applicationType.split(","); - for (String type : types) { - if (!type.trim().isEmpty()) { - appTypes.add(type.trim()); - } - } - } - } - } - } + Set appTypes = parseQueries(applicationTypes, false); if (!appTypes.isEmpty()) { checkAppTypes = true; } - String allAppStates; - RMAppState[] stateArray = RMAppState.values(); - allAppStates = Arrays.toString(stateArray); - - Set appStates = new HashSet(); // stateQuery is deprecated. if (stateQuery != null && !stateQuery.isEmpty()) { statesQuery.add(stateQuery); } - if (!statesQuery.isEmpty()) { - for (String applicationState : statesQuery) { - if (applicationState != null && !applicationState.isEmpty()) { - String[] states = applicationState.split(","); - for (String state : states) { - try { - RMAppState.valueOf(state.trim()); - } catch (IllegalArgumentException iae) { - throw new BadRequestException( - "Invalid application-state " + state - + " specified. It should be one of " + allAppStates); - } - appStates.add(state.trim().toLowerCase()); - } - } - } - } + Set appStates = parseQueries(statesQuery, true); if (!appStates.isEmpty()) { checkAppStates = true; } @@ -394,8 +362,8 @@ public AppsInfo getApps(@Context HttpServletRequest hsr, continue; } } - if (checkAppTypes - && !appTypes.contains(rmapp.getApplicationType())) { + if (checkAppTypes && !appTypes.contains( + rmapp.getApplicationType().trim().toLowerCase())) { continue; } @@ -416,6 +384,128 @@ public AppsInfo getApps(@Context HttpServletRequest hsr, } @GET + @Path("/appstatistics") + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public ApplicationStatisticsInfo getAppStatistics( + @Context HttpServletRequest hsr, + @QueryParam("states") Set stateQueries, + @QueryParam("applicationTypes") Set typeQueries) { + init(); + + // parse the params and build the scoreboard + // converting state/type name to lowercase + Set states = parseQueries(stateQueries, true); + Set types = parseQueries(typeQueries, false); + if (types.size() == 0) { + throw new BadRequestException("applicationTypes is empty or null"); + } else if (types.size() != 1) { + throw new BadRequestException("# of applicationTypes = " + types.size() + + ", we temporarily support at most one applicationType"); + } + // if no states, returns the counts of all RMAppStates + if (states.size() == 0) { + for (RMAppState state : RMAppState.values()) { + states.add(state.toString().toLowerCase()); + } + } + // in case we extend to multiple applicationTypes in the future + Map> scoreboard = buildScoreboard(states, types); + + // go through the apps in RM to count the numbers, ignoring the case of + // the state/type name + ConcurrentMap apps = rm.getRMContext().getRMApps(); + for (RMApp rmapp : apps.values()) { + String state = rmapp.getState().toString().toLowerCase(); + String type = rmapp.getApplicationType().trim().toLowerCase(); + if (states.isEmpty() && !types.isEmpty() && types.contains(type)) { + countApp(scoreboard, null, type); + } else if ( + !states.isEmpty() && states.contains(state) && types.isEmpty()) { + countApp(scoreboard, state, null); + } else if (!states.isEmpty() && states.contains(state) + && !types.isEmpty() && types.contains(type)) { + countApp(scoreboard, state, type); + } + } + + // fill the response object + ApplicationStatisticsInfo appStatInfo = new ApplicationStatisticsInfo(); + for (Map.Entry> partScoreboard + : scoreboard.entrySet()) { + for (Map.Entry statEntry + : partScoreboard.getValue().entrySet()) { + StatisticsItemInfo statItem = new StatisticsItemInfo( + partScoreboard.getKey(), statEntry.getKey(), statEntry.getValue()); + appStatInfo.add(statItem); + } + } + return appStatInfo; + } + + private static Set parseQueries( + Set queries, boolean isState) { + Set params = new HashSet(); + if (!queries.isEmpty()) { + for (String query : queries) { + if (query != null && !query.trim().isEmpty()) { + String[] paramStrs = query.split(","); + for (String paramStr : paramStrs) { + if (paramStr != null && !paramStr.trim().isEmpty()) { + if (isState) { + try { + // enum string is in the uppercase + RMAppState.valueOf(paramStr.trim().toUpperCase()); + } catch (RuntimeException e) { + RMAppState[] stateArray = RMAppState.values(); + String allAppStates = Arrays.toString(stateArray); + throw new BadRequestException( + "Invalid application-state " + paramStr.trim() + + " specified. It should be one of " + allAppStates); + } + } + params.add(paramStr.trim().toLowerCase()); + } + } + } + } + } + return params; + } + + private static Map> buildScoreboard( + Set states, Set types) { + Map> scoreboard + = new HashMap>(); + if (states.isEmpty()) { + Map partScoreboard = new HashMap(); + scoreboard.put(null, partScoreboard); + for (String type : types) { + partScoreboard.put(type, 0L); + } + } else { + for (String state : states) { + Map partScoreboard = new HashMap(); + scoreboard.put(state, partScoreboard); + if (types.isEmpty()) { + partScoreboard.put(null, 0L); + } else { + for (String type : types) { + partScoreboard.put(type, 0L); + } + } + } + } + return scoreboard; + } + + private static void countApp( + Map> scoreboard, String state, String type) { + Map partScoreboard = scoreboard.get(state); + Long count = partScoreboard.get(type); + partScoreboard.put(type, count + 1L); + } + + @GET @Path("/apps/{appid}") @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) public AppInfo getApp(@Context HttpServletRequest hsr, diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ApplicationStatisticsInfo.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ApplicationStatisticsInfo.java new file mode 100644 index 0000000..b77ffb4 --- /dev/null +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/ApplicationStatisticsInfo.java @@ -0,0 +1,44 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.yarn.server.resourcemanager.webapp.dao; + +import java.util.ArrayList; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "appStatInfo") +@XmlAccessorType(XmlAccessType.FIELD) +public class ApplicationStatisticsInfo { + + protected ArrayList statItem + = new ArrayList(); + + public ApplicationStatisticsInfo() { + } // JAXB needs this + + public void add(StatisticsItemInfo statItem) { + this.statItem.add(statItem); + } + + public ArrayList getStatItems() { + return statItem; + } + +} diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/StatisticsItemInfo.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/StatisticsItemInfo.java new file mode 100644 index 0000000..063af1c --- /dev/null +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/StatisticsItemInfo.java @@ -0,0 +1,53 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.yarn.server.resourcemanager.webapp.dao; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "statItem") +@XmlAccessorType(XmlAccessType.FIELD) +public class StatisticsItemInfo { + + protected String state; + protected String type; + protected long count; + + public StatisticsItemInfo() { + } // JAXB needs this + + public StatisticsItemInfo(String state, String type, long count) { + this.state = state; + this.type = type; + this.count = count; + } + + public String getState() { + return state; + } + + public String getType() { + return type; + } + + public long getCount() { + return count; + } + +} \ No newline at end of file diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesApps.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesApps.java index 5e71452..7217d91 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesApps.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesApps.java @@ -976,6 +976,144 @@ public void testAppsQueryAppTypes() throws JSONException, Exception { } @Test + public void testAppStatistics() throws JSONException, Exception { + try { + rm.start(); + MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 4096); + Thread.sleep(1); + RMApp app1 = rm.submitApp(1024, "", UserGroupInformation.getCurrentUser() + .getShortUserName(), null, false, null, 2, null, "MAPREDUCE"); + amNodeManager.nodeHeartbeat(true); + // finish App + MockAM am = rm + .sendAMLaunched(app1.getCurrentAppAttempt().getAppAttemptId()); + am.registerAppAttempt(); + am.unregisterAppAttempt(); + amNodeManager.nodeHeartbeat(app1.getCurrentAppAttempt().getAppAttemptId(), + 1, ContainerState.COMPLETE); + + rm.submitApp(1024, "", UserGroupInformation.getCurrentUser() + .getShortUserName(), null, false, null, 2, null, "MAPREDUCE"); + rm.submitApp(1024, "", UserGroupInformation.getCurrentUser() + .getShortUserName(), null, false, null, 2, null, "OTHER"); + + // zero type, zero state + WebResource r = resource(); + ClientResponse response = r.path("ws").path("v1").path("cluster") + .path("appstatistics") + .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); + assertEquals(Status.BAD_REQUEST, response.getClientResponseStatus()); + assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); + JSONObject json = response.getEntity(JSONObject.class); + assertEquals("incorrect number of elements", 1, json.length()); + JSONObject exception = json.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.checkStringContains("exception message", + "applicationTypes is empty or null", message); + WebServicesTestUtils.checkStringEqual("exception type", + "BadRequestException", type); + WebServicesTestUtils.checkStringEqual("exception className", + "org.apache.hadoop.yarn.webapp.BadRequestException", className); + + // one type, zero state + r = resource(); + response = r.path("ws").path("v1").path("cluster") + .path("appstatistics") + .queryParam("applicationTypes", "MAPREDUCE") + .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); + assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); + json = response.getEntity(JSONObject.class); + assertEquals("incorrect number of elements", 1, json.length()); + JSONObject appsStatInfo = json.getJSONObject("appStatInfo"); + assertEquals("incorrect number of elements", 1, appsStatInfo.length()); + JSONArray statItems = appsStatInfo.getJSONArray("statItem"); + assertEquals("incorrect number of elements", + RMAppState.values().length, statItems.length()); + for (int i = 0; i < RMAppState.values().length; ++i) { + assertEquals("mapreduce", statItems.getJSONObject(0).getString("type")); + if (statItems.getJSONObject(0).getString("state").equals("accepted")) { + assertEquals("1", statItems.getJSONObject(0).getString("count")); + } else if ( + statItems.getJSONObject(0).getString("state").equals("finished")) { + assertEquals("1", statItems.getJSONObject(0).getString("count")); + } else { + assertEquals("0", statItems.getJSONObject(0).getString("count")); + } + } + + // two types, zero state + r = resource(); + response = r.path("ws").path("v1").path("cluster") + .path("appstatistics") + .queryParam("applicationTypes", "MAPREDUCE,OTHER") + .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); + assertEquals(Status.BAD_REQUEST, response.getClientResponseStatus()); + assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); + json = response.getEntity(JSONObject.class); + assertEquals("incorrect number of elements", 1, json.length()); + exception = json.getJSONObject("RemoteException"); + assertEquals("incorrect number of elements", 3, exception.length()); + message = exception.getString("message"); + type = exception.getString("exception"); + className = exception.getString("javaClassName"); + WebServicesTestUtils.checkStringContains("exception message", + "we temporarily support at most one applicationType", message); + WebServicesTestUtils.checkStringEqual("exception type", + "BadRequestException", type); + WebServicesTestUtils.checkStringEqual("exception className", + "org.apache.hadoop.yarn.webapp.BadRequestException", className); + + // one type, two states + r = resource(); + response = r.path("ws").path("v1").path("cluster") + .path("appstatistics") + .queryParam("states", RMAppState.FINISHED.toString() + + "," + RMAppState.ACCEPTED.toString()) + .queryParam("applicationTypes", "MAPREDUCE") + .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); + assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); + json = response.getEntity(JSONObject.class); + assertEquals("incorrect number of elements", 1, json.length()); + appsStatInfo = json.getJSONObject("appStatInfo"); + assertEquals("incorrect number of elements", 1, appsStatInfo.length()); + statItems = appsStatInfo.getJSONArray("statItem"); + assertEquals("incorrect number of elements", 2, statItems.length()); + assertEquals("accepted", statItems.getJSONObject(0).getString("state")); + assertEquals("mapreduce", statItems.getJSONObject(0).getString("type")); + assertEquals("1", statItems.getJSONObject(0).getString("count")); + assertEquals("finished", statItems.getJSONObject(1).getString("state")); + assertEquals("mapreduce", statItems.getJSONObject(1).getString("type")); + assertEquals("1", statItems.getJSONObject(1).getString("count")); + + // invalid state + r = resource(); + response = r.path("ws").path("v1").path("cluster") + .path("appstatistics").queryParam("states", "wrong_state") + .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); + assertEquals(Status.BAD_REQUEST, response.getClientResponseStatus()); + assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); + json = response.getEntity(JSONObject.class); + assertEquals("incorrect number of elements", 1, json.length()); + exception = json.getJSONObject("RemoteException"); + assertEquals("incorrect number of elements", 3, exception.length()); + message = exception.getString("message"); + type = exception.getString("exception"); + className = exception.getString("javaClassName"); + WebServicesTestUtils.checkStringContains("exception message", + "Invalid application-state wrong_state", message); + WebServicesTestUtils.checkStringEqual("exception type", + "BadRequestException", type); + WebServicesTestUtils.checkStringEqual("exception className", + "org.apache.hadoop.yarn.webapp.BadRequestException", className); + } finally { + rm.stop(); + } + } + + @Test public void testSingleApp() throws JSONException, Exception { rm.start(); MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/apt/ResourceManagerRest.apt.vm hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/apt/ResourceManagerRest.apt.vm index 2201112..393a092 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/apt/ResourceManagerRest.apt.vm +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/apt/ResourceManagerRest.apt.vm @@ -1269,6 +1269,129 @@ _01_000001 +---+ +* Cluster Application Statistics API + + With the Application Statistics API, you can obtain a collection of triples, each of which contains the application type, the application state and the number of applications of this type and this state in ResourceManager context. Note that with the performance concern, we currently only support at most one applicationType per query. We may support multiple applicationTypes per query as well as more statistics in the future. When you run a GET operation on this resource, you obtain a collection of statItem objects. + +** URI + +------ + * http:///ws/v1/cluster/appstatistics +------ + +** HTTP Operations Supported + +------ + * GET +------ + +** Query Parameters Required + + Two paramters can be specified. The parameters are case insensitive. + +------ + * states - states of the applications, specified as a comma-separated list. If states is not provided, the API will enumerate all application states and return the counts of them. + * applicationTypes - types of the applications, specified as a comma-separated list. Note that applicationTypes must be provided, and we only support at most one applicationType temporarily. Otherwise, users will expect an BadRequestException. +------ + +** Elements of the (statItems) object + + When you make a request for the list of statistics items, the information will be returned as a collection of statItem objects + +*-----------+----------------------------------------------------------------------+-------------------------------------+ +|| Item || Data Type || Description | +*-----------+----------------------------------------------------------------------+-------------------------------------+ +| statItem | array of statItem objects(JSON)/zero or more statItem objects(XML) | The collection of statItem objects | +*-----------+----------------------------------------------------------------------+-------------------------------------+ + +** Response Examples + + <> + + HTTP Request: + +------ + GET http:///ws/v1/cluster/appstatistics?states=accepted,running,finished&applicationTypes=mapreduce +------ + + Response Header: + ++---+ + HTTP/1.1 200 OK + Content-Type: application/json + Transfer-Encoding: chunked + Server: Jetty(6.1.26) ++---+ + + Response Body: + ++---+ +{ + "appStatInfo": + { + "statItem": + [ + { + "state" : "accepted", + "type" : "mapreduce", + "count" : 4 + }, + { + "state" : "running", + "type" : "mapreduce", + "count" : 1 + }, + { + "state" : "finished", + "type" : "mapreduce", + "count" : 7 + } + ] + } +} ++---+ + + <> + + HTTP Request: + +------ + GET http:///ws/v1/cluster/appstatistics?states=accepted,running,finished&applicationTypes=mapreduce + Accept: application/xml +------ + + Response Header: + ++---+ + HTTP/1.1 200 OK + Content-Type: application/xml + Content-Length: 2459 + Server: Jetty(6.1.26) ++---+ + + Response Body: + ++---+ + + + + accepted + mapreduce + 4 + + + running + mapreduce + 1 + + + finished + mapreduce + 7 + + ++---+ + * Cluster {Application API} An application resource contains information about a particular application that was submitted to a cluster.