Repository: spark
Updated Branches:
  refs/heads/branch-1.4 ba24dfae7 -> 76e8344f2


http://git-wip-us.apache.org/repos/asf/spark/blob/76e8344f/core/src/main/resources/org/apache/spark/ui/static/spark-dag-viz.css
----------------------------------------------------------------------
diff --git 
a/core/src/main/resources/org/apache/spark/ui/static/spark-dag-viz.css 
b/core/src/main/resources/org/apache/spark/ui/static/spark-dag-viz.css
new file mode 100644
index 0000000..8481710
--- /dev/null
+++ b/core/src/main/resources/org/apache/spark/ui/static/spark-dag-viz.css
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#dag-viz-graph svg path {
+  stroke: #444444;
+  stroke-width: 1.5px;
+}
+
+#dag-viz-graph svg g.cluster rect {
+  stroke-width: 4px;
+  stroke-opacity: 0.5;
+}
+
+#dag-viz-graph svg g.node circle,
+#dag-viz-graph svg g.node rect {
+  fill: #444444;
+}
+
+#dag-viz-graph svg g.node.cached circle,
+#dag-viz-graph svg g.node.cached rect {
+  fill: #FF0000;
+}
+
+/* Job page specific styles */
+
+#dag-viz-graph svg.job marker#marker-arrow path {
+  fill: #444444;
+  stroke-width: 0px;
+}
+
+#dag-viz-graph svg.job g.cluster rect {
+  fill: #FFFFFF;
+  stroke: #AADFFF;
+}
+
+#dag-viz-graph svg.job g.cluster[id*="stage"] rect {
+  stroke: #FFDDEE;
+  stroke-width: 6px;
+}
+
+#dag-viz-graph svg.job g#cross-stage-edges path {
+  fill: none;
+}
+
+#dag-viz-graph svg.job g.cluster text {
+  fill: #AAAAAA;
+}
+
+/* Stage page specific styles */
+
+#dag-viz-graph svg.stage g.cluster rect {
+  fill: #F0F8FF;
+  stroke: #AADFFF;
+}
+
+#dag-viz-graph svg.stage g.cluster[id*="stage"] rect {
+  fill: #FFFFFF;
+  stroke: #FFDDEE;
+  stroke-width: 6px;
+}
+
+#dag-viz-graph svg.stage g.node g.label text tspan {
+  fill: #FFFFFF;
+}
+
+#dag-viz-graph svg.stage g.cluster text {
+  fill: #444444;
+  font-weight: bold;
+}

http://git-wip-us.apache.org/repos/asf/spark/blob/76e8344f/core/src/main/resources/org/apache/spark/ui/static/spark-dag-viz.js
----------------------------------------------------------------------
diff --git 
a/core/src/main/resources/org/apache/spark/ui/static/spark-dag-viz.js 
b/core/src/main/resources/org/apache/spark/ui/static/spark-dag-viz.js
index 99b0294..eb9cf50 100644
--- a/core/src/main/resources/org/apache/spark/ui/static/spark-dag-viz.js
+++ b/core/src/main/resources/org/apache/spark/ui/static/spark-dag-viz.js
@@ -23,10 +23,10 @@
  *   (2) an RDD and its operation scopes, and
  *   (3) an RDD's operation scopes and the stage / job hierarchy
  *
- * An operation scope is a general, named code block representing an operation
- * that instantiates RDDs (e.g. filter, textFile, reduceByKey). An operation
- * scope can be nested inside of other scopes if the corresponding RDD 
operation
- * invokes other such operations (for more detail, see 
o.a.s.rdd.operationScope).
+ * An operation scope is a general, named code block that instantiates RDDs
+ * (e.g. filter, textFile, reduceByKey). An operation scope can be nested 
inside
+ * of other scopes if the corresponding RDD operation invokes other such 
operations
+ * (for more detail, see o.a.s.rdd.RDDOperationScope).
  *
  * A stage may include one or more operation scopes if the RDD operations are
  * streamlined into one stage (e.g. rdd.map(...).filter(...).flatMap(...)).
@@ -52,14 +52,7 @@
  */
 
 var VizConstants = {
-  rddColor: "#444444",
-  rddCachedColor: "#FF0000",
-  rddOperationColor: "#AADFFF",
-  stageColor: "#FFDDEE",
-  clusterLabelColor: "#888888",
-  edgeColor: "#444444",
-  edgeWidth: "1.5px",
-  svgMarginX: 0,
+  svgMarginX: 20,
   svgMarginY: 20,
   stageSep: 50,
   graphPrefix: "graph_",
@@ -69,13 +62,21 @@ var VizConstants = {
   stageClusterPrefix: "cluster_stage_"
 };
 
-// Helper d3 accessors for the elements that contain our graph and its metadata
-function graphContainer() { return d3.select("#dag-viz-graph"); }
-function metadataContainer() { return d3.select("#dag-viz-metadata"); }
+var JobPageVizConstants = {
+  clusterLabelSize: 11,
+  stageClusterLabelSize: 14
+}
+
+var StagePageVizConstants = {
+  clusterLabelSize: 14,
+  stageClusterLabelSize: 18
+}
 
 /*
  * Show or hide the RDD DAG visualization.
+ *
  * The graph is only rendered the first time this is called.
+ * This is the narrow interface called from the Scala UI code.
  */
 function toggleDagViz(forJob) {
   var arrowSelector = ".expand-dag-viz-arrow";
@@ -113,70 +114,52 @@ function toggleDagViz(forJob) {
 function renderDagViz(forJob) {
 
   // If there is not a dot file to render, fail fast and report error
+  var jobOrStage = forJob ? "job" : "stage";
   if (metadataContainer().empty()) {
-    graphContainer().append("div").text(
-      "No visualization information available for this " + (forJob ? "job" : 
"stage"));
+    graphContainer()
+      .append("div")
+      .text("No visualization information available for this " + jobOrStage);
     return;
   }
 
-  var svg = graphContainer().append("svg");
+  // Render
+  var svg = graphContainer()
+    .append("svg")
+    .attr("class", jobOrStage);
   if (forJob) {
     renderDagVizForJob(svg);
   } else {
     renderDagVizForStage(svg);
   }
 
-  // Find cached RDDs
+  // Find cached RDDs and mark them as such
   metadataContainer().selectAll(".cached-rdd").each(function(v) {
     var nodeId = VizConstants.nodePrefix + d3.select(this).text();
-    graphContainer().selectAll("#" + nodeId).classed("cached", true);
+    svg.selectAll("#" + nodeId).classed("cached", true);
   });
 
-  // Set the appropriate SVG dimensions to ensure that all elements are 
displayed
-  var boundingBox = svg.node().getBBox();
-  svg.style("width", (boundingBox.width + VizConstants.svgMarginX) + "px");
-  svg.style("height", (boundingBox.height + VizConstants.svgMarginY) + "px");
-
-  // Add labels to clusters because dagre-d3 doesn't do this for us
-  svg.selectAll("g.cluster rect").each(function() {
-    var rect = d3.select(this);
-    var cluster = d3.select(this.parentNode);
-    // Shift the boxes up a little to make room for the labels
-    rect.attr("y", toFloat(rect.attr("y")) - 10);
-    rect.attr("height", toFloat(rect.attr("height")) + 10);
-    var labelX = toFloat(rect.attr("x")) + toFloat(rect.attr("width")) - 5;
-    var labelY = toFloat(rect.attr("y")) + 15;
-    var labelText = cluster.attr("name").replace(VizConstants.clusterPrefix, 
"");
-    cluster.append("text")
-      .attr("x", labelX)
-      .attr("y", labelY)
-      .attr("text-anchor", "end")
-      .text(labelText);
-  });
-
-  // We have shifted a few elements upwards, so we should fix the SVG views
-  var startX = -VizConstants.svgMarginX;
-  var startY = -VizConstants.svgMarginY;
-  var endX = toFloat(svg.style("width")) + VizConstants.svgMarginX;
-  var endY = toFloat(svg.style("height")) + VizConstants.svgMarginY;
-  var newViewBox = startX + " " + startY + " " + endX + " " + endY;
-  svg.attr("viewBox", newViewBox);
-
-  // Lastly, apply some custom style to the DAG
-  styleDagViz(forJob);
+  // More post-processing
+  drawClusterLabels(svg, forJob);
+  resizeSvg(svg);
 }
 
-/* Render the RDD DAG visualization for a stage. */
+/* Render the RDD DAG visualization on the stage page. */
 function renderDagVizForStage(svgContainer) {
   var metadata = metadataContainer().select(".stage-metadata");
   var dot = metadata.select(".dot-file").text();
-  var containerId = VizConstants.graphPrefix + metadata.attr("stageId");
+  var containerId = VizConstants.graphPrefix + metadata.attr("stage-id");
   var container = svgContainer.append("g").attr("id", containerId);
   renderDot(dot, container);
+
+  // Round corners on RDDs
+  svgContainer
+    .selectAll("g.node rect")
+    .attr("rx", "5")
+    .attr("ry", "5");
 }
 
 /*
- * Render the RDD DAG visualization for a job.
+ * Render the RDD DAG visualization on the job page.
  *
  * Due to limitations in dagre-d3, each stage is rendered independently so that
  * we have more control on how to position them. Unfortunately, this means we
@@ -186,32 +169,46 @@ function renderDagVizForStage(svgContainer) {
 function renderDagVizForJob(svgContainer) {
   var crossStageEdges = [];
 
+  // Each div.stage-metadata contains the information needed to generate the 
graph
+  // for a stage. This includes the DOT file produced from the appropriate UI 
listener,
+  // any incoming and outgoing edges, and any cached RDDs that belong to this 
stage.
   metadataContainer().selectAll(".stage-metadata").each(function(d, i) {
     var metadata = d3.select(this);
     var dot = metadata.select(".dot-file").text();
-    var stageId = metadata.attr("stageId");
+    var stageId = metadata.attr("stage-id");
     var containerId = VizConstants.graphPrefix + stageId;
-    // TODO: handle stage attempts
+    // Link each graph to the corresponding stage page (TODO: handle stage 
attempts)
     var stageLink =
       "/stages/stage/?id=" + stageId.replace(VizConstants.stagePrefix, "") + 
"&attempt=0";
     var container = svgContainer
-      .append("a").attr("xlink:href", stageLink)
-      .append("g").attr("id", containerId);
-    // Now we need to shift the container for this stage so it doesn't overlap
-    // with existing ones. We do not need to do this for the first stage.
+      .append("a")
+      .attr("xlink:href", stageLink)
+      .append("g")
+      .attr("id", containerId);
+
+    // Now we need to shift the container for this stage so it doesn't overlap 
with
+    // existing ones, taking into account the position and width of the last 
stage's
+    // container. We do not need to do this for the first stage of this job.
     if (i > 0) {
-      // Take into account the position and width of the last stage's container
-      var existingStages = stageClusters();
+      var existingStages = svgContainer
+        .selectAll("g.cluster")
+        .filter("[id*=\"" + VizConstants.stageClusterPrefix + "\"]");
       if (!existingStages.empty()) {
-        var lastStage = existingStages[0].pop();
-        var lastStageId = d3.select(lastStage).attr("id");
-        var lastStageWidth = toFloat(d3.select("#" + lastStageId + " 
rect").attr("width"));
-        var lastStagePosition = getAbsolutePosition(lastStageId);
+        var lastStage = d3.select(existingStages[0].pop());
+        var lastStageId = lastStage.attr("id");
+        var lastStageWidth = toFloat(svgContainer
+          .select("#" + lastStageId)
+          .select("rect")
+          .attr("width"));
+        var lastStagePosition = getAbsolutePosition(lastStage);
         var offset = lastStagePosition.x + lastStageWidth + 
VizConstants.stageSep;
         container.attr("transform", "translate(" + offset + ", 0)");
       }
     }
+
+    // Actually render the stage
     renderDot(dot, container);
+
     // If there are any incoming edges into this graph, keep track of them to 
render
     // them separately later. Note that we cannot draw them now because we 
need to
     // put these edges in a separate container that is on top of all stage 
graphs.
@@ -221,15 +218,7 @@ function renderDagVizForJob(svgContainer) {
     });
   });
 
-  // Draw edges that cross stages
-  if (crossStageEdges.length > 0) {
-    var container = svgContainer.append("g").attr("id", "cross-stage-edges");
-    for (var i = 0; i < crossStageEdges.length; i++) {
-      var fromRDDId = crossStageEdges[i][0];
-      var toRDDId = crossStageEdges[i][1];
-      connectRDDs(fromRDDId, toRDDId, container);
-    }
-  }
+  drawCrossStageEdges(crossStageEdges, svgContainer);
 }
 
 /* Render the dot file as an SVG in the given container. */
@@ -243,99 +232,156 @@ function renderDot(dot, container) {
   renderer(container, g);
 }
 
-/* Style the visualization we just rendered. */
-function styleDagViz(forJob) {
-  graphContainer().selectAll("svg g.cluster rect")
-    .style("fill", "white")
-    .style("stroke", VizConstants.rddOperationColor)
-    .style("stroke-width", "4px")
-    .style("stroke-opacity", "0.5");
-  graphContainer().selectAll("svg g.cluster text")
-    .attr("fill", VizConstants.clusterLabelColor)
-    .attr("font-size", "11px");
-  graphContainer().selectAll("svg path")
-    .style("stroke", VizConstants.edgeColor)
-    .style("stroke-width", VizConstants.edgeWidth);
-  stageClusters()
-    .select("rect")
-    .style("stroke", VizConstants.stageColor)
-    .style("strokeWidth", "6px");
-
-  // Put an arrow at the end of every edge
-  // We need to do this because we manually render some edges ourselves
-  // For these edges, we borrow the arrow marker generated by dagre-d3
-  var dagreD3Marker = graphContainer().select("svg g.edgePaths marker").node();
-  graphContainer().select("svg")
-    .append(function() { return dagreD3Marker.cloneNode(true); })
-    .attr("id", "marker-arrow")
-    .select("path")
-    .attr("fill", VizConstants.edgeColor)
-    .attr("strokeWidth", "0px");
-  graphContainer().selectAll("svg g > path").attr("marker-end", 
"url(#marker-arrow)");
-  graphContainer().selectAll("svg g.edgePaths def").remove(); // We no longer 
need these
-
-  // Apply any job or stage specific styles
+/* -------------------- *
+ * | Helper functions | *
+ * -------------------- */
+
+// Helper d3 accessors
+function graphContainer() { return d3.select("#dag-viz-graph"); }
+function metadataContainer() { return d3.select("#dag-viz-metadata"); }
+
+/*
+ * Helper function to create draw a label for each cluster.
+ *
+ * We need to do this manually because dagre-d3 does not support labeling 
clusters.
+ * In general, the clustering support for dagre-d3 is quite limited at this 
point.
+ */
+function drawClusterLabels(svgContainer, forJob) {
   if (forJob) {
-    styleDagVizForJob();
+    var clusterLabelSize = JobPageVizConstants.clusterLabelSize;
+    var stageClusterLabelSize = JobPageVizConstants.stageClusterLabelSize;
   } else {
-    styleDagVizForStage();
+    var clusterLabelSize = StagePageVizConstants.clusterLabelSize;
+    var stageClusterLabelSize = StagePageVizConstants.stageClusterLabelSize;
   }
+  svgContainer.selectAll("g.cluster").each(function() {
+    var cluster = d3.select(this);
+    var isStage = cluster.attr("id").indexOf(VizConstants.stageClusterPrefix) 
> -1;
+    var labelSize = isStage ? stageClusterLabelSize : clusterLabelSize;
+    drawClusterLabel(cluster, labelSize);
+  });
 }
 
-/* Apply job-page-specific style to the visualization. */
-function styleDagVizForJob() {
-  graphContainer().selectAll("svg g.node circle")
-    .style("fill", VizConstants.rddColor);
-  // TODO: add a legend to explain what a highlighted dot means
-  graphContainer().selectAll("svg g.cached circle")
-    .style("fill", VizConstants.rddCachedColor);
-  graphContainer().selectAll("svg g#cross-stage-edges path")
-    .style("fill", "none");
+/*
+ * Helper function to draw a label for the given cluster element based on its 
name.
+ *
+ * In the process, we need to expand the bounding box to make room for the 
label.
+ * We need to do this because dagre-d3 did not take this into account when it 
first
+ * rendered the bounding boxes. Note that this means we need to adjust the 
view box
+ * of the SVG afterwards since we shifted a few boxes around.
+ */
+function drawClusterLabel(d3cluster, fontSize) {
+  var cluster = d3cluster;
+  var rect = d3cluster.select("rect");
+  rect.attr("y", toFloat(rect.attr("y")) - fontSize);
+  rect.attr("height", toFloat(rect.attr("height")) + fontSize);
+  var labelX = toFloat(rect.attr("x")) + toFloat(rect.attr("width")) - 
fontSize / 2;
+  var labelY = toFloat(rect.attr("y")) + fontSize * 1.5;
+  var labelText = cluster.attr("name").replace(VizConstants.clusterPrefix, "");
+  cluster.append("text")
+    .attr("x", labelX)
+    .attr("y", labelY)
+    .attr("text-anchor", "end")
+    .style("font-size", fontSize)
+    .text(labelText);
 }
 
-/* Apply stage-page-specific style to the visualization. */
-function styleDagVizForStage() {
-  graphContainer().selectAll("svg g.node rect")
-    .style("fill", "none")
-    .style("stroke", VizConstants.rddColor)
-    .style("stroke-width", "2px")
-    .attr("rx", "5") // round corners
-    .attr("ry", "5");
-    // TODO: add a legend to explain what a highlighted RDD means
-  graphContainer().selectAll("svg g.cached rect")
-    .style("stroke", VizConstants.rddCachedColor);
-  graphContainer().selectAll("svg g.node g.label text tspan")
-    .style("fill", VizConstants.rddColor);
+/*
+ * Helper function to size the SVG appropriately such that all elements are 
displyed.
+ * This assumes that all outermost elements are clusters (rectangles).
+ */
+function resizeSvg(svg) {
+  var allClusters = svg.selectAll("g.cluster rect")[0];
+  var startX = -VizConstants.svgMarginX +
+    toFloat(d3.min(allClusters, function(e) {
+      return getAbsolutePosition(d3.select(e)).x;
+    }));
+  var startY = -VizConstants.svgMarginY +
+    toFloat(d3.min(allClusters, function(e) {
+      return getAbsolutePosition(d3.select(e)).y;
+    }));
+  var endX = VizConstants.svgMarginX +
+    toFloat(d3.max(allClusters, function(e) {
+      var t = d3.select(e)
+      return getAbsolutePosition(t).x + toFloat(t.attr("width"));
+    }));
+  var endY = VizConstants.svgMarginY +
+    toFloat(d3.max(allClusters, function(e) {
+      var t = d3.select(e)
+      return getAbsolutePosition(t).y + toFloat(t.attr("height"));
+    }));
+  var width = endX - startX;
+  var height = endY - startY;
+  svg.attr("viewBox", startX + " " + startY + " " + width + " " + height)
+     .attr("width", width)
+     .attr("height", height);
 }
 
 /*
- * (Job page only) Helper method to compute the absolute
- * position of the group element identified by the given ID.
+ * (Job page only) Helper function to draw edges that cross stage boundaries.
+ * We need to do this manually because we render each stage separately in 
dagre-d3.
  */
-function getAbsolutePosition(groupId) {
-  var obj = d3.select("#" + groupId).filter("g");
-  var _x = 0, _y = 0;
+function drawCrossStageEdges(edges, svgContainer) {
+  if (edges.length == 0) {
+    return;
+  }
+  // Draw the paths first
+  var edgesContainer = svgContainer.append("g").attr("id", 
"cross-stage-edges");
+  for (var i = 0; i < edges.length; i++) {
+    var fromRDDId = edges[i][0];
+    var toRDDId = edges[i][1];
+    connectRDDs(fromRDDId, toRDDId, edgesContainer, svgContainer);
+  }
+  // Now draw the arrows by borrowing the arrow marker generated by dagre-d3
+  var dagreD3Marker = svgContainer.select("g.edgePaths marker").node();
+  if (!dagreD3Marker.empty()) {
+    svgContainer
+      .append(function() { return dagreD3Marker.cloneNode(true); })
+      .attr("id", "marker-arrow")
+    svgContainer.selectAll("g > path").attr("marker-end", 
"url(#marker-arrow)");
+    svgContainer.selectAll("g.edgePaths def").remove(); // We no longer need 
these
+  }
+}
+
+/*
+ * (Job page only) Helper function to compute the absolute
+ * position of the specified element in our graph.
+ */
+function getAbsolutePosition(d3selection) {
+  if (d3selection.empty()) {
+    throw "Attempted to get absolute position of an empty selection.";
+  }
+  var obj = d3selection;
+  var _x = toFloat(obj.attr("x")) || 0;
+  var _y = toFloat(obj.attr("y")) || 0;
   while (!obj.empty()) {
     var transformText = obj.attr("transform");
-    var translate = d3.transform(transformText).translate
-    _x += translate[0];
-    _y += translate[1];
-    obj = d3.select(obj.node().parentNode).filter("g")
+    if (transformText) {
+      var translate = d3.transform(transformText).translate;
+      _x += toFloat(translate[0]);
+      _y += toFloat(translate[1]);
+    }
+    // Climb upwards to find how our parents are translated
+    obj = d3.select(obj.node().parentNode);
+    // Stop when we've reached the graph container itself
+    if (obj.node() == graphContainer().node()) {
+      break;
+    }
   }
   return { x: _x, y: _y };
 }
 
-/* (Job page only) Connect two RDD nodes with a curved edge. */
-function connectRDDs(fromRDDId, toRDDId, container) {
+/* (Job page only) Helper function to connect two RDDs with a curved edge. */
+function connectRDDs(fromRDDId, toRDDId, edgesContainer, svgContainer) {
   var fromNodeId = VizConstants.nodePrefix + fromRDDId;
-  var toNodeId = VizConstants.nodePrefix + toRDDId
-  var fromPos = getAbsolutePosition(fromNodeId);
-  var toPos = getAbsolutePosition(toNodeId);
+  var toNodeId = VizConstants.nodePrefix + toRDDId;
+  var fromPos = getAbsolutePosition(svgContainer.select("#" + fromNodeId));
+  var toPos = getAbsolutePosition(svgContainer.select("#" + toNodeId));
 
   // On the job page, RDDs are rendered as dots (circles). When rendering the 
path,
   // we need to account for the radii of these circles. Otherwise the arrow 
heads
   // will bleed into the circle itself.
-  var delta = toFloat(graphContainer()
+  var delta = toFloat(svgContainer
     .select("g.node#" + toNodeId)
     .select("circle")
     .attr("r"));
@@ -375,18 +421,15 @@ function connectRDDs(fromRDDId, toRDDId, container) {
   }
 
   var line = d3.svg.line().interpolate("basis");
-  container.append("path").datum(points).attr("d", line);
+  edgesContainer.append("path").datum(points).attr("d", line);
 }
 
-/* Helper d3 accessor to clusters that represent stages. */
-function stageClusters() {
-  return graphContainer().selectAll("g.cluster").filter(function() {
-    return d3.select(this).attr("id").indexOf(VizConstants.stageClusterPrefix) 
> -1;
-  });
-}
-
-/* Helper method to convert attributes to numeric values. */
+/* Helper function to convert attributes to numeric values. */
 function toFloat(f) {
-  return parseFloat(f.replace(/px$/, ""));
+  if (f) {
+    return parseFloat(f.toString().replace(/px$/, ""));
+  } else {
+    return f;
+  }
 }
 

http://git-wip-us.apache.org/repos/asf/spark/blob/76e8344f/core/src/main/scala/org/apache/spark/ui/UIUtils.scala
----------------------------------------------------------------------
diff --git a/core/src/main/scala/org/apache/spark/ui/UIUtils.scala 
b/core/src/main/scala/org/apache/spark/ui/UIUtils.scala
index 2f3fb18..e2d03f8 100644
--- a/core/src/main/scala/org/apache/spark/ui/UIUtils.scala
+++ b/core/src/main/scala/org/apache/spark/ui/UIUtils.scala
@@ -156,13 +156,10 @@ private[spark] object UIUtils extends Logging {
 
   def commonHeaderNodes: Seq[Node] = {
     <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-    <link rel="stylesheet" href={prependBaseUri("/static/bootstrap.min.css")}
-          type="text/css" />
-    <link rel="stylesheet" href={prependBaseUri("/static/webui.css")}
-          type="text/css" />
-    <link rel="stylesheet" href={prependBaseUri("/static/vis.min.css")}
-          typ="text/css" />
-    <link rel="stylesheet" 
href={prependBaseUri("/static/timeline-view.css")}></link>
+    <link rel="stylesheet" href={prependBaseUri("/static/bootstrap.min.css")} 
type="text/css" />
+    <link rel="stylesheet" href={prependBaseUri("/static/webui.css")} 
type="text/css" />
+    <link rel="stylesheet" href={prependBaseUri("/static/vis.min.css")} 
type="text/css" />
+    <link rel="stylesheet" href={prependBaseUri("/static/timeline-view.css")} 
type="text/css" />
     <script src={prependBaseUri("/static/sorttable.js")} ></script>
     <script src={prependBaseUri("/static/jquery-1.11.1.min.js")}></script>
     <script src={prependBaseUri("/static/vis.min.js")}></script>
@@ -174,6 +171,7 @@ private[spark] object UIUtils extends Logging {
   }
 
   def vizHeaderNodes: Seq[Node] = {
+    <link rel="stylesheet" href={prependBaseUri("/static/spark-dag-viz.css")} 
type="text/css" />
     <script src={prependBaseUri("/static/d3.min.js")}></script>
     <script src={prependBaseUri("/static/dagre-d3.min.js")}></script>
     <script src={prependBaseUri("/static/graphlib-dot.min.js")}></script>
@@ -358,7 +356,7 @@ private[spark] object UIUtils extends Logging {
       <div id="dag-viz-metadata">
         {
           graphs.map { g =>
-            <div class="stage-metadata" stageId={g.rootCluster.id} 
style="display:none">
+            <div class="stage-metadata" stage-id={g.rootCluster.id} 
style="display:none">
               <div class="dot-file">{RDDOperationGraph.makeDotFile(g, 
forJob)}</div>
               { g.incomingEdges.map { e => <div 
class="incoming-edge">{e.fromId},{e.toId}</div> } }
               { g.outgoingEdges.map { e => <div 
class="outgoing-edge">{e.fromId},{e.toId}</div> } }

http://git-wip-us.apache.org/repos/asf/spark/blob/76e8344f/core/src/main/scala/org/apache/spark/ui/scope/RDDOperationGraph.scala
----------------------------------------------------------------------
diff --git 
a/core/src/main/scala/org/apache/spark/ui/scope/RDDOperationGraph.scala 
b/core/src/main/scala/org/apache/spark/ui/scope/RDDOperationGraph.scala
index a18c193..edf005f 100644
--- a/core/src/main/scala/org/apache/spark/ui/scope/RDDOperationGraph.scala
+++ b/core/src/main/scala/org/apache/spark/ui/scope/RDDOperationGraph.scala
@@ -181,22 +181,29 @@ private[ui] object RDDOperationGraph extends Logging {
     if (forJob) {
       s"""${node.id} [label=" " shape="circle" padding="5" 
labelStyle="font-size: 0"]"""
     } else {
-      s"""${node.id} [label="${node.name} (${node.id})"]"""
+      s"""${node.id} [label="${node.name} (${node.id})" padding="5" 
labelStyle="font-size: 10"]"""
     }
   }
 
   /** Return the dot representation of a subgraph in an RDDOperationGraph. */
   private def makeDotSubgraph(
-      scope: RDDOperationCluster,
+      cluster: RDDOperationCluster,
       forJob: Boolean,
       indent: String): String = {
     val subgraph = new StringBuilder
-    subgraph.append(indent + s"subgraph cluster${scope.id} {\n")
-    subgraph.append(indent + s"""  label="${scope.name}";\n""")
-    scope.childNodes.foreach { node =>
+    // TODO: move specific graph properties like these to spark-dag-viz.js
+    val paddingTop = if (forJob) 10 else 20
+    subgraph.append(indent + s"subgraph cluster${cluster.id} {\n")
+    subgraph.append(indent + s"""  label="${cluster.name}";\n""")
+    // If there are nested clusters, add some padding
+    // Do this for the stage page because we use bigger fonts there
+    if (cluster.childClusters.nonEmpty) {
+      subgraph.append(indent + s"""  paddingTop="$paddingTop";\n""")
+    }
+    cluster.childNodes.foreach { node =>
       subgraph.append(indent + s"  ${makeDotNode(node, forJob)};\n")
     }
-    scope.childClusters.foreach { cscope =>
+    cluster.childClusters.foreach { cscope =>
       subgraph.append(makeDotSubgraph(cscope, forJob, indent + "  "))
     }
     subgraph.append(indent + "}\n")


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to