cloud-8.x-2.0-beta1/modules/cloud_service_providers/k8s/js/k8s_node_heatmap.js

modules/cloud_service_providers/k8s/js/k8s_node_heatmap.js
(function ($, drupalSettings) {

  'use strict';

  if (!drupalSettings.k8s || !drupalSettings.k8s.metrics_enable) {
    $("#k8s_node_allocated_resources").parent().parent().hide();
    return;
  }

  const FONT_FAMILY = 'Lucida Grande, -apple-system, BlinkMacSystemFont';
  const FONT_SIZE_AXIS_LABEL = 13;
  const RECT_PX = 85;
  const MAX_Y_AXIS_LABEL_STR_LENGTH = 30;

  // Create a dummy <span>...</span> to measure the length (pixel) of a string.
  // This <span/> element should be removed later on.
  $('<span/>', {id: 'width_check', appendTo: 'body'})
    .css('font-size', FONT_SIZE_AXIS_LABEL)
    .css('visibility', 'hidden')
    .css('position', 'absolute')
    .css('white-space', 'nowrap');

  //Read the data.
  let updateNodeHeatmap = function () {
    d3.json(window.location.pathname + '/allocated_resources').then(function (nodes) {

      // List up the node names.
      let get_node_name = function (node_name) {
        return node_name.length > MAX_Y_AXIS_LABEL_STR_LENGTH
          ? node_name.substring(0, MAX_Y_AXIS_LABEL_STR_LENGTH) + '...'
          : node_name;
      };

      let node_names = nodes.map(function (node) {
        return get_node_name(node.name);
      });

      let y_axis_label_px = 0;

      // 1. Get the max pods capacity across the K8s cluster.
      let max_pods_capacity = Math.max(...(nodes.map(function (node) {
        return node.podsCapacity;
      })));

      // 2. Set the dimensions and margins of the graph.
      let pods_allocation_scale
        = [...Array(max_pods_capacity).keys()].map(i => ++i);

      let pods = Array();
      nodes.forEach(function (node) {

        // Measure the length (pixel) of a string.
        // let width_check_px = $('#width_check').text(node.name).get(0).offsetWidth;
        let node_name = get_node_name(node.name);

        let width_check_px = $('#width_check').text(node_name).get(0).offsetWidth;

        y_axis_label_px = width_check_px > y_axis_label_px
          ? width_check_px
          : y_axis_label_px;

        for (let i = 0; i < node.podsCapacity; i++) {
          let pod_name = i < node.podsAllocation && node.pods[i]
            ? node.pods[i].name
            : '';

          let cpu_usage = i < node.podsAllocation && node.pods[i]
            // @TODO: "* 100 * 3" is an adjustment. Change the unit for the normal use.
            ? node.pods[i].cpuUsage * 100 * 3 + 20
            : 0;

          let memory_usage = i < node.podsAllocation && node.pods[i]
            ? Math.floor(node.pods[i].memoryUsage / 1024 / 1024)
            : '0';

          pods.push({
            'index': i + 1,
            'nodeName': node_name,
            'name': pod_name,
            'cpuUsage': cpu_usage,
            'memoryUsage': memory_usage
          });
        }
      });

      // Remove the <span/> element.
      $('#width_check').empty();

      let margin = {top: 20, right: 15, bottom: 20, left: y_axis_label_px + 15};
      let width = max_pods_capacity * RECT_PX - margin.left - margin.right;
      let height = node_names.length * RECT_PX - margin.top - margin.bottom;
      height = height < RECT_PX
        ? RECT_PX - margin.top / 4 - margin.bottom / 4
        : height;

      // * Important * Initialization the SVG object.  If you remove this code,
      // the SVG object will be duplicated repeatedly.
      $('#k8s_node_heatmap').empty();

      // Append the svg object to the body of the page.
      let svg = d3.select('#k8s_node_heatmap')
        .append('svg')
        .attr('class' ,'node_heatmap')
        .attr('width', width + margin.left + margin.right)
        .attr('height', height + margin.top + margin.bottom)
        .append('g')
        .style('font-family', FONT_FAMILY)
        .attr('transform',
          'translate(' + margin.left + ', ' + margin.top + ')');

      // Build X scales and axis:
      let x = d3.scaleBand()
        .range([0, width])
        .domain(pods_allocation_scale)
        .padding(0.1);

      svg.append('g')
        .style('font-size', FONT_SIZE_AXIS_LABEL)
        .style('font-family', FONT_FAMILY)
        .attr('transform', 'translate(0,' + height + ')')
        .call(d3.axisBottom(x).tickSize(0))
        .select('.domain').remove();

      // Build Y scales and axis:
      let y = d3.scaleBand()
        .range([height, 0])
        .domain(node_names)
        .padding(0.1);

      svg.append('g')
        .style('font-size', FONT_SIZE_AXIS_LABEL)
        .style('font-family', FONT_FAMILY)
        .call(d3.axisLeft(y).tickSize(0))
        .select('.domain').remove();

      // Build color scale.
      let my_color = d3.scaleLinear()
      // .range(['white', '#69b3a2']) // Green.
      // .range(['white', '#f89830']) // Light Orange.
      // .range(['white', '#f79820']) // Dark Orange.
      // Dark orange.
        .range(['white', '#ff9900'])
        .domain([1, 100]);

      // Create a tooltip.
      let tooltip = d3.select('#k8s_node_heatmap')
        .append('div')
        .style('opacity', 0)
        .attr('class', 'tooltip')
        .style('background-color', 'white')
        .style('border', 'solid')
        .style('border-width', '2px')
        .style('border-radius', '5px')
        .style('padding', '5px');

      // Three function that change the tooltip when user hover / move / leave a cell.
      let mouseover = function (pod) {
        tooltip.style('opacity', 1);
        d3.select(this)
          .style('stroke', '#cc6600')
          .style('opacity', 1);
      };

      let mousemove = function (pod) {
        tooltip
          .html('<strong>' + pod.name + '</strong><br />'
            + pod.cpuUsage + '<br />'
            + pod.memoryUsage + ' MiB')
          .style('display', 'block')
          .style('left', (d3.mouse(this)[0] + y_axis_label_px + RECT_PX / 2.5) + 'px')
          .style('top', node_names.length > 1
            ? (d3.mouse(this)[1] + RECT_PX / 2.5 + margin.top) + 'px'
            : ($('#k8s_node_heatmap').get(0).offset().top) + 'px'
          );
      };

      let mouseleave = function (pod) {
        tooltip.style('display', 'none');
        d3.select(this)
          .style('stroke', 'none')
          .style('opacity', 0.8);
      };

      // Add the squares.
      svg.selectAll('svg.node_heatmap')
        .exit()
        .remove()
        .data(pods, function (pod) {
          return pod.nodeName + ':' + pod.index;
        })
        .enter()
        .append('rect')
        .attr('x', function (pod) {
          return x(pod.index);
        })
        .attr('y', function (pod) {
          return y(pod.nodeName);
        })
        .attr('rx', 5)
        .attr('ry', 5)
        .attr('width', x.bandwidth())
        .attr('height', y.bandwidth())
        // 20: Adjust color.
        .style('fill', function (pod) {
          return my_color(pod.cpuUsage + 20);
        })
        .style('stroke-width', 4)
        .style('stroke', 'none')
        .style('opacity', 0.8)
        .on('mouseover', mouseover)
        .on('mousemove', mousemove)
        .on('mouseleave', mouseleave);
    });
  };

  updateNodeHeatmap();

  let interval = drupalSettings.k8s.k8s_js_refresh_interval || 10;
  setInterval(function() {
    updateNodeHeatmap();
  }, interval * 1000);

} (jQuery, drupalSettings));

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc