Skip to content

Commit

Permalink
Generalize SparkUI header to display tabs dynamically
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewor14 committed Apr 3, 2014
1 parent a37ad4f commit ed25dfc
Show file tree
Hide file tree
Showing 15 changed files with 37 additions and 78 deletions.
22 changes: 0 additions & 22 deletions core/src/main/scala/org/apache/spark/ui/Page.scala

This file was deleted.

2 changes: 1 addition & 1 deletion core/src/main/scala/org/apache/spark/ui/SparkUI.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ private[spark] class SparkUI(

/** Initialize all components of the server. Must be called before bind(). */
def start() {
attachTab(new BlockManagerTab(this))
attachTab(new JobProgressTab(this))
attachTab(new BlockManagerTab(this))
attachTab(new EnvironmentTab(this))
attachTab(new ExecutorsTab(this))
attachHandler(createStaticHandler(SparkUI.STATIC_RESOURCE_DIR, "/static"))
Expand Down
36 changes: 8 additions & 28 deletions core/src/main/scala/org/apache/spark/ui/UIUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ import scala.xml.Node
/** Utility functions for generating XML pages with spark content. */
private[spark] object UIUtils {

import Page._

// SimpleDateFormat is not thread-safe. Don't expose it to avoid improper use.
private val dateFormat = new ThreadLocal[SimpleDateFormat]() {
override def initialValue(): SimpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
Expand Down Expand Up @@ -62,26 +60,13 @@ private[spark] object UIUtils {
basePath: String,
appName: String,
title: String,
page: Page.Value) : Seq[Node] = {
val jobs = page match {
case Stages =>
<li class="active"><a href={prependBaseUri(basePath, "/stages")}>Stages</a></li>
case _ => <li><a href={prependBaseUri(basePath, "/stages")}>Stages</a></li>
}
val storage = page match {
case Storage =>
<li class="active"><a href={prependBaseUri(basePath, "/storage")}>Storage</a></li>
case _ => <li><a href={prependBaseUri(basePath, "/storage")}>Storage</a></li>
}
val environment = page match {
case Environment =>
<li class="active"><a href={prependBaseUri(basePath, "/environment")}>Environment</a></li>
case _ => <li><a href={prependBaseUri(basePath, "/environment")}>Environment</a></li>
}
val executors = page match {
case Executors =>
<li class="active"><a href={prependBaseUri(basePath, "/executors")}>Executors</a></li>
case _ => <li><a href={prependBaseUri(basePath, "/executors")}>Executors</a></li>
tabs: Seq[UITab],
activeTab: UITab) : Seq[Node] = {

val header = tabs.map { tab =>
<li class={if (tab == activeTab) "active" else ""}>
<a href={prependBaseUri(basePath, "/" + tab.prefix)}>{tab.name}</a>
</li>
}

<html>
Expand All @@ -100,12 +85,7 @@ private[spark] object UIUtils {
<a href={prependBaseUri(basePath, "/")} class="brand">
<img src={prependBaseUri("/static/spark-logo-77x50px-hd.png")} />
</a>
<ul class="nav">
{jobs}
{storage}
{environment}
{executors}
</ul>
<ul class="nav">{header}</ul>
<p class="navbar-text pull-right"><strong>{appName}</strong> application UI</p>
</div>
</div>
Expand Down
11 changes: 5 additions & 6 deletions core/src/main/scala/org/apache/spark/ui/WebUI.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ private[spark] abstract class WebUI(securityManager: SecurityManager, basePath:
protected val handlers = ArrayBuffer[ServletContextHandler]()
protected var serverInfo: Option[ServerInfo] = None

def getTabs: Seq[UITab] = tabs.toSeq
def getHandlers: Seq[ServletContextHandler] = handlers.toSeq
def getListeners: Seq[SparkListener] = tabs.flatMap(_.listener)

/** Attach a tab to this UI, along with all of its attached pages. Only valid before bind(). */
def attachTab(tab: UITab) {
tab.start()
Expand All @@ -65,12 +69,6 @@ private[spark] abstract class WebUI(securityManager: SecurityManager, basePath:
handlers += handler
}

/** Return a list of listeners attached to this UI. */
def getListeners = tabs.flatMap(_.listener)

/** Return a list of handlers attached to this UI. */
def getHandlers = handlers.toSeq

/** Initialize all components of the server. Must be called before bind(). */
def start()

Expand Down Expand Up @@ -98,6 +96,7 @@ private[spark] abstract class WebUI(securityManager: SecurityManager, basePath:
private[spark] abstract class UITab(val prefix: String) {
val pages = ArrayBuffer[UIPage]()
var listener: Option[SparkListener] = None
var name = prefix.capitalize

/** Attach a page to this tab. This prepends the page's prefix with the tab's own prefix. */
def attachPage(page: UIPage) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ private[ui] class EnvironmentTab(parent: SparkUI) extends UITab("environment") {
assert(listener.isDefined, "EnvironmentTab has not started yet!")
listener.get.asInstanceOf[EnvironmentListener]
}

def headerTabs: Seq[UITab] = parent.getTabs
}

/**
Expand Down
3 changes: 1 addition & 2 deletions core/src/main/scala/org/apache/spark/ui/env/IndexPage.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import javax.servlet.http.HttpServletRequest
import scala.xml.Node

import org.apache.spark.ui.{UIUtils, UIPage}
import org.apache.spark.ui.Page.Environment

private[ui] class IndexPage(parent: EnvironmentTab) extends UIPage("") {
private val appName = parent.appName
Expand All @@ -46,7 +45,7 @@ private[ui] class IndexPage(parent: EnvironmentTab) extends UIPage("") {
<h4>Classpath Entries</h4> {classpathEntriesTable}
</span>

UIUtils.headerSparkPage(content, basePath, appName, "Environment", Environment)
UIUtils.headerSparkPage(content, basePath, appName, "Environment", parent.headerTabs, parent)
}

private def propertyHeader = Seq("Name", "Value")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ private[ui] class ExecutorsTab(parent: SparkUI) extends UITab("executors") {
assert(listener.isDefined, "ExecutorsTab has not started yet!")
listener.get.asInstanceOf[ExecutorsListener]
}

def headerTabs: Seq[UITab] = parent.getTabs
}

/**
Expand Down
5 changes: 2 additions & 3 deletions core/src/main/scala/org/apache/spark/ui/exec/IndexPage.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import javax.servlet.http.HttpServletRequest
import scala.xml.Node

import org.apache.spark.ui.{UIPage, UIUtils}
import org.apache.spark.ui.Page.Executors
import org.apache.spark.util.Utils

private[ui] class IndexPage(parent: ExecutorsTab) extends UIPage("") {
Expand Down Expand Up @@ -56,8 +55,8 @@ private[ui] class IndexPage(parent: ExecutorsTab) extends UIPage("") {
</div>
</div>;

UIUtils.headerSparkPage(
content, basePath, appName, "Executors (" + execInfo.size + ")", Executors)
UIUtils.headerSparkPage(content, basePath, appName, "Executors (" + execInfo.size + ")",
parent.headerTabs, parent)
}

/** Header fields for the executors table */
Expand Down
3 changes: 1 addition & 2 deletions core/src/main/scala/org/apache/spark/ui/jobs/IndexPage.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import scala.xml.{Node, NodeSeq}

import org.apache.spark.scheduler.Schedulable
import org.apache.spark.ui.{UIPage, UIUtils}
import org.apache.spark.ui.Page.Stages

/** Page showing list of all ongoing and recently finished stages and pools */
private[ui] class IndexPage(parent: JobProgressTab) extends UIPage("") {
Expand Down Expand Up @@ -92,7 +91,7 @@ private[ui] class IndexPage(parent: JobProgressTab) extends UIPage("") {
<h4 id ="failed">Failed Stages ({failedStages.size})</h4> ++
failedStagesTable.toNodeSeq

UIUtils.headerSparkPage(content, basePath, appName, "Spark Stages", Stages)
UIUtils.headerSparkPage(content, basePath, appName, "Spark Stages", parent.headerTabs, parent)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,6 @@ private[ui] class JobProgressTab(parent: SparkUI) extends UITab("stages") {
}

def isFairScheduler = jobProgressListener.schedulingMode.exists(_ == SchedulingMode.FAIR)

def headerTabs: Seq[UITab] = parent.getTabs
}
5 changes: 2 additions & 3 deletions core/src/main/scala/org/apache/spark/ui/jobs/PoolPage.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import scala.xml.Node

import org.apache.spark.scheduler.{Schedulable, StageInfo}
import org.apache.spark.ui.{UIPage, UIUtils}
import org.apache.spark.ui.Page.Stages

/** Page showing specific pool details */
private[ui] class PoolPage(parent: JobProgressTab) extends UIPage("pool") {
Expand Down Expand Up @@ -51,8 +50,8 @@ private[ui] class PoolPage(parent: JobProgressTab) extends UIPage("pool") {
<h4>Summary </h4> ++ poolTable.toNodeSeq ++
<h4>{activeStages.size} Active Stages</h4> ++ activeStagesTable.toNodeSeq

UIUtils.headerSparkPage(
content, basePath, appName, "Fair Scheduler Pool: " + poolName, Stages)
UIUtils.headerSparkPage(content, basePath, appName, "Fair Scheduler Pool: " + poolName,
parent.headerTabs, parent)
}
}
}
9 changes: 4 additions & 5 deletions core/src/main/scala/org/apache/spark/ui/jobs/StagePage.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import javax.servlet.http.HttpServletRequest
import scala.xml.Node

import org.apache.spark.ui.{UIPage, UIUtils}
import org.apache.spark.ui.Page.Stages
import org.apache.spark.util.{Utils, Distribution}

/** Page showing statistics and task list for a given stage */
Expand All @@ -42,8 +41,8 @@ private[ui] class StagePage(parent: JobProgressTab) extends UIPage("stage") {
<h4>Summary Metrics</h4> No tasks have started yet
<h4>Tasks</h4> No tasks have started yet
</div>
return UIUtils.headerSparkPage(
content, basePath, appName, "Details for Stage %s".format(stageId), Stages)
return UIUtils.headerSparkPage(content, basePath, appName,
"Details for Stage %s".format(stageId), parent.headerTabs, parent)
}

val tasks = listener.stageIdToTaskData(stageId).values.toSeq.sortBy(_.taskInfo.launchTime)
Expand Down Expand Up @@ -204,8 +203,8 @@ private[ui] class StagePage(parent: JobProgressTab) extends UIPage("stage") {
<h4>Aggregated Metrics by Executor</h4> ++ executorTable.toNodeSeq ++
<h4>Tasks</h4> ++ taskTable

UIUtils.headerSparkPage(
content, basePath, appName, "Details for Stage %d".format(stageId), Stages)
UIUtils.headerSparkPage(content, basePath, appName, "Details for Stage %d".format(stageId),
parent.headerTabs, parent)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ private[ui] class BlockManagerTab(parent: SparkUI) extends UITab("storage") {
assert(listener.isDefined, "BlockManagerTab has not started yet!")
listener.get.asInstanceOf[BlockManagerListener]
}

def headerTabs: Seq[UITab] = parent.getTabs
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import scala.xml.Node

import org.apache.spark.storage.RDDInfo
import org.apache.spark.ui.{UIPage, UIUtils}
import org.apache.spark.ui.Page.Storage
import org.apache.spark.util.Utils

/** Page showing list of RDD's currently stored in the cluster */
Expand All @@ -35,7 +34,7 @@ private[ui] class IndexPage(parent: BlockManagerTab) extends UIPage("") {
override def render(request: HttpServletRequest): Seq[Node] = {
val rdds = listener.rddInfoList
val content = UIUtils.listingTable(rddHeader, rddRow, rdds)
UIUtils.headerSparkPage(content, basePath, appName, "Storage ", Storage)
UIUtils.headerSparkPage(content, basePath, appName, "Storage ", parent.headerTabs, parent)
}

/** Header fields for the RDD table */
Expand Down
8 changes: 4 additions & 4 deletions core/src/main/scala/org/apache/spark/ui/storage/RDDPage.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import scala.xml.Node

import org.apache.spark.storage.{BlockId, BlockStatus, StorageStatus, StorageUtils}
import org.apache.spark.ui.{UIPage, UIUtils}
import org.apache.spark.ui.Page.Storage
import org.apache.spark.util.Utils

/** Page showing storage details for a given RDD */
Expand All @@ -37,7 +36,8 @@ private[ui] class RddPage(parent: BlockManagerTab) extends UIPage("rdd") {
val storageStatusList = listener.storageStatusList
val rddInfo = listener.rddInfoList.find(_.id == rddId).getOrElse {
// Rather than crashing, render an "RDD Not Found" page
return UIUtils.headerSparkPage(Seq[Node](), basePath, appName, "RDD Not Found", Storage)
return UIUtils.headerSparkPage(Seq[Node](), basePath, appName, "RDD Not Found",
parent.headerTabs, parent)
}

// Worker table
Expand Down Expand Up @@ -95,8 +95,8 @@ private[ui] class RddPage(parent: BlockManagerTab) extends UIPage("rdd") {
</div>
</div>;

UIUtils.headerSparkPage(
content, basePath, appName, "RDD Storage Info for " + rddInfo.name, Storage)
UIUtils.headerSparkPage(content, basePath, appName, "RDD Storage Info for " + rddInfo.name,
parent.headerTabs, parent)
}

/** Header fields for the worker table */
Expand Down

0 comments on commit ed25dfc

Please sign in to comment.