MAPREDUCE-3792. Fix "bin/mapred job -list" to display all jobs instead of only the jobs owned by the user. Contributed by Jason Lowe.
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1296721 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
26cd2356e0
commit
fab57a144d
|
@ -241,6 +241,9 @@ Release 0.23.2 - UNRELEASED
|
|||
MAPREDUCE-3896. Add user information to the delegation token issued by the
|
||||
history server. (Vinod Kumar Vavilapalli via sseth)
|
||||
|
||||
MAPREDUCE-3792. Fix "bin/mapred job -list" to display all jobs instead of
|
||||
only the jobs owned by the user. (Jason Lowe via vinodkv)
|
||||
|
||||
Release 0.23.1 - 2012-02-17
|
||||
|
||||
INCOMPATIBLE CHANGES
|
||||
|
|
|
@ -590,7 +590,9 @@ public class CLI extends Configured implements Tool {
|
|||
@Private
|
||||
public static String headerPattern = "%23s\t%10s\t%14s\t%12s\t%12s\t%10s\t%15s\t%15s\t%8s\t%8s\t%10s\t%10s\n";
|
||||
@Private
|
||||
public static String dataPattern = "%23s\t%10s\t%14d\t%12s\t%12s\t%10s\t%14d\t%14d\t%7dM\t%7sM\t%9dM\t%10s\n";
|
||||
public static String dataPattern = "%23s\t%10s\t%14d\t%12s\t%12s\t%10s\t%15s\t%15s\t%8s\t%8s\t%10s\t%10s\n";
|
||||
private static String memPattern = "%dM";
|
||||
private static String UNAVAILABLE = "N/A";
|
||||
|
||||
@Private
|
||||
public void displayJobList(JobStatus[] jobs, PrintWriter writer) {
|
||||
|
@ -599,15 +601,20 @@ public class CLI extends Configured implements Tool {
|
|||
"Queue", "Priority", "UsedContainers",
|
||||
"RsvdContainers", "UsedMem", "RsvdMem", "NeededMem", "AM info");
|
||||
for (JobStatus job : jobs) {
|
||||
int numUsedSlots = job.getNumUsedSlots();
|
||||
int numReservedSlots = job.getNumReservedSlots();
|
||||
int usedMem = job.getUsedMem();
|
||||
int rsvdMem = job.getReservedMem();
|
||||
int neededMem = job.getNeededMem();
|
||||
writer.printf(dataPattern,
|
||||
job.getJobID().toString(), job.getState(), job.getStartTime(),
|
||||
job.getUsername(), job.getQueue(),
|
||||
job.getPriority().name(),
|
||||
job.getNumUsedSlots(),
|
||||
job.getNumReservedSlots(),
|
||||
job.getUsedMem(),
|
||||
job.getReservedMem(),
|
||||
job.getNeededMem(),
|
||||
numUsedSlots < 0 ? UNAVAILABLE : numUsedSlots,
|
||||
numReservedSlots < 0 ? UNAVAILABLE : numReservedSlots,
|
||||
usedMem < 0 ? UNAVAILABLE : String.format(memPattern, usedMem),
|
||||
rsvdMem < 0 ? UNAVAILABLE : String.format(memPattern, rsvdMem),
|
||||
neededMem < 0 ? UNAVAILABLE : String.format(memPattern, neededMem),
|
||||
job.getSchedulingInfo());
|
||||
}
|
||||
writer.flush();
|
||||
|
|
|
@ -79,6 +79,7 @@ import org.apache.hadoop.yarn.util.BuilderUtils;
|
|||
|
||||
public class ClientServiceDelegate {
|
||||
private static final Log LOG = LogFactory.getLog(ClientServiceDelegate.class);
|
||||
private static final String UNAVAILABLE = "N/A";
|
||||
|
||||
// Caches for per-user NotRunningJobs
|
||||
private HashMap<JobState, HashMap<String, NotRunningJob>> notRunningJobs;
|
||||
|
@ -160,6 +161,13 @@ public class ClientServiceDelegate {
|
|||
LOG.debug("Application state is " + application.getYarnApplicationState());
|
||||
application = rm.getApplicationReport(appId);
|
||||
continue;
|
||||
} else if (UNAVAILABLE.equals(application.getHost())) {
|
||||
if (!amAclDisabledStatusLogged) {
|
||||
LOG.info("Job " + jobId + " is running, but the host is unknown."
|
||||
+ " Verify user has VIEW_JOB access.");
|
||||
amAclDisabledStatusLogged = true;
|
||||
}
|
||||
return getNotRunningJob(application, JobState.RUNNING);
|
||||
}
|
||||
if(!conf.getBoolean(MRJobConfig.JOB_AM_ACCESS_DISABLED, false)) {
|
||||
UserGroupInformation newUgi = UserGroupInformation.createRemoteUser(
|
||||
|
@ -369,9 +377,12 @@ public class ClientServiceDelegate {
|
|||
report.setJobFile(jobFile);
|
||||
}
|
||||
String historyTrackingUrl = report.getTrackingUrl();
|
||||
return TypeConverter.fromYarn(report, "http://"
|
||||
+ (StringUtils.isNotEmpty(historyTrackingUrl) ? historyTrackingUrl
|
||||
: trackingUrl));
|
||||
String url = StringUtils.isNotEmpty(historyTrackingUrl)
|
||||
? historyTrackingUrl : trackingUrl;
|
||||
if (!UNAVAILABLE.equals(url)) {
|
||||
url = "http://" + url;
|
||||
}
|
||||
return TypeConverter.fromYarn(report, url);
|
||||
}
|
||||
|
||||
public org.apache.hadoop.mapreduce.TaskReport[] getTaskReports(JobID oldJobID, TaskType taskType)
|
||||
|
|
|
@ -143,6 +143,18 @@ public interface ClientRMProtocol {
|
|||
* {@link GetApplicationReportResponse} which includes the
|
||||
* {@link ApplicationReport} for the application.</p>
|
||||
*
|
||||
* <p>If the user does not have <code>VIEW_APP</code> access then the
|
||||
* following fields in the report will be set to stubbed values:
|
||||
* <ul>
|
||||
* <li>host - set to "N/A"</li>
|
||||
* <li>RPC port - set to -1</li>
|
||||
* <li>client token - set to "N/A"</li>
|
||||
* <li>diagnostics - set to "N/A"</li>
|
||||
* <li>tracking URL - set to "N/A"</li>
|
||||
* <li>original tracking URL - set to "N/A"</li>
|
||||
* <li>resource usage report - all values are -1</li>
|
||||
* </ul></p>
|
||||
*
|
||||
* @param request request for an application report
|
||||
* @return application report
|
||||
* @throws YarnRemoteException
|
||||
|
@ -176,6 +188,11 @@ public interface ClientRMProtocol {
|
|||
* {@link GetAllApplicationsResponse} which includes the
|
||||
* {@link ApplicationReport} for all the applications.</p>
|
||||
*
|
||||
* <p>If the user does not have <code>VIEW_APP</code> access for an
|
||||
* application then the corresponding report will be filtered as
|
||||
* described in {@link #getApplicationReport(GetApplicationReportRequest)}.
|
||||
* </p>
|
||||
*
|
||||
* @param request request for report on all running applications
|
||||
* @return report on all running applications
|
||||
* @throws YarnRemoteException
|
||||
|
|
|
@ -334,6 +334,19 @@ public class BuilderUtils {
|
|||
return report;
|
||||
}
|
||||
|
||||
public static ApplicationResourceUsageReport newApplicationResourceUsageReport(
|
||||
int numUsedContainers, int numReservedContainers, Resource usedResources,
|
||||
Resource reservedResources, Resource neededResources) {
|
||||
ApplicationResourceUsageReport report =
|
||||
recordFactory.newRecordInstance(ApplicationResourceUsageReport.class);
|
||||
report.setNumUsedContainers(numUsedContainers);
|
||||
report.setNumReservedContainers(numReservedContainers);
|
||||
report.setUsedResources(usedResources);
|
||||
report.setReservedResources(reservedResources);
|
||||
report.setNeededResources(neededResources);
|
||||
return report;
|
||||
}
|
||||
|
||||
public static Resource newResource(int memory) {
|
||||
Resource resource = recordFactory.newRecordInstance(Resource.class);
|
||||
resource.setMemory(memory);
|
||||
|
|
|
@ -218,14 +218,10 @@ public class ClientRMService extends AbstractService implements
|
|||
+ "absent application " + applicationId);
|
||||
}
|
||||
|
||||
if (!checkAccess(callerUGI, application.getUser(),
|
||||
ApplicationAccessType.VIEW_APP, applicationId)) {
|
||||
throw RPCUtil.getRemoteException(new AccessControlException("User "
|
||||
+ callerUGI.getShortUserName() + " cannot perform operation "
|
||||
+ ApplicationAccessType.VIEW_APP.name() + " on " + applicationId));
|
||||
}
|
||||
|
||||
ApplicationReport report = application.createAndGetApplicationReport();
|
||||
boolean allowAccess = checkAccess(callerUGI, application.getUser(),
|
||||
ApplicationAccessType.VIEW_APP, applicationId);
|
||||
ApplicationReport report =
|
||||
application.createAndGetApplicationReport(allowAccess);
|
||||
|
||||
GetApplicationReportResponse response = recordFactory
|
||||
.newRecordInstance(GetApplicationReportResponse.class);
|
||||
|
@ -349,14 +345,9 @@ public class ClientRMService extends AbstractService implements
|
|||
|
||||
List<ApplicationReport> reports = new ArrayList<ApplicationReport>();
|
||||
for (RMApp application : this.rmContext.getRMApps().values()) {
|
||||
// Only give out the applications viewable by the user as
|
||||
// ApplicationReport has confidential information like client-token, ACLs
|
||||
// etc. Web UI displays all applications though as we filter and print
|
||||
// only public information there.
|
||||
if (checkAccess(callerUGI, application.getUser(),
|
||||
ApplicationAccessType.VIEW_APP, application.getApplicationId())) {
|
||||
reports.add(application.createAndGetApplicationReport());
|
||||
}
|
||||
boolean allowAccess = checkAccess(callerUGI, application.getUser(),
|
||||
ApplicationAccessType.VIEW_APP, application.getApplicationId());
|
||||
reports.add(application.createAndGetApplicationReport(allowAccess));
|
||||
}
|
||||
|
||||
GetAllApplicationsResponse response =
|
||||
|
@ -395,7 +386,7 @@ public class ClientRMService extends AbstractService implements
|
|||
appReports = new ArrayList<ApplicationReport>(
|
||||
apps.size());
|
||||
for (RMApp app : apps) {
|
||||
appReports.add(app.createAndGetApplicationReport());
|
||||
appReports.add(app.createAndGetApplicationReport(true));
|
||||
}
|
||||
}
|
||||
queueInfo.setApplications(appReports);
|
||||
|
|
|
@ -92,12 +92,25 @@ public interface RMApp extends EventHandler<RMAppEvent> {
|
|||
|
||||
/**
|
||||
* 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
|
||||
* will be stubbed:
|
||||
* <ul>
|
||||
* <li>host - set to "N/A"</li>
|
||||
* <li>RPC port - set to -1</li>
|
||||
* <li>client token - set to "N/A"</li>
|
||||
* <li>diagnostics - set to "N/A"</li>
|
||||
* <li>tracking URL - set to "N/A"</li>
|
||||
* <li>original tracking URL - set to "N/A"</li>
|
||||
* <li>resource usage report - all values are -1</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param allowAccess whether to allow full access to the report
|
||||
* @return the {@link ApplicationReport} detailing the status of the application.
|
||||
*/
|
||||
ApplicationReport createAndGetApplicationReport();
|
||||
ApplicationReport createAndGetApplicationReport(boolean allowAccess);
|
||||
|
||||
/**
|
||||
* Application level metadata is stored in {@link ApplicationStore} whicn
|
||||
* Application level metadata is stored in {@link ApplicationStore} which
|
||||
* can persist the information.
|
||||
* @return the {@link ApplicationStore} for this {@link RMApp}.
|
||||
*/
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.apache.hadoop.yarn.server.resourcemanager.RMAppManagerEvent;
|
|||
import org.apache.hadoop.yarn.server.resourcemanager.RMAppManagerEventType;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.RMContext;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.recovery.ApplicationsStore.ApplicationStore;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.resource.Resources;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttempt;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttemptEvent;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.rmapp.attempt.RMAppAttemptEventType;
|
||||
|
@ -64,6 +65,7 @@ import org.apache.hadoop.yarn.util.Records;
|
|||
public class RMAppImpl implements RMApp {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(RMAppImpl.class);
|
||||
private static final String UNAVAILABLE = "N/A";
|
||||
|
||||
// Immutable fields
|
||||
private final ApplicationId applicationId;
|
||||
|
@ -162,6 +164,12 @@ public class RMAppImpl implements RMApp {
|
|||
private final StateMachine<RMAppState, RMAppEventType, RMAppEvent>
|
||||
stateMachine;
|
||||
|
||||
private static final ApplicationResourceUsageReport
|
||||
DUMMY_APPLICATION_RESOURCE_USAGE_REPORT =
|
||||
BuilderUtils.newApplicationResourceUsageReport(-1, -1,
|
||||
Resources.createResource(-1), Resources.createResource(-1),
|
||||
Resources.createResource(-1));
|
||||
|
||||
public RMAppImpl(ApplicationId applicationId, RMContext rmContext,
|
||||
Configuration config, String name, String user, String queue,
|
||||
ApplicationSubmissionContext submissionContext, String clientTokenStr,
|
||||
|
@ -324,17 +332,19 @@ public class RMAppImpl implements RMApp {
|
|||
|
||||
|
||||
@Override
|
||||
public ApplicationReport createAndGetApplicationReport() {
|
||||
public ApplicationReport createAndGetApplicationReport(boolean allowAccess) {
|
||||
this.readLock.lock();
|
||||
|
||||
try {
|
||||
String clientToken = "N/A";
|
||||
String trackingUrl = "N/A";
|
||||
String host = "N/A";
|
||||
String origTrackingUrl = "N/A";
|
||||
String clientToken = UNAVAILABLE;
|
||||
String trackingUrl = UNAVAILABLE;
|
||||
String host = UNAVAILABLE;
|
||||
String origTrackingUrl = UNAVAILABLE;
|
||||
int rpcPort = -1;
|
||||
ApplicationResourceUsageReport appUsageReport = null;
|
||||
FinalApplicationStatus finishState = getFinalApplicationStatus();
|
||||
String diags = UNAVAILABLE;
|
||||
if (allowAccess) {
|
||||
if (this.currentAttempt != null) {
|
||||
trackingUrl = this.currentAttempt.getTrackingUrl();
|
||||
origTrackingUrl = this.currentAttempt.getOriginalTrackingUrl();
|
||||
|
@ -343,10 +353,14 @@ public class RMAppImpl implements RMApp {
|
|||
rpcPort = this.currentAttempt.getRpcPort();
|
||||
appUsageReport = currentAttempt.getApplicationResourceUsageReport();
|
||||
}
|
||||
diags = this.diagnostics.toString();
|
||||
} else {
|
||||
appUsageReport = DUMMY_APPLICATION_RESOURCE_USAGE_REPORT;
|
||||
}
|
||||
return BuilderUtils.newApplicationReport(this.applicationId, this.user,
|
||||
this.queue, this.name, host, rpcPort, clientToken,
|
||||
createApplicationState(this.stateMachine.getCurrentState()),
|
||||
this.diagnostics.toString(), trackingUrl,
|
||||
diags, trackingUrl,
|
||||
this.startTime, this.finishTime, finishState, appUsageReport,
|
||||
origTrackingUrl);
|
||||
} finally {
|
||||
|
|
|
@ -518,17 +518,11 @@ public class RMAppAttemptImpl implements RMAppAttempt {
|
|||
}
|
||||
}
|
||||
|
||||
ApplicationResourceUsageReport appResources =
|
||||
recordFactory.newRecordInstance(ApplicationResourceUsageReport.class);
|
||||
appResources.setNumUsedContainers(numUsedContainers);
|
||||
appResources.setNumReservedContainers(numReservedContainers);
|
||||
appResources.setUsedResources(
|
||||
Resources.createResource(currentConsumption));
|
||||
appResources.setReservedResources(
|
||||
Resources.createResource(reservedResources));
|
||||
appResources.setNeededResources(
|
||||
return BuilderUtils.newApplicationResourceUsageReport(
|
||||
numUsedContainers, numReservedContainers,
|
||||
Resources.createResource(currentConsumption),
|
||||
Resources.createResource(reservedResources),
|
||||
Resources.createResource(currentConsumption + reservedResources));
|
||||
return appResources;
|
||||
} finally {
|
||||
this.readLock.unlock();
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
|||
import java.net.InetSocketAddress;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
@ -40,6 +41,8 @@ import org.apache.hadoop.yarn.api.protocolrecords.KillApplicationRequest;
|
|||
import org.apache.hadoop.yarn.api.protocolrecords.SubmitApplicationRequest;
|
||||
import org.apache.hadoop.yarn.api.records.ApplicationId;
|
||||
import org.apache.hadoop.yarn.api.records.ApplicationAccessType;
|
||||
import org.apache.hadoop.yarn.api.records.ApplicationReport;
|
||||
import org.apache.hadoop.yarn.api.records.ApplicationResourceUsageReport;
|
||||
import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
|
||||
import org.apache.hadoop.yarn.api.records.ContainerLaunchContext;
|
||||
import org.apache.hadoop.yarn.api.records.Resource;
|
||||
|
@ -65,6 +68,7 @@ public class TestApplicationACLs {
|
|||
private static final String SUPER_USER = "superUser";
|
||||
private static final String FRIENDLY_GROUP = "friendly-group";
|
||||
private static final String SUPER_GROUP = "superGroup";
|
||||
private static final String UNAVAILABLE = "N/A";
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(TestApplicationACLs.class);
|
||||
|
||||
|
@ -298,20 +302,20 @@ public class TestApplicationACLs {
|
|||
ClientRMProtocol enemyRmClient = getRMClientForUser(ENEMY);
|
||||
|
||||
// View as the enemy
|
||||
try {
|
||||
enemyRmClient.getApplicationReport(appReportRequest);
|
||||
Assert.fail("App view by the enemy should fail!!");
|
||||
} catch (YarnRemoteException e) {
|
||||
LOG.info("Got exception while viewing app as the enemy", e);
|
||||
Assert.assertEquals("User enemy cannot perform operation VIEW_APP on "
|
||||
+ applicationId, e.getMessage());
|
||||
}
|
||||
ApplicationReport appReport = enemyRmClient.getApplicationReport(
|
||||
appReportRequest).getApplicationReport();
|
||||
verifyEnemyAppReport(appReport);
|
||||
|
||||
// List apps as enemy
|
||||
Assert.assertEquals("App view by enemy should not list any apps!!", 0,
|
||||
enemyRmClient.getAllApplications(
|
||||
recordFactory.newRecordInstance(GetAllApplicationsRequest.class))
|
||||
.getApplicationList().size());
|
||||
List<ApplicationReport> appReports = enemyRmClient
|
||||
.getAllApplications(recordFactory
|
||||
.newRecordInstance(GetAllApplicationsRequest.class))
|
||||
.getApplicationList();
|
||||
Assert.assertEquals("App view by enemy should list the apps!!", 4,
|
||||
appReports.size());
|
||||
for (ApplicationReport report : appReports) {
|
||||
verifyEnemyAppReport(report);
|
||||
}
|
||||
|
||||
// Kill app as the enemy
|
||||
try {
|
||||
|
@ -319,11 +323,37 @@ public class TestApplicationACLs {
|
|||
Assert.fail("App killing by the enemy should fail!!");
|
||||
} catch (YarnRemoteException e) {
|
||||
LOG.info("Got exception while killing app as the enemy", e);
|
||||
Assert.assertEquals(
|
||||
"User enemy cannot perform operation MODIFY_APP on "
|
||||
Assert.assertEquals("User enemy cannot perform operation MODIFY_APP on "
|
||||
+ applicationId, e.getMessage());
|
||||
}
|
||||
|
||||
rmClient.forceKillApplication(finishAppRequest);
|
||||
}
|
||||
|
||||
private void verifyEnemyAppReport(ApplicationReport appReport) {
|
||||
Assert.assertEquals("Enemy should not see app host!",
|
||||
UNAVAILABLE, appReport.getHost());
|
||||
Assert.assertEquals("Enemy should not see app rpc port!",
|
||||
-1, appReport.getRpcPort());
|
||||
Assert.assertEquals("Enemy should not see app client token!",
|
||||
UNAVAILABLE, appReport.getClientToken());
|
||||
Assert.assertEquals("Enemy should not see app diagnostics!",
|
||||
UNAVAILABLE, appReport.getDiagnostics());
|
||||
Assert.assertEquals("Enemy should not see app tracking url!",
|
||||
UNAVAILABLE, appReport.getTrackingUrl());
|
||||
Assert.assertEquals("Enemy should not see app original tracking url!",
|
||||
UNAVAILABLE, appReport.getOriginalTrackingUrl());
|
||||
ApplicationResourceUsageReport usageReport =
|
||||
appReport.getApplicationResourceUsageReport();
|
||||
Assert.assertEquals("Enemy should not see app used containers",
|
||||
-1, usageReport.getNumUsedContainers());
|
||||
Assert.assertEquals("Enemy should not see app reserved containers",
|
||||
-1, usageReport.getNumReservedContainers());
|
||||
Assert.assertEquals("Enemy should not see app used resources",
|
||||
-1, usageReport.getUsedResources().getMemory());
|
||||
Assert.assertEquals("Enemy should not see app reserved resources",
|
||||
-1, usageReport.getReservedResources().getMemory());
|
||||
Assert.assertEquals("Enemy should not see app needed resources",
|
||||
-1, usageReport.getNeededResources().getMemory());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -207,7 +207,7 @@ public abstract class MockAsm extends MockApps {
|
|||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
@Override
|
||||
public ApplicationReport createAndGetApplicationReport() {
|
||||
public ApplicationReport createAndGetApplicationReport(boolean allowAccess) {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
@Override
|
||||
|
|
|
@ -119,7 +119,7 @@ public class MockRMApp implements RMApp {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ApplicationReport createAndGetApplicationReport() {
|
||||
public ApplicationReport createAndGetApplicationReport(boolean allowAccess) {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue