Пример #1
0
def render_graph_pnp(graph_template, translated_metrics):
    graph_title = None
    vertical_label = None

    rrdgraph_commands = ""

    legend_precision = graph_template.get("legend_precision", 2)
    legend_scale = graph_template.get("legend_scale", 1)
    legend_scale_symbol = scale_symbols[legend_scale]

    # Define one RRD variable for each of the available metrics.
    # Note: We need to use the original name, not the translated one.
    for var_name, metrics in translated_metrics.items():
        rrd = "$RRDBASE$_" + metrics["orig_name"] + ".rrd"
        scale = metrics["scale"]
        unit = metrics["unit"]

        if scale != 1.0:
            rrdgraph_commands += "DEF:%s_UNSCALED=%s:1:MAX " % (var_name, rrd)
            rrdgraph_commands += "CDEF:%s=%s_UNSCALED,%f,* " % (
                var_name, var_name, scale)

        else:
            rrdgraph_commands += "DEF:%s=%s:1:MAX " % (var_name, rrd)

        # Scaling for legend
        rrdgraph_commands += "CDEF:%s_LEGSCALED=%s,%f,/ " % (
            var_name, var_name, legend_scale)

        # Prepare negative variants for upside-down graph
        rrdgraph_commands += "CDEF:%s_NEG=%s,-1,* " % (var_name, var_name)
        rrdgraph_commands += "CDEF:%s_LEGSCALED_NEG=%s_LEGSCALED,-1,* " % (
            var_name, var_name)

    # Now add areas and lines to the graph
    graph_metrics = []

    # Graph with upside down metrics? (e.g. for Disk IO)
    have_upside_down = False

    # Compute width of the right column of the legend
    max_title_length = 0
    for nr, metric_definition in enumerate(graph_template["metrics"]):
        if len(metric_definition) >= 3:
            title = metric_definition[2]
        elif not "," in metric_definition:
            metric_name = metric_definition[0].split("#")[0]
            mi = translated_metrics[metric_name]
            title = mi["title"]
        else:
            title = ""
        max_title_length = max(max_title_length, len(title))

    for nr, metric_definition in enumerate(graph_template["metrics"]):
        metric_name = metric_definition[0]
        line_type = metric_definition[1]  # "line", "area", "stack"

        # Optional title, especially for derived values
        if len(metric_definition) >= 3:
            title = metric_definition[2]
        else:
            title = ""

        # Prefixed minus renders the metrics in negative direction
        if line_type[0] == '-':
            have_upside_down = True
            upside_down = True
            upside_down_factor = -1
            line_type = line_type[1:]
            upside_down_suffix = "_NEG"
        else:
            upside_down = False
            upside_down_factor = 1
            upside_down_suffix = ""

        if line_type == "line":
            draw_type = "LINE"
            draw_stack = ""
        elif line_type == "area":
            draw_type = "AREA"
            draw_stack = ""
        elif line_type == "stack":
            draw_type = "AREA"
            draw_stack = ":STACK"

        # User can specify alternative color using a suffixed #aabbcc
        if '#' in metric_name:
            metric_name, custom_color = metric_name.split("#", 1)
        else:
            custom_color = None

        commands = ""
        # Derived value with RBN syntax (evaluated by RRDTool!).
        if "," in metric_name:
            # We evaluate just in order to get color and unit.
            # TODO: beware of division by zero. All metrics are set to 1 here.
            _value, unit, color = evaluate(metric_name, translated_metrics)

            if "@" in metric_name:
                expression, _explicit_unit_name = metric_name.rsplit(
                    "@", 1)  # isolate expression
            else:
                expression = metric_name

            # Choose a unique name for the derived variable and compute it
            commands += "CDEF:DERIVED%d=%s " % (nr, expression)
            if upside_down:
                commands += "CDEF:DERIVED%d_NEG=DERIVED%d,-1,* " % (nr, nr)

            metric_name = "DERIVED%d" % nr
            # Scaling and upsidedown handling for legend
            commands += "CDEF:%s_LEGSCALED=%s,%f,/ " % (
                metric_name, metric_name, legend_scale)
            if upside_down:
                commands += "CDEF:%s_LEGSCALED%s=%s,%f,/ " % (
                    metric_name, upside_down_suffix, metric_name,
                    legend_scale * upside_down_factor)

        else:
            mi = translated_metrics[metric_name]
            if not title:
                title = mi["title"]
            color = parse_color_into_hexrgb(mi["color"])
            unit = mi["unit"]

        if custom_color:
            color = "#" + custom_color

        # Paint the graph itself
        # TODO: Die Breite des Titels intelligent berechnen. Bei legend = "mirrored" muss man die
        # Vefügbare Breite ermitteln und aufteilen auf alle Titel
        right_pad = " " * (max_title_length - len(title))
        commands += "%s:%s%s%s:\"%s%s\"%s " % (
            draw_type, metric_name, upside_down_suffix, color,
            title.replace(":", "\\:"), right_pad, draw_stack)
        if line_type == "area":
            commands += "LINE:%s%s%s " % (
                metric_name, upside_down_suffix,
                render_color(darken_color(parse_color(color), 0.2)))

        unit_symbol = unit["symbol"]
        if unit_symbol == "%":
            unit_symbol = "%%"
        else:
            unit_symbol = " " + unit_symbol

        graph_metrics.append((metric_name, unit_symbol, commands))

        # Use title and label of this metrics as default for the graph
        if title and not graph_title:
            graph_title = title
        if not vertical_label:
            vertical_label = unit["title"]

    # Now create the rrdgraph commands for all metrics - according to the choosen layout
    for metric_name, unit_symbol, commands in graph_metrics:
        rrdgraph_commands += commands

        legend_symbol = unit_symbol
        if unit_symbol and unit_symbol[0] == " ":
            legend_symbol = " %s%s" % (legend_scale_symbol, unit_symbol[1:])
        if legend_symbol == " bits/s":
            # Use a literal '%s' so that GPRINT outputs values with the appropriate
            # SI magnitude (e.g. 123456 -> 123.456 k)
            legend_symbol = " %sbit/s"

        for what, what_title in [("AVERAGE", _("average")), ("MAX", _("max")),
                                 ("LAST", _("last"))]:
            rrdgraph_commands += "GPRINT:%s_LEGSCALED:%s:\"%%8.%dlf%s %s\" " % (
                metric_name,
                what,
                legend_precision,
                legend_symbol,
                what_title,
            )
        rrdgraph_commands += "COMMENT:\"\\n\" "

    # add horizontal rules for warn and crit scalars
    for scalar in graph_template.get("scalars", []):
        rrdgraph_commands += _scalar_value_command(scalar, translated_metrics)

    # For graphs with both up and down, paint a gray rule at 0
    if have_upside_down:
        rrdgraph_commands += "HRULE:0#c0c0c0 "

    # Now compute the arguments for the command line of rrdgraph
    rrdgraph_arguments = ""

    graph_title = graph_template.get("title", graph_title)
    vertical_label = graph_template.get("vertical_label", vertical_label)

    rrdgraph_arguments += " --vertical-label %s --title %s " % (
        cmk.utils.quote_shell_string(
            vertical_label or " "), cmk.utils.quote_shell_string(graph_title))

    min_value, max_value = get_graph_range(graph_template, translated_metrics)
    if min_value is not None and max_value is not None:
        rrdgraph_arguments += " -l %f -u %f" % (min_value, max_value)
    else:
        rrdgraph_arguments += " -l 0"

    return graph_title + "\n" + rrdgraph_arguments + "\n" + rrdgraph_commands + "\n"
Пример #2
0
        t = graph_artwork["start_time"]
        color = parse_color(curve["color"])

        if curve["type"] == "area":
            curve = cast(LayoutedCurveArea, curve)
            points = curve["points"]
            prev_lower = None
            prev_upper = None

            gradient = (
                t_orig,
                v_orig,
                t_orig,
                v_orig + v_mm,
                (darken_color(color, color_gradient), color, lighten_color(color, color_gradient)),
                (0.0, 0.5, 1.0),
            )

            for lower, upper in points:
                if (
                    lower is not None
                    and upper is not None
                    and prev_lower is not None
                    and prev_upper is not None
                ):
                    pdf_document.begin_path()
                    pdf_document.move_to(trans_t(t - step) - 0.01, trans_v(prev_lower))
                    pdf_document.line_to(trans_t(t - step) - 0.01, trans_v(prev_upper))
                    pdf_document.line_to(trans_t(t), trans_v(upper))
                    pdf_document.line_to(trans_t(t), trans_v(lower))
Пример #3
0
def render_graph_pdf(
    instance,
    graph_artwork,
    graph_data_range,
    graph_render_options,
    pos_left=None,
    pos_top=None,
    total_width=None,
    total_height=None,
):
    pdf_document = instance["document"]

    logger.debug("  Render graph %r",
                 graph_artwork["definition"]["specification"])

    if pos_left is None:  # floating element
        pdf_document.margin(2.5)

    # Styling for PDF graphs. Note: We could make some of these
    # configurable
    font_size = graph_render_options["font_size"]
    mm_per_ex = mm_per_ex_by_render_options(graph_render_options)
    v_label_margin = 1.0  # mm
    t_label_margin = _graph_time_label_margin()
    left_border = _graph_vertical_axis_width(graph_render_options)
    left_margin = _graph_left_margin(graph_render_options)
    top_margin = _graph_top_margin(graph_render_options)
    right_margin = _graph_right_margin(graph_render_options)
    bottom_margin = _graph_bottom_margin(graph_render_options)
    axis_color = parse_color(graph_render_options["foreground_color"])
    zero_rule_color = parse_color(graph_render_options["foreground_color"])
    canvas_color = parse_color(graph_render_options["canvas_color"])
    background_color = parse_color(graph_render_options["background_color"])
    foreground_color = parse_color(graph_render_options["foreground_color"])
    axis_over_width = _graph_axis_over_width(graph_render_options)
    color_gradient = graph_render_options["color_gradient"] / 100.0
    curve_line_width = 0.1  # mm
    rule_line_width = 0.1  # mm
    label_line_width = 0.04  # mm
    v_line_color = tuple(
        map(parse_color,
            [graph_render_options["foreground_color"], "#a0a0a0", "#a0a0a0"]))
    v_line_dash = [None, [0.2, 0.4], None]
    t_line_color = tuple(
        map(parse_color,
            [graph_render_options["foreground_color"], "#a0a0a0", "#666666"]))
    t_line_dash = [None, [0.2, 0.2], None]
    legend_box_line_width = 0.1

    pdf_document.save_state()
    pdf_document.set_font_size(font_size)

    legend_box_size = mm_per_ex

    title_height = graph_title_height(graph_render_options)
    legend_height = graph_legend_height(graph_artwork, graph_render_options)

    if pos_left is not None:
        # Absolute placement of graph
        height = total_height - title_height - legend_height
        width = total_width

    else:
        # Place graph in page flow
        width_ex, height_ex = graph_render_options["size"]
        width = width_ex * mm_per_ex
        height = height_ex * mm_per_ex

    left, top, width, total_height = pdf_document.add_canvas(
        width,
        height + title_height + legend_height,
        border_width=graph_render_options["border_width"],
        left_mm=pos_left,
    )

    # From here width, height, total_height, left and top are in "mm".

    right = left + width - right_margin
    total_bottom = top - total_height
    bottom = top - height - title_height

    # Fill canvas with background color
    pdf_document.render_rect(left,
                             total_bottom,
                             width,
                             total_height,
                             fill_color=background_color)

    # Regular title (above graph area)
    if graph_render_options["show_title"] is True:
        pdf_document.render_aligned_text(
            left + right_margin,
            top - title_height,
            width,
            title_height,
            graph_artwork["title"],
            align="left",
            bold=True,
            color=foreground_color,
        )

    # The following code is inspired by htdocs/js/graphs.js:render_graph(). Whenever
    # you change something there, the change should also be reflected here!

    bottom_border = _graph_bottom_border(graph_render_options)

    # Prepare position and translation of origin
    t_range_from = graph_artwork["time_axis"]["range"][0]
    t_range_to = graph_artwork["time_axis"]["range"][1]
    t_range = t_range_to - t_range_from
    t_mm = width - left_border - left_margin - right_margin
    t_mm_per_second = 1.0 * t_mm / t_range

    v_range_from = graph_artwork["vertical_axis"]["range"][0]
    v_range_to = graph_artwork["vertical_axis"]["range"][1]
    v_range = v_range_to - v_range_from
    v_mm = height - top_margin - bottom_border - bottom_margin
    v_mm_per_unit = 1.0 * v_mm / v_range

    t_orig = left + left_border + left_margin
    v_orig = bottom + bottom_border + bottom_margin
    v_axis_orig = v_range_from

    # paint graph background
    pdf_document.render_rect(t_orig,
                             v_orig,
                             t_mm,
                             v_mm,
                             fill_color=canvas_color)

    # Now transform the whole chooridate system to our real t and v choords
    # so if we paint something at (0, 0) it will correctly represent a
    # value of 0 and a time point of time_start.
    trans_t = lambda t: (t - t_range_from) * t_mm_per_second + t_orig
    trans_v = lambda v: v_orig + ((v - v_axis_orig) * v_mm_per_unit)
    trans = lambda t, v: (trans_t(t), trans_v(v))

    # Paint curves
    pdf_document.save_state()
    pdf_document.add_clip_rect(t_orig, v_orig, t_mm, v_mm)
    step = graph_artwork["step"] / 2.0
    for curve in graph_artwork["curves"]:
        if curve.get("dont_paint"):
            continue

        t = graph_artwork["start_time"]
        points = curve["points"]
        color = parse_color(curve["color"])

        if curve["type"] == "area":
            prev_lower = None
            prev_upper = None

            gradient = (
                t_orig,
                v_orig,
                t_orig,
                v_orig + v_mm,
                (darken_color(color, color_gradient), color,
                 lighten_color(color, color_gradient)),
                (0.0, 0.5, 1.0),
            )

            for lower, upper in points:
                if (lower is not None and upper is not None
                        and prev_lower is not None and prev_upper is not None):
                    pdf_document.begin_path()
                    pdf_document.move_to(
                        trans_t(t - step) - 0.01, trans_v(prev_lower))
                    pdf_document.line_to(
                        trans_t(t - step) - 0.01, trans_v(prev_upper))
                    pdf_document.line_to(trans_t(t), trans_v(upper))
                    pdf_document.line_to(trans_t(t), trans_v(lower))
                    pdf_document.line_to(
                        trans_t(t - step) - 0.01, trans_v(prev_lower))
                    pdf_document.close_path()
                    pdf_document.fill_path(color, gradient=gradient)

                prev_lower = lower
                prev_upper = upper
                t += step

        else:  # "line"
            last_value = None
            pdf_document.begin_path()
            for value in points:
                if value is not None:
                    p = trans(t, value)
                    if last_value is not None:
                        pdf_document.line_to(p[0], p[1])
                    else:
                        pdf_document.move_to(p[0], p[1])
                last_value = value
                t += step
            pdf_document.stroke_path(color=color, width=curve_line_width)

    pdf_document.restore_state()  # Remove clipping

    # Now we use these four dimensions for drawing into the canvas using render_...
    # functions from pdf. Note: top > bottom.

    # Clear areas where values have been painted out of range. This is
    # At top and bottom
    pdf_document.render_rect(t_orig,
                             v_orig + v_mm,
                             t_mm,
                             top_margin,
                             fill_color=background_color)
    pdf_document.render_rect(t_orig,
                             bottom,
                             t_mm,
                             v_orig - bottom,
                             fill_color=background_color)

    # Paint axes and a strong line at 0, if that is in the range
    pdf_document.render_line(t_orig,
                             v_orig - axis_over_width,
                             t_orig,
                             v_orig + v_mm,
                             color=axis_color)
    pdf_document.render_line(t_orig - axis_over_width,
                             v_orig,
                             right,
                             v_orig,
                             color=axis_color)
    if v_range_from <= 0 <= v_range_to:
        pdf_document.render_line(t_orig,
                                 trans_v(0),
                                 right,
                                 trans_v(0),
                                 color=zero_rule_color)

    # Show the inline title
    if graph_render_options["show_title"] == "inline":
        title_top = top - (mm_per_ex_by_render_options(graph_render_options) *
                           2)
        pdf_document.render_aligned_text(
            left,
            title_top,
            width,
            mm_per_ex_by_render_options(graph_render_options) * 2,
            graph_artwork["title"],
            align="center",
            bold=True,
            color=foreground_color,
        )

    if graph_render_options["show_graph_time"]:
        title_top = top - (mm_per_ex_by_render_options(graph_render_options) *
                           2)
        pdf_document.render_aligned_text(
            left - right_margin,
            title_top,
            width,
            mm_per_ex_by_render_options(graph_render_options) * 2,
            graph_artwork["time_axis"]["title"],
            align="right",
            bold=True,
            color=foreground_color,
        )

    # Paint the vertical axis
    if graph_render_options["show_vertical_axis"]:
        # Render optional vertical axis label
        vertical_axis_label = graph_artwork["vertical_axis"]["axis_label"]
        if vertical_axis_label:
            pdf_document.render_aligned_text(
                left + left_margin,
                top - title_height,
                left_border,
                title_height,
                vertical_axis_label,
                align="center",
                valign="middle",
                color=foreground_color,
            )

    for position, label, line_width in graph_artwork["vertical_axis"][
            "labels"]:
        if line_width > 0:
            pdf_document.render_line(
                t_orig,
                trans_v(position),
                right,
                trans_v(position),
                width=label_line_width,
                color=v_line_color[line_width],
                dashes=v_line_dash[line_width],
            )

        if graph_render_options["show_vertical_axis"] and label:
            pdf_document.render_aligned_text(
                t_orig - v_label_margin - left_border,
                trans_v(position),
                left_border,
                mm_per_ex,
                label,
                align="right",
                valign="middle",
                color=foreground_color,
            )

    # Paint time axis
    for position, label, line_width in graph_artwork["time_axis"]["labels"]:
        t_pos_mm = trans_t(position)
        if line_width > 0 and t_pos_mm > t_orig:
            pdf_document.render_line(
                t_pos_mm,
                v_orig,
                t_pos_mm,
                trans_v(v_range_to),
                width=label_line_width,
                color=t_line_color[line_width],
                dashes=t_line_dash[line_width],
            )

        if graph_render_options["show_time_axis"] and label:
            pdf_document.render_aligned_text(
                t_pos_mm,
                v_orig - t_label_margin - mm_per_ex,
                0,
                mm_per_ex,
                label,
                align="center",
                color=foreground_color,
            )

    # Paint horizontal rules like warn and crit
    rules = graph_artwork["horizontal_rules"]
    for position, label, color_from_rule, title in rules:
        if v_range_from <= position <= v_range_to:
            pdf_document.render_line(
                t_orig,
                trans_v(position),
                right,
                trans_v(position),
                width=rule_line_width,
                color=parse_color(color_from_rule),
            )

    # Paint legend
    if graph_render_options["show_legend"]:
        legend_lineskip = get_graph_legend_lineskip(graph_render_options)
        legend_top_margin = _graph_legend_top_margin()
        legend_top = bottom - legend_top_margin + bottom_margin
        legend_column_width = (width - left_margin - left_border -
                               right_margin) / 7.0

        def paint_legend_line(color, texts):
            l = t_orig
            if color:
                pdf_document.render_rect(
                    l,
                    legend_top + mm_per_ex * 0.2,
                    legend_box_size,
                    legend_box_size,
                    fill_color=color,
                    line_width=legend_box_line_width,
                )
            for nr, text in enumerate(texts):
                if text:
                    pdf_document.render_aligned_text(
                        l + (color and nr == 0 and legend_box_size + 0.8 or 0),
                        legend_top,
                        legend_column_width,
                        legend_lineskip,
                        text,
                        align=nr == 0 and "left" or "right",
                        color=foreground_color,
                    )
                if nr == 0:
                    l += legend_column_width * 3
                else:
                    l += legend_column_width

        scalars = [
            ("min", _("Minimum")),
            ("max", _("Maximum")),
            ("average", _("Average")),
            ("last", _("Last")),
        ]
        scalars_legend_line: List[Optional[str]] = [None]

        paint_legend_line(None, scalars_legend_line + [x[1] for x in scalars])
        pdf_document.render_line(t_orig, legend_top, t_orig + t_mm, legend_top)

        for curve in graph_artwork["curves"]:
            legend_top -= legend_lineskip
            texts = [str(curve["title"])]
            for scalar, title in scalars:
                texts.append(curve["scalars"][scalar][1])
            paint_legend_line(parse_color(curve["color"]), texts)

        if graph_artwork["horizontal_rules"]:
            pdf_document.render_line(t_orig, legend_top, t_orig + t_mm,
                                     legend_top)
            for value, readable, color_from_artwork, title in graph_artwork[
                    "horizontal_rules"]:
                legend_top -= legend_lineskip
                paint_legend_line(parse_color(color_from_artwork),
                                  [title] + [None] * 3 + [readable])

    if graph_artwork["definition"].get("is_forecast"):
        pin = trans_t(graph_artwork["requested_end_time"])
        pdf_document.render_line(pin,
                                 v_orig,
                                 pin,
                                 trans_v(v_range_to),
                                 color=(0.0, 1.0, 0.0))

    pdf_document.restore_state()
    if left is None:  # floating element
        pdf_document.margin(2.5)

    logger.debug("  Finished rendering graph")
Пример #4
0
            continue

        t = graph_artwork["start_time"]
        points = curve["points"]
        color = parse_color(curve["color"])

        if curve["type"] == "area":
            prev_lower = None
            prev_upper = None

            gradient = (
                t_orig,
                v_orig,
                t_orig,
                v_orig + v_mm,
                (darken_color(color, color_gradient), color,
                 lighten_color(color, color_gradient)),
                (0.0, 0.5, 1.0),
            )

            for lower, upper in points:
                if (lower is not None and upper is not None
                        and prev_lower is not None and prev_upper is not None):
                    pdf_document.begin_path()
                    pdf_document.move_to(
                        trans_t(t - step) - 0.01, trans_v(prev_lower))
                    pdf_document.line_to(
                        trans_t(t - step) - 0.01, trans_v(prev_upper))
                    pdf_document.line_to(trans_t(t), trans_v(upper))
                    pdf_document.line_to(trans_t(t), trans_v(lower))
                    pdf_document.line_to(