def render()

in core/src/main/scala/org/apache/spark/ui/jobs/JobPage.scala [224:529]


  def render(request: HttpServletRequest): Seq[Node] = {
    val parameterId = request.getParameter("id")
    require(parameterId != null && parameterId.nonEmpty, "Missing id parameter")

    val jobId = parameterId.toInt
    val (jobData, sqlExecutionId) = store.asOption(store.jobWithAssociatedSql(jobId)).getOrElse {
      val content =
        <div id="no-info">
          <p>No information to display for job {jobId}</p>
        </div>
      return UIUtils.headerSparkPage(
        request, s"Details for Job $jobId", content, parent)
    }

    val isComplete = jobData.status != JobExecutionStatus.RUNNING
    val stages = jobData.stageIds.map { stageId =>
      // This could be empty if the listener hasn't received information about the
      // stage or if the stage information has been garbage collected
      store.asOption(store.lastStageAttempt(stageId)).getOrElse {
        new v1.StageData(
          status = v1.StageStatus.PENDING,
          stageId = stageId,
          attemptId = 0,
          numTasks = 0,
          numActiveTasks = 0,
          numCompleteTasks = 0,
          numFailedTasks = 0,
          numKilledTasks = 0,
          numCompletedIndices = 0,

          submissionTime = None,
          firstTaskLaunchedTime = None,
          completionTime = None,
          failureReason = None,

          executorDeserializeTime = 0L,
          executorDeserializeCpuTime = 0L,
          executorRunTime = 0L,
          executorCpuTime = 0L,
          resultSize = 0L,
          jvmGcTime = 0L,
          resultSerializationTime = 0L,
          memoryBytesSpilled = 0L,
          diskBytesSpilled = 0L,
          peakExecutionMemory = 0L,
          inputBytes = 0L,
          inputRecords = 0L,
          outputBytes = 0L,
          outputRecords = 0L,
          shuffleRemoteBlocksFetched = 0L,
          shuffleLocalBlocksFetched = 0L,
          shuffleFetchWaitTime = 0L,
          shuffleRemoteBytesRead = 0L,
          shuffleRemoteBytesReadToDisk = 0L,
          shuffleLocalBytesRead = 0L,
          shuffleReadBytes = 0L,
          shuffleReadRecords = 0L,
          shuffleCorruptMergedBlockChunks = 0L,
          shuffleMergedFetchFallbackCount = 0L,
          shuffleMergedRemoteBlocksFetched = 0L,
          shuffleMergedLocalBlocksFetched = 0L,
          shuffleMergedRemoteChunksFetched = 0L,
          shuffleMergedLocalChunksFetched = 0L,
          shuffleMergedRemoteBytesRead = 0L,
          shuffleMergedLocalBytesRead = 0L,
          shuffleRemoteReqsDuration = 0L,
          shuffleMergedRemoteReqsDuration = 0L,
          shuffleWriteBytes = 0L,
          shuffleWriteTime = 0L,
          shuffleWriteRecords = 0L,

          name = "Unknown",
          description = None,
          details = "Unknown",
          schedulingPool = null,

          rddIds = Nil,
          accumulatorUpdates = Nil,
          tasks = None,
          executorSummary = None,
          speculationSummary = None,
          killedTasksSummary = Map(),
          ResourceProfile.UNKNOWN_RESOURCE_PROFILE_ID,
          peakExecutorMetrics = None,
          taskMetricsDistributions = None,
          executorMetricsDistributions = None,
          isShufflePushEnabled = false,
          shuffleMergersCount = 0)
      }
    }

    val activeStages = Buffer[v1.StageData]()
    val completedStages = Buffer[v1.StageData]()
    // If the job is completed, then any pending stages are displayed as "skipped":
    val pendingOrSkippedStages = Buffer[v1.StageData]()
    val failedStages = Buffer[v1.StageData]()
    for (stage <- stages) {
      if (stage.submissionTime.isEmpty) {
        pendingOrSkippedStages += stage
      } else if (stage.completionTime.isDefined) {
        if (stage.status == v1.StageStatus.FAILED) {
          failedStages += stage
        } else {
          completedStages += stage
        }
      } else {
        activeStages += stage
      }
    }

    val basePath = "jobs/job"

    val pendingOrSkippedTableId =
      if (isComplete) {
        "skipped"
      } else {
        "pending"
      }

    val activeStagesTable =
      new StageTableBase(store, request, activeStages.toSeq, "active", "activeStage",
        parent.basePath, basePath, parent.isFairScheduler,
        killEnabled = parent.killEnabled, isFailedStage = false)
    val pendingOrSkippedStagesTable =
      new StageTableBase(store, request, pendingOrSkippedStages.toSeq, pendingOrSkippedTableId,
        "pendingStage", parent.basePath, basePath, parent.isFairScheduler,
        killEnabled = false, isFailedStage = false)
    val completedStagesTable =
      new StageTableBase(store, request, completedStages.toSeq, "completed", "completedStage",
        parent.basePath, basePath, parent.isFairScheduler,
        killEnabled = false, isFailedStage = false)
    val failedStagesTable =
      new StageTableBase(store, request, failedStages.toSeq, "failed", "failedStage",
        parent.basePath, basePath, parent.isFairScheduler,
        killEnabled = false, isFailedStage = true)

    val shouldShowActiveStages = activeStages.nonEmpty
    val shouldShowPendingStages = !isComplete && pendingOrSkippedStages.nonEmpty
    val shouldShowCompletedStages = completedStages.nonEmpty
    val shouldShowSkippedStages = isComplete && pendingOrSkippedStages.nonEmpty
    val shouldShowFailedStages = failedStages.nonEmpty

    val summary: NodeSeq =
      <div>
        <ul class="list-unstyled">
          <li>
            <Strong>Status:</Strong>
            {jobData.status}
          </li>
          <li>
            <Strong>Submitted:</Strong>
            {JobDataUtil.getFormattedSubmissionTime(jobData)}
          </li>
          <li>
            <Strong>Duration:</Strong>
            {JobDataUtil.getFormattedDuration(jobData)}
          </li>
          {
            if (sqlExecutionId.isDefined) {
              <li>
                <strong>Associated SQL Query: </strong>
                {<a href={"%s/SQL/execution/?id=%s".format(
                  UIUtils.prependBaseUri(request, parent.basePath),
                  sqlExecutionId.get)
                }>{sqlExecutionId.get}</a>}
              </li>
            }
          }
          {
            if (jobData.jobGroup.isDefined) {
              <li>
                <strong>Job Group:</strong>
                {jobData.jobGroup.get}
              </li>
            }
          }
          {
            if (shouldShowActiveStages) {
              <li>
                <a href="#active"><strong>Active Stages:</strong></a>
                {activeStages.size}
              </li>
            }
          }
          {
            if (shouldShowPendingStages) {
              <li>
                <a href="#pending">
                  <strong>Pending Stages:</strong>
                </a>{pendingOrSkippedStages.size}
              </li>
            }
          }
          {
            if (shouldShowCompletedStages) {
              <li>
                <a href="#completed"><strong>Completed Stages:</strong></a>
                {completedStages.size}
              </li>
            }
          }
          {
            if (shouldShowSkippedStages) {
            <li>
              <a href="#skipped"><strong>Skipped Stages:</strong></a>
              {pendingOrSkippedStages.size}
            </li>
          }
          }
          {
            if (shouldShowFailedStages) {
              <li>
                <a href="#failed"><strong>Failed Stages:</strong></a>
                {failedStages.size}
              </li>
            }
          }
        </ul>
      </div>

    var content = summary
    val appStartTime = store.applicationInfo().attempts.head.startTime.getTime()

    content ++= makeTimeline((activeStages ++ completedStages ++ failedStages).toSeq,
      store.executorList(false), appStartTime)

    val operationGraphContent = store.asOption(store.operationGraphForJob(jobId)) match {
      case Some(operationGraph) => UIUtils.showDagVizForJob(jobId, operationGraph)
      case None =>
        <div id="no-info">
          <p>No DAG visualization information to display for job {jobId}</p>
        </div>
    }
    content ++= operationGraphContent

    if (shouldShowActiveStages) {
      content ++=
        <span id="active" class="collapse-aggregated-activeStages collapse-table"
            onClick="collapseTable('collapse-aggregated-activeStages','aggregated-activeStages')">
          <h4>
            <span class="collapse-table-arrow arrow-open"></span>
            <a>Active Stages ({activeStages.size})</a>
          </h4>
        </span> ++
        <div class="aggregated-activeStages collapsible-table">
          {activeStagesTable.toNodeSeq}
        </div>
    }
    if (shouldShowPendingStages) {
      content ++=
        <span id="pending" class="collapse-aggregated-pendingOrSkippedStages collapse-table"
            onClick="collapseTable('collapse-aggregated-pendingOrSkippedStages',
            'aggregated-pendingOrSkippedStages')">
          <h4>
            <span class="collapse-table-arrow arrow-open"></span>
            <a>Pending Stages ({pendingOrSkippedStages.size})</a>
          </h4>
        </span> ++
        <div class="aggregated-pendingOrSkippedStages collapsible-table">
          {pendingOrSkippedStagesTable.toNodeSeq}
        </div>
    }
    if (shouldShowCompletedStages) {
      content ++=
        <span id="completed" class="collapse-aggregated-completedStages collapse-table"
            onClick="collapseTable('collapse-aggregated-completedStages',
            'aggregated-completedStages')">
          <h4>
            <span class="collapse-table-arrow arrow-open"></span>
            <a>Completed Stages ({completedStages.size})</a>
          </h4>
        </span> ++
        <div class="aggregated-completedStages collapsible-table">
          {completedStagesTable.toNodeSeq}
        </div>
    }
    if (shouldShowSkippedStages) {
      content ++=
        <span id="skipped" class="collapse-aggregated-pendingOrSkippedStages collapse-table"
            onClick="collapseTable('collapse-aggregated-pendingOrSkippedStages',
            'aggregated-pendingOrSkippedStages')">
          <h4>
            <span class="collapse-table-arrow arrow-open"></span>
            <a>Skipped Stages ({pendingOrSkippedStages.size})</a>
          </h4>
        </span> ++
        <div class="aggregated-pendingOrSkippedStages collapsible-table">
          {pendingOrSkippedStagesTable.toNodeSeq}
        </div>
    }
    if (shouldShowFailedStages) {
      content ++=
        <span id ="failed" class="collapse-aggregated-failedStages collapse-table"
            onClick="collapseTable('collapse-aggregated-failedStages','aggregated-failedStages')">
          <h4>
            <span class="collapse-table-arrow arrow-open"></span>
            <a>Failed Stages ({failedStages.size})</a>
          </h4>
        </span> ++
        <div class="aggregated-failedStages collapsible-table">
          {failedStagesTable.toNodeSeq}
        </div>
    }
    UIUtils.headerSparkPage(
      request, s"Details for Job $jobId", content, parent, showVisualization = true)
  }