YARN-3517. RM web ui for dumping scheduler logs should be for admins only (Varun Vasudev via tgraves)

This commit is contained in:
tgraves 2015-04-29 21:25:42 +00:00
parent 3dd6395bb2
commit 2e215484bd
5 changed files with 139 additions and 16 deletions

View File

@ -268,6 +268,9 @@ Release 2.8.0 - UNRELEASED
YARN-2740. Fix NodeLabelsManager to properly handle node label modifications
when distributed node label configuration enabled. (Naganarasimha G R via wangda)
YARN-3517. RM web ui for dumping scheduler logs should be for admins only
(Varun Vasudev via tgraves)
Release 2.7.1 - UNRELEASED
INCOMPATIBLE CHANGES

View File

@ -138,4 +138,15 @@ public class ApplicationACLsManager {
}
return false;
}
/**
* Check if the given user in an admin.
*
* @param calledUGI
* UserGroupInformation for the user
* @return true if the user is an admin, false otherwise
*/
public final boolean isAdmin(final UserGroupInformation calledUGI) {
return this.adminAclsManager.isAdmin(calledUGI);
}
}

View File

@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.SchedulerHealth;
@ -33,6 +34,7 @@ import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.UserInfo
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.CapacitySchedulerInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.CapacitySchedulerLeafQueueInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.CapacitySchedulerQueueInfo;
import org.apache.hadoop.yarn.server.security.ApplicationACLsManager;
import org.apache.hadoop.yarn.util.Times;
import org.apache.hadoop.yarn.webapp.ResponseInfo;
import org.apache.hadoop.yarn.webapp.SubView;
@ -190,28 +192,46 @@ class CapacitySchedulerPage extends RmView {
static class QueuesBlock extends HtmlBlock {
final CapacityScheduler cs;
final CSQInfo csqinfo;
private final ResourceManager rm;
@Inject QueuesBlock(ResourceManager rm, CSQInfo info) {
cs = (CapacityScheduler) rm.getResourceScheduler();
csqinfo = info;
this.rm = rm;
}
@Override
public void render(Block html) {
html._(MetricsOverviewTable.class);
// Dump CapacityScheduler debug logs
html.div()
UserGroupInformation callerUGI = this.getCallerUGI();
boolean isAdmin = false;
ApplicationACLsManager aclsManager = rm.getApplicationACLsManager();
if (aclsManager.areACLsEnabled()) {
if (callerUGI != null && aclsManager.isAdmin(callerUGI)) {
isAdmin = true;
}
} else {
isAdmin = true;
}
// only show button to dump CapacityScheduler debug logs to admins
if (isAdmin) {
html.div()
.button()
.$onclick("confirmAction()").b("Dump scheduler logs")._()
.select().$id("time")
.option().$value("60")._("1 min")._()
.option().$value("300")._("5 min")._()
.option().$value("600")._("10 min")._()
.$style(
"border-style: solid; border-color: #000000; border-width: 1px;"
+ " cursor: hand; cursor: pointer; border-radius: 4px")
.$onclick("confirmAction()").b("Dump scheduler logs")._().select()
.$id("time").option().$value("60")._("1 min")._().option()
.$value("300")._("5 min")._().option().$value("600")._("10 min")._()
._()._();
StringBuilder script = new StringBuilder();
script.append("function confirmAction() {")
.append(" b = confirm(\"Are you sure you wish to generate scheduler logs?\");")
StringBuilder script = new StringBuilder();
script
.append("function confirmAction() {")
.append(" b = confirm(\"Are you sure you wish to generate"
+ " scheduler logs?\");")
.append(" if (b == true) {")
.append(" var timePeriod = $(\"#time\").val();")
.append(" $.ajax({")
@ -225,13 +245,14 @@ class CapacitySchedulerPage extends RmView {
.append(" alert(\"Scheduler log is being generated.\");")
.append(" }, 1000);")
.append(" }).fail(function(data){")
.append(" alert(\"Scheduler log generation failed. Please check the ResourceManager log for more informtion.\");")
.append(" console.log(data);")
.append(" });")
.append(" }")
.append(
" alert(\"Scheduler log generation failed. Please check the"
+ " ResourceManager log for more informtion.\");")
.append(" console.log(data);").append(" });").append(" }")
.append("}");
html.script().$type("text/javascript")._(script.toString())._();
html.script().$type("text/javascript")._(script.toString())._();
}
UL<DIV<DIV<Hamlet>>> ul = html.
div("#cs-wrapper.ui-widget").

View File

@ -142,10 +142,12 @@ import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ResourceInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.SchedulerInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.SchedulerTypeInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.StatisticsItemInfo;
import org.apache.hadoop.yarn.server.security.ApplicationACLsManager;
import org.apache.hadoop.yarn.server.utils.BuilderUtils;
import org.apache.hadoop.yarn.util.AdHocLogDumper;
import org.apache.hadoop.yarn.util.ConverterUtils;
import org.apache.hadoop.yarn.webapp.BadRequestException;
import org.apache.hadoop.yarn.webapp.ForbiddenException;
import org.apache.hadoop.yarn.webapp.NotFoundException;
import org.apache.hadoop.yarn.webapp.util.WebAppUtils;
@ -263,8 +265,17 @@ public class RMWebServices {
@POST
@Path("/scheduler/logs")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public String dumpSchedulerLogs(@FormParam("time") String time) throws IOException {
public String dumpSchedulerLogs(@FormParam("time") String time,
@Context HttpServletRequest hsr) throws IOException {
init();
UserGroupInformation callerUGI = getCallerUserGroupInformation(hsr, true);
ApplicationACLsManager aclsManager = rm.getApplicationACLsManager();
if (aclsManager.areACLsEnabled()) {
if (callerUGI == null || !aclsManager.isAdmin(callerUGI)) {
String msg = "Only admins can carry out this operation.";
throw new ForbiddenException(msg);
}
}
ResourceScheduler rs = rm.getResourceScheduler();
int period = Integer.parseInt(time);
if (period <= 0) {

View File

@ -26,7 +26,9 @@ import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.File;
import java.io.StringReader;
import java.security.Principal;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
@ -37,6 +39,7 @@ import javax.ws.rs.core.MediaType;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.io.FileUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.service.Service.STATE;
import org.apache.hadoop.util.VersionInfo;
@ -54,9 +57,13 @@ import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager;
import org.apache.hadoop.yarn.server.resourcemanager.nodelabels.RMNodeLabelsManager;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.QueueMetrics;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ResourceScheduler;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler;
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fifo.FifoScheduler;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppsInfo;
import org.apache.hadoop.yarn.server.security.ApplicationACLsManager;
import org.apache.hadoop.yarn.util.YarnVersionInfo;
import org.apache.hadoop.yarn.webapp.ForbiddenException;
import org.apache.hadoop.yarn.webapp.GenericExceptionHandler;
import org.apache.hadoop.yarn.webapp.JerseyTestBase;
import org.apache.hadoop.yarn.webapp.WebServicesTestUtils;
@ -643,4 +650,74 @@ public class TestRMWebServices extends JerseyTestBase {
null, null, null, null, null, null, null, emptySet, emptySet);
assertTrue(appsInfo.getApps().isEmpty());
}
@Test
public void testDumpingSchedulerLogs() throws Exception {
ResourceManager mockRM = mock(ResourceManager.class);
Configuration conf = new YarnConfiguration();
HttpServletRequest mockHsr = mock(HttpServletRequest.class);
ApplicationACLsManager aclsManager = new ApplicationACLsManager(conf);
when(mockRM.getApplicationACLsManager()).thenReturn(aclsManager);
RMWebServices webSvc =
new RMWebServices(mockRM, conf, mock(HttpServletResponse.class));
// nothing should happen
webSvc.dumpSchedulerLogs("1", mockHsr);
Thread.sleep(1000);
checkSchedulerLogFileAndCleanup();
conf.setBoolean(YarnConfiguration.YARN_ACL_ENABLE, true);
conf.setStrings(YarnConfiguration.YARN_ADMIN_ACL, "admin");
aclsManager = new ApplicationACLsManager(conf);
when(mockRM.getApplicationACLsManager()).thenReturn(aclsManager);
webSvc = new RMWebServices(mockRM, conf, mock(HttpServletResponse.class));
boolean exceptionThrown = false;
try {
webSvc.dumpSchedulerLogs("1", mockHsr);
fail("Dumping logs should fail");
} catch (ForbiddenException ae) {
exceptionThrown = true;
}
assertTrue("ForbiddenException expected", exceptionThrown);
exceptionThrown = false;
when(mockHsr.getUserPrincipal()).thenReturn(new Principal() {
@Override
public String getName() {
return "testuser";
}
});
try {
webSvc.dumpSchedulerLogs("1", mockHsr);
fail("Dumping logs should fail");
} catch (ForbiddenException ae) {
exceptionThrown = true;
}
assertTrue("ForbiddenException expected", exceptionThrown);
when(mockHsr.getUserPrincipal()).thenReturn(new Principal() {
@Override
public String getName() {
return "admin";
}
});
webSvc.dumpSchedulerLogs("1", mockHsr);
Thread.sleep(1000);
checkSchedulerLogFileAndCleanup();
}
private void checkSchedulerLogFileAndCleanup() {
String targetFile;
ResourceScheduler scheduler = rm.getResourceScheduler();
if (scheduler instanceof FairScheduler) {
targetFile = "yarn-fair-scheduler-debug.log";
} else if (scheduler instanceof CapacityScheduler) {
targetFile = "yarn-capacity-scheduler-debug.log";
} else {
targetFile = "yarn-scheduler-debug.log";
}
File logFile = new File(System.getProperty("yarn.log.dir"), targetFile);
assertTrue("scheduler log file doesn't exist", logFile.exists());
FileUtils.deleteQuietly(logFile);
}
}