YARN-3517. RM web ui for dumping scheduler logs should be for admins only (Varun Vasudev via tgraves)
(cherry picked from commit 2e215484bd
)
This commit is contained in:
parent
460127e6f2
commit
2e13183f60
|
@ -220,6 +220,9 @@ Release 2.8.0 - UNRELEASED
|
||||||
YARN-2740. Fix NodeLabelsManager to properly handle node label modifications
|
YARN-2740. Fix NodeLabelsManager to properly handle node label modifications
|
||||||
when distributed node label configuration enabled. (Naganarasimha G R via wangda)
|
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
|
Release 2.7.1 - UNRELEASED
|
||||||
|
|
||||||
INCOMPATIBLE CHANGES
|
INCOMPATIBLE CHANGES
|
||||||
|
|
|
@ -138,4 +138,15 @@ public class ApplicationACLsManager {
|
||||||
}
|
}
|
||||||
return false;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.hadoop.security.UserGroupInformation;
|
||||||
import org.apache.hadoop.util.StringUtils;
|
import org.apache.hadoop.util.StringUtils;
|
||||||
import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager;
|
import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager;
|
||||||
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.SchedulerHealth;
|
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.CapacitySchedulerInfo;
|
||||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.CapacitySchedulerLeafQueueInfo;
|
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.resourcemanager.webapp.dao.CapacitySchedulerQueueInfo;
|
||||||
|
import org.apache.hadoop.yarn.server.security.ApplicationACLsManager;
|
||||||
import org.apache.hadoop.yarn.util.Times;
|
import org.apache.hadoop.yarn.util.Times;
|
||||||
import org.apache.hadoop.yarn.webapp.ResponseInfo;
|
import org.apache.hadoop.yarn.webapp.ResponseInfo;
|
||||||
import org.apache.hadoop.yarn.webapp.SubView;
|
import org.apache.hadoop.yarn.webapp.SubView;
|
||||||
|
@ -190,28 +192,46 @@ class CapacitySchedulerPage extends RmView {
|
||||||
static class QueuesBlock extends HtmlBlock {
|
static class QueuesBlock extends HtmlBlock {
|
||||||
final CapacityScheduler cs;
|
final CapacityScheduler cs;
|
||||||
final CSQInfo csqinfo;
|
final CSQInfo csqinfo;
|
||||||
|
private final ResourceManager rm;
|
||||||
|
|
||||||
@Inject QueuesBlock(ResourceManager rm, CSQInfo info) {
|
@Inject QueuesBlock(ResourceManager rm, CSQInfo info) {
|
||||||
cs = (CapacityScheduler) rm.getResourceScheduler();
|
cs = (CapacityScheduler) rm.getResourceScheduler();
|
||||||
csqinfo = info;
|
csqinfo = info;
|
||||||
|
this.rm = rm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(Block html) {
|
public void render(Block html) {
|
||||||
html._(MetricsOverviewTable.class);
|
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()
|
.button()
|
||||||
.$onclick("confirmAction()").b("Dump scheduler logs")._()
|
.$style(
|
||||||
.select().$id("time")
|
"border-style: solid; border-color: #000000; border-width: 1px;"
|
||||||
.option().$value("60")._("1 min")._()
|
+ " cursor: hand; cursor: pointer; border-radius: 4px")
|
||||||
.option().$value("300")._("5 min")._()
|
.$onclick("confirmAction()").b("Dump scheduler logs")._().select()
|
||||||
.option().$value("600")._("10 min")._()
|
.$id("time").option().$value("60")._("1 min")._().option()
|
||||||
|
.$value("300")._("5 min")._().option().$value("600")._("10 min")._()
|
||||||
._()._();
|
._()._();
|
||||||
|
|
||||||
StringBuilder script = new StringBuilder();
|
StringBuilder script = new StringBuilder();
|
||||||
script.append("function confirmAction() {")
|
script
|
||||||
.append(" b = confirm(\"Are you sure you wish to generate scheduler logs?\");")
|
.append("function confirmAction() {")
|
||||||
|
.append(" b = confirm(\"Are you sure you wish to generate"
|
||||||
|
+ " scheduler logs?\");")
|
||||||
.append(" if (b == true) {")
|
.append(" if (b == true) {")
|
||||||
.append(" var timePeriod = $(\"#time\").val();")
|
.append(" var timePeriod = $(\"#time\").val();")
|
||||||
.append(" $.ajax({")
|
.append(" $.ajax({")
|
||||||
|
@ -225,13 +245,14 @@ class CapacitySchedulerPage extends RmView {
|
||||||
.append(" alert(\"Scheduler log is being generated.\");")
|
.append(" alert(\"Scheduler log is being generated.\");")
|
||||||
.append(" }, 1000);")
|
.append(" }, 1000);")
|
||||||
.append(" }).fail(function(data){")
|
.append(" }).fail(function(data){")
|
||||||
.append(" alert(\"Scheduler log generation failed. Please check the ResourceManager log for more informtion.\");")
|
.append(
|
||||||
.append(" console.log(data);")
|
" alert(\"Scheduler log generation failed. Please check the"
|
||||||
.append(" });")
|
+ " ResourceManager log for more informtion.\");")
|
||||||
.append(" }")
|
.append(" console.log(data);").append(" });").append(" }")
|
||||||
.append("}");
|
.append("}");
|
||||||
|
|
||||||
html.script().$type("text/javascript")._(script.toString())._();
|
html.script().$type("text/javascript")._(script.toString())._();
|
||||||
|
}
|
||||||
|
|
||||||
UL<DIV<DIV<Hamlet>>> ul = html.
|
UL<DIV<DIV<Hamlet>>> ul = html.
|
||||||
div("#cs-wrapper.ui-widget").
|
div("#cs-wrapper.ui-widget").
|
||||||
|
|
|
@ -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.SchedulerInfo;
|
||||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.SchedulerTypeInfo;
|
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.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.server.utils.BuilderUtils;
|
||||||
import org.apache.hadoop.yarn.util.AdHocLogDumper;
|
import org.apache.hadoop.yarn.util.AdHocLogDumper;
|
||||||
import org.apache.hadoop.yarn.util.ConverterUtils;
|
import org.apache.hadoop.yarn.util.ConverterUtils;
|
||||||
import org.apache.hadoop.yarn.webapp.BadRequestException;
|
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.NotFoundException;
|
||||||
import org.apache.hadoop.yarn.webapp.util.WebAppUtils;
|
import org.apache.hadoop.yarn.webapp.util.WebAppUtils;
|
||||||
|
|
||||||
|
@ -263,8 +265,17 @@ public class RMWebServices {
|
||||||
@POST
|
@POST
|
||||||
@Path("/scheduler/logs")
|
@Path("/scheduler/logs")
|
||||||
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
@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();
|
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();
|
ResourceScheduler rs = rm.getResourceScheduler();
|
||||||
int period = Integer.parseInt(time);
|
int period = Integer.parseInt(time);
|
||||||
if (period <= 0) {
|
if (period <= 0) {
|
||||||
|
|
|
@ -26,7 +26,9 @@ import static org.mockito.Matchers.isA;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
|
import java.security.Principal;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -37,6 +39,7 @@ import javax.ws.rs.core.MediaType;
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.service.Service.STATE;
|
import org.apache.hadoop.service.Service.STATE;
|
||||||
import org.apache.hadoop.util.VersionInfo;
|
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.nodelabels.RMNodeLabelsManager;
|
||||||
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.QueueMetrics;
|
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.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.scheduler.fifo.FifoScheduler;
|
||||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppsInfo;
|
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.util.YarnVersionInfo;
|
||||||
|
import org.apache.hadoop.yarn.webapp.ForbiddenException;
|
||||||
import org.apache.hadoop.yarn.webapp.GenericExceptionHandler;
|
import org.apache.hadoop.yarn.webapp.GenericExceptionHandler;
|
||||||
import org.apache.hadoop.yarn.webapp.JerseyTestBase;
|
import org.apache.hadoop.yarn.webapp.JerseyTestBase;
|
||||||
import org.apache.hadoop.yarn.webapp.WebServicesTestUtils;
|
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);
|
null, null, null, null, null, null, null, emptySet, emptySet);
|
||||||
assertTrue(appsInfo.getApps().isEmpty());
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue