| /* |
| * 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. |
| */ |
| |
| /** |
| * Code for drawing the enclosed rectangles that make up the physical plan for this topology. |
| * Exports a single global function drawPhysicalPlan. |
| */ |
| (function (global) { |
| var topo, instances, containers; |
| |
| function drawPhysicalPlan(planController, data, packingData, div_id, outerWidth, minHeight, cluster, environ, toponame) { |
| var margin = { |
| top: 50, |
| right: 0, |
| bottom: 20, |
| left: 0 |
| }; |
| var width = outerWidth - margin.left - margin.right; |
| |
| instances = data.result.instances; |
| containers = data.result.stmgrs; |
| containerPlan = packingData.container_plans; |
| |
| var sptblt = Object.keys(data.result.spouts) |
| .concat(Object.keys(data.result.bolts)); |
| |
| var color = d3.scale.ordinal().domain(sptblt.sort()).range(colors); |
| |
| for (var ck in containers) { |
| var container = containers[ck]; |
| container.children = []; |
| } |
| |
| for (var i in sptblt) { |
| var comp = sptblt[i]; |
| for (var wk in instances) { |
| var worker = instances[wk]; |
| if (worker.name === comp) { |
| containers[worker.stmgrId].children.push(worker); |
| } |
| } |
| } |
| |
| var containerList = _.values(containers); |
| containerList.forEach(function (container) { |
| // make ordering of instances the same in each container |
| container.children = _.sortBy(container.children, 'name'); |
| // Parse index |
| container.index = parseInt(container.id.split('-')[1]); |
| // Search for resource config in packing plan |
| container.required_resources = { |
| 'cpu': 0, |
| 'disk': 0, |
| 'ram': 0 |
| }; |
| for (var i in containerPlan) { |
| var packing = containerPlan[i]; |
| if (packing.id === container.index) { |
| container.required_resources = packing.required_resources; |
| } |
| } |
| }); |
| |
| // Sort the containers by their id so they are easier to find in the UI. |
| containerList = _.sortBy(containerList, function(container) { |
| return container.index; |
| }); |
| |
| var maxInstances = d3.max(containerList, function (d) { |
| return d.children.length; |
| }); |
| |
| /** |
| * Config paramaters for container/heron instance layout |
| */ |
| |
| // margin outside of each container as a fraction of the container width |
| var containerMarginRatio = 0.08; |
| // padding inside each container as a fraction of container width |
| var containerPaddingRatio = 0.05; |
| // margin around each heron instance as a faction of the instance width |
| var instanceMarginRatio = 0.15; |
| // smallest allowable width for a heron instance (below, this and we expand vertically) |
| var minInstanceWidth = 6; |
| // largest allowable width for heron instance (above this we shrink) |
| var maxInstanceWith = 30; |
| |
| /** |
| * Compute the container and instance sizes |
| */ |
| |
| var innerCols = Math.ceil(Math.sqrt(maxInstances)); |
| // compute the min columns allowed to keep instance size below limit |
| var minContainerCols = Math.ceil(width / (innerCols * maxInstanceWith / (1 - 2 * instanceMarginRatio))); |
| // compute the max columns allowed to keep instance size above min limit |
| var maxContainerCols = Math.ceil(width / (innerCols * minInstanceWidth / (1 - 2 * instanceMarginRatio))); |
| // compute # cols to give 50% more columns than rows, and bound it by max/min columns |
| var desiredContainerCols = Math.ceil(Math.sqrt(containerList.length) * 1.5); |
| var cols = Math.min(maxContainerCols, Math.max(minContainerCols, desiredContainerCols)); |
| // from there, compute the required number of rows and exact instance/container sizes... |
| var rows = Math.ceil(containerList.length / cols); |
| var innerRows = Math.ceil(maxInstances / innerCols); |
| var containerWidth = (width / cols) * (1 - 2 * containerMarginRatio); |
| var containerPadding = containerWidth * containerPaddingRatio; |
| var containerMargin = containerMarginRatio * containerWidth; |
| var instanceSize = (containerWidth - containerPadding * 2) / innerCols; |
| var containerHeight = instanceSize * innerRows + containerPadding * 2; |
| var height = (containerHeight + containerMargin * 2) * rows; |
| var totalVisHeight = height + margin.top + margin.bottom; |
| var totalVisWidth = Math.min(width, containerList.length * (containerWidth + containerMargin * 2)); |
| |
| var svg = d3.select(div_id).text("").append("svg") |
| .attr("width", totalVisWidth) |
| .attr("height", totalVisHeight) |
| .style("z-index", "1") |
| .append("g") |
| .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); |
| |
| var textContainer = svg.append('text') |
| .attr('x', (totalVisWidth - margin.left - margin.right) / 2) |
| .attr('y', height + 25) |
| .style('text-anchor', 'middle'); |
| |
| var instance_tip = d3.tip() |
| .attr('class', 'd3-tip main text-center') |
| .offset([8, 0]) |
| .direction('s') |
| .html(function(d) { |
| var rows = window.pollingMetrics; |
| var cols = window.pollingPeriods; |
| var result = d.id + '<br>'; |
| if (d.currentMetric && _.isNumber(d.currentMetricValue)) { |
| result += '<strong>' + d.currentMetric.metric.format(d.currentMetricValue) + '</strong>'; |
| result += ' '; |
| result += d.currentMetric.metric.legendDescription; |
| result += ' over last '; |
| result += d.currentMetric.time.name; |
| if (d.tooltipDetails) { |
| result += d.tooltipDetails; |
| } |
| } |
| return result; |
| }); |
| |
| var container_tip = d3.tip() |
| .attr('class', 'd3-tip main text-center container') |
| .offset([8, 0]) |
| .direction('s') |
| .html(function (container) { |
| return '<ul>' + |
| '<li>cpu required: ' + container.required_resources.cpu + '</li>' + |
| '<li>ram required: ' + (container.required_resources.ram / 1048576).toFixed(2) + 'MB</li>' + |
| '<li>disk required: ' + (container.required_resources.disk / 1048576).toFixed(2) + 'MB</li>' + |
| '</ul>'; |
| }); |
| |
| var containerGs = svg.selectAll('.physical-plan') |
| .data(containerList, function (d) { return d.id; }) |
| .enter() |
| .append('g') |
| .attr('class', 'physical-plan') |
| .attr('transform', function (d, i) { |
| var x = (i % cols) * (containerWidth + 2 * containerMargin) + containerMargin; |
| var y = Math.floor(i / cols) * (containerHeight + 2 * containerMargin) + containerMargin; |
| return 'translate(' + x + ',' + y + ')'; |
| }); |
| |
| containerGs.append('rect') |
| .attr('width', containerWidth) |
| .attr('height', containerHeight) |
| .attr('class', 'container') |
| .on('mouseover', container_tip.show) |
| .on('mouseout', container_tip.hide); |
| |
| containerGs.selectAll('.instance') |
| .data(function (d) { return d.children; }, function (d) { return d.id; }) |
| .enter() |
| .append('rect') |
| .attr('class', 'instance') |
| .style('fill', function (d) { |
| d.defaultColor = color(d.name); |
| d.color = d.color || d.defaultColor; |
| return d.color; |
| }) |
| .attr('width', instanceSize * (1 - 2 * instanceMarginRatio)) |
| .attr('height', instanceSize * (1 - 2 * instanceMarginRatio)) |
| .attr('x', function (d, i) { return containerPadding + (i % innerCols) * instanceSize + instanceSize * instanceMarginRatio; }) |
| .attr('y', function (d, i) { return containerPadding +Math.floor(i / innerCols) * instanceSize + instanceSize * instanceMarginRatio; }) |
| .on('mouseover', function (d) { |
| planController.physicalComponentHoverOver(d, instance_tip); |
| }) |
| .on('mouseout', function (d) { |
| planController.physicalComponentHoverOut(d, instance_tip); |
| }) |
| .on('click', function (d) { |
| planController.physicalComponentClicked(d); |
| }); |
| |
| svg.call(instance_tip); |
| svg.call(container_tip); |
| } |
| |
| // Stash the old values for transition. |
| function stash(d) { |
| d.x0 = d.x; |
| d.dx0 = d.dx; |
| } |
| |
| global.drawPhysicalPlan = drawPhysicalPlan; |
| }(this)); |