Dashboard mit Link auf ein anderes Dashboard

Moin Leute

gibt es die Möglichkeit auf ein Dashboard ein Link oder Bild zu machen, mit dem man auf ein anderes Dashboard kommt?

Ich habe z. B. ein Gesamtübersicht’s Dashboard und dort möchte ich ein Link implementieren wo man auf ein Dashboard kommt wo nur kritische Elemente sind. Also quasi das Dashboard, aber nur die als CRIT eingeordneten Systeme.

Ich habe das mit URL probiert, aber:

  1. Öffnet er das Dashboard dann in dem kleinen Widget - zumindest bei Links Klick
  2. Zeigt er das Dashboard Menü (Dashboard, Add, Dashboards usw.) mit an.

Greetz

Ovrld

Da bin ich auch noch dran und versuche gerade ein Dashlet dafür zu programmieren … eigentlich eher ein universal_link_dashlet als buttons

Allerding ist das unter der React-GUI nicht ganz so trivial…

1 Like

Lass mich gerne wissen, ob du das irgendwie hinbekommen hast :slight_smile:

Greetz

so nun aber

nano ~/local/lib/check_mk/gui/plugins/dashboard/link_dashlet.py
Code Dashlet

`

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Check_MK 2.4 Compatible Link Dashlet

Creates clickable links/buttons to other dashboards, external URLs, or views.

Install to: local/lib/check_mk/gui/plugins/dashboard/link_dashlet.py
(Note the different path for CMK 2.4!)
"""

from cmk.gui.i18n import _
from cmk.gui.type_defs import HTTPVariables
from cmk.gui.htmllib.html import html
from cmk.gui.utils.html import HTML
from cmk.gui.valuespec import (
    CascadingDropdown,
    Dictionary,
    DropdownChoice,
    TextInput,
    TextAreaUnicode,
)

# Menu entry from helper library
import cmk.gui.dashboard.page_show_dashboard
from cmk.plugins.dashlets.other_dashlet_entries import my_dashboard_add_other_dashlet_entries
cmk.gui.dashboard.page_show_dashboard._dashboard_add_other_dashlet_entries = my_dashboard_add_other_dashlet_entries

try:
    # Check_MK 2.4 API
    from cmk.gui.dashboard.dashlet import Dashlet, dashlet_registry
    from cmk.gui.dashboard.type_defs import DashletConfig, DashletId
except ImportError:
    # Fallback for older versions
    from cmk.gui.plugins.dashboard.utils import Dashlet, dashlet_registry


@dashlet_registry.register
class LinkDashlet(Dashlet):
    """Dashlet that displays a clickable link/button"""

    @classmethod
    def type_name(cls):
        return "link_dashlet"

    @classmethod
    def title(cls):
        return _("Link / Button")

    @classmethod
    def description(cls):
        return _("Clickable link or button to dashboard, view, or URL")

    @classmethod
    def sort_index(cls):
        return 10

    @classmethod
    def initial_size(cls):
        return (20, 10)

    @classmethod
    def is_resizable(cls):
        return True

    @classmethod
    def initial_refresh_interval(cls):
        return False  # No refresh needed

    @classmethod
    def vs_parameters(cls):
        return Dictionary(
            title=_("Properties"),
            render="form",
            optional_keys=["description", "icon"],
            elements=[
                (
                    "link_type",
                    CascadingDropdown(
                        title=_("Link Type"),
                        choices=[
                            (
                                "dashboard",
                                _("Check_MK Dashboard"),
                                TextInput(
                                    title=_("Dashboard Name"),
                                    help=_("Name of the dashboard (from URL: dashboard.py?name=XXX)"),
                                    size=40,
                                ),
                            ),
                            (
                                "view",
                                _("Check_MK View"),
                                TextInput(
                                    title=_("View Name"),
                                    help=_("Name of the view (e.g., allhosts, allservices)"),
                                    size=40,
                                ),
                            ),
                            (
                                "url",
                                _("External URL"),
                                Dictionary(
                                    elements=[
                                        (
                                            "url",
                                            TextInput(
                                                title=_("URL"),
                                                help=_("Full URL (must start with http:// or https://)"),
                                                size=60,
                                            ),
                                        ),
                                        (
                                            "open_new",
                                            DropdownChoice(
                                                title=_("Open in"),
                                                choices=[
                                                    (False, _("Same window")),
                                                    (True, _("New window/tab")),
                                                ],
                                                default_value=True,
                                            ),
                                        ),
                                    ],
                                    optional_keys=[],
                                ),
                            ),
                            (
                                "iframe",
                                _("Embedded (iframe)"),
                                TextInput(
                                    title=_("URL to embed"),
                                    help=_("URL to embed in iframe. Note: Not all sites allow embedding."),
                                    size=60,
                                ),
                            ),
                        ],
                        default_value=("dashboard", "main"),
                    ),
                ),
                (
                    "link_title",
                    TextInput(
                        title=_("Link Title"),
                        help=_("Text displayed on the button"),
                        size=40,
                        allow_empty=False,
                        default_value="Link",
                    ),
                ),
                (
                    "link_description",
                    TextAreaUnicode(
                        title=_("Description (optional)"),
                        help=_("Additional text shown below title"),
                        rows=2,
                        cols=40,
                    ),
                ),
                (
                    "link_style",
                    DropdownChoice(
                        title=_("Display Style"),
                        choices=[
                            ("button", _("Large Button")),
                            ("card", _("Card")),
                            ("minimal", _("Minimal")),
                        ],
                        default_value="button",
                    ),
                ),
                (
                    "link_icon",
                    DropdownChoice(
                        title=_("Icon"),
                        choices=[
                            ("", _("No icon")),
                            ("📊", "📊 Dashboard"),
                            ("🖥️", "🖥️ Server"),
                            ("📈", "📈 Graph"),
                            ("🔍", "🔍 Search"),
                            ("⚙️", "⚙️ Settings"),
                            ("🌐", "🌐 Network"),
                            ("📡", "📡 Wireless"),
                            ("🚀", "🚀 Launch"),
                            ("⚡", "⚡ Fast"),
                            ("📋", "📋 List"),
                        ],
                        default_value="📊",
                    ),
                ),
                (
                    "link_color",
                    DropdownChoice(
                        title=_("Color Theme"),
                        choices=[
                            ("blue", _("Blue")),
                            ("green", _("Green")),
                            ("purple", _("Purple")),
                            ("orange", _("Orange")),
                            ("red", _("Red")),
                        ],
                        default_value="blue",
                    ),
                ),
            ],
        )

    def _get_link_data(self):
        """Get URL and target based on configuration"""
        link_config = self._dashlet_spec.get("link_type", ("dashboard", "main"))
        link_type = link_config[0]
        link_value = link_config[1]
        
        if link_type == "dashboard":
            url = f"dashboard.py?name={link_value}"
            target = "_self"
            is_iframe = False
        elif link_type == "view":
            url = f"view.py?view_name={link_value}"
            target = "_self"
            is_iframe = False
        elif link_type == "url":
            url = link_value.get("url", "#")
            target = "_blank" if link_value.get("open_new", True) else "_self"
            is_iframe = False
        elif link_type == "iframe":
            url = link_value
            target = None
            is_iframe = True
        else:
            url = "#"
            target = "_self"
            is_iframe = False
        
        return url, target, is_iframe

    def show(self):
        """Render the dashlet"""
        title = self._dashlet_spec.get("link_title", "Link")
        description = self._dashlet_spec.get("link_description", "")
        style = self._dashlet_spec.get("link_style", "button")
        icon = self._dashlet_spec.get("link_icon", "")
        color = self._dashlet_spec.get("link_color", "blue")
        
        url, target, is_iframe = self._get_link_data()
        
        # Color mappings
        color_map = {
            "blue": ("667eea", "764ba2"),
            "green": ("11998e", "38ef7d"),
            "purple": ("a8edea", "fed6e3"),
            "orange": ("f093fb", "f5576c"),
            "red": ("fa709a", "fee140"),
        }
        
        c1, c2 = color_map.get(color, ("667eea", "764ba2"))
        
# Styles
        html.open_div(style="height: 100%; padding: 0; margin: 0;")

        html.write_html(f"""
        <style>
            .link-dashlet-{self.dashlet_id} {{
                height: 100%;
                display: flex;
                align-items: center;
                justify-content: center;
                padding: 20px;
                box-sizing: border-box;
                font-family: inherit;
            }}

            /* Gemeinsame Basis für den Link */
            .link-dashlet-{self.dashlet_id} a {{
                text-decoration: none;
                color: white;
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: center;
                width: 100%;
                height: 100%;
                border-radius: 12px;
                background: linear-gradient(135deg, #{c1} 0%, #{c2} 100%);
                transition: all 0.3s ease;
                box-sizing: border-box;
                text-align: center;
            }}

            .link-dashlet-{self.dashlet_id} a:hover {{
                transform: translateY(-6px);
                box-shadow: 0 12px 25px rgba(0,0,0,0.25);
            }}

            /* Icon und Textgrößen */
            .link-dashlet-{self.dashlet_id} .icon {{
                font-size: 48px;
                margin-bottom: 12px;
            }}
            .link-dashlet-{self.dashlet_id} .title {{
                font-weight: bold;
                margin-bottom: 6px;
            }}
            .link-dashlet-{self.dashlet_id} .desc {{
                font-size: 13px;
                opacity: 0.9;
                line-height: 1.3;
            }}

            /* STYLE: Large Button */
            .style-button a {{
                padding: 30px;
                font-size: 22px;
                box-shadow: 0 8px 20px rgba(0,0,0,0.2);
            }}
            .style-button .title {{
                font-size: 24px;
            }}
            .style-button .icon {{
                font-size: 60px;
            }}

            /* STYLE: Card */
            .style-card a {{
                padding: 20px;
                box-shadow: 0 6px 15px rgba(0,0,0,0.15);
                border: 1px solid rgba(255,255,255,0.2);
            }}
            .style-card .title {{
                font-size: 20px;
            }}
            .style-card .icon {{
                font-size: 50px;
            }}

            /* STYLE: Minimal */
            .style-minimal a {{
                background: none !important;
                color: #333 !important;
                padding: 10px;
                border-radius: 8px;
            }}
            .style-minimal a:hover {{
                background: rgba(0,0,0,0.05) !important;
                transform: none;
                box-shadow: none;
            }}
            .style-minimal .icon {{
                font-size: 32px;
                margin-bottom: 8px;
            }}
            .style-minimal .title {{
                font-size: 16px;
                color: #333;
            }}
            .style-minimal .desc {{
                font-size: 12px;
                color: #666;
            }}

            /* iFrame immer vollflächig, unabhängig vom Style */
            .link-dashlet-{self.dashlet_id} iframe {{
                width: 100%;
                height: 100%;
                border: none;
                border-radius: 8px;
            }}
        </style>
        """)

        if is_iframe:
            # iFrame immer gleich darstellen
            html.write_html(HTML(f"""
            <div class="link-dashlet-{self.dashlet_id}" style="padding: 0;">
                <iframe src="{html.attrencode(url)}" allowfullscreen></iframe>
            </div>
            """))
        else:
            # Link/Button/Card/Minimal
            style_class = f"style-{style}"

            html.open_div(class_=f"link-dashlet-{self.dashlet_id} {style_class}")
            html.open_a(href=url, target=target if target else None)

            if icon:
                html.div(icon, class_="icon")
            html.div(title, class_="title")
            if description:
                html.div(description, class_="desc")

            html.close_a()
            html.close_div()

        html.close_div()

`

zusätzlich falls der Menüpunkt nicht im Add Menü von Dashboard auftaucht:

nano ~/local/lib/check_mk/plugins/dashlets/other_dashlet_entries.py
Code
#!/usr/bin/env python3

from cmk.gui.dashboard.page_show_dashboard import _dashboard_add_non_view_dashlet_link
from cmk.gui.page_menu import PageMenuEntry, PageMenu
from cmk.gui.dashboard.type_defs import DashboardName
from collections.abc import Iterable

def my_dashboard_add_other_dashlet_entries(name: DashboardName) -> Iterable[PageMenuEntry]:
    yield PageMenuEntry(
        title="Custom URL",
        icon_name="dashlet_url",
        item=_dashboard_add_non_view_dashlet_link(name, "url"),
    )

    yield PageMenuEntry(
        title="Static text",
        icon_name="dashlet_nodata",
        item=_dashboard_add_non_view_dashlet_link(name, "nodata"),
    )

    yield PageMenuEntry(
        title="LinkDashlet",
        icon_name="view_copy",
        item=_dashboard_add_non_view_dashlet_link(name, "link_dashlet"),
    )

Danke auch an @StefanM und @ChristianM

2 Likes

Top!

Ist dann hoffentlich auch offiziell mit den neuen Dashboards möglich - out of the box :wink:

ja wäre toll … mal sehen glaube ja nicht so richtig dran

1 Like

Klappt super - danke!

Ich habe mal die KI in der 2.5 gefragt. Die Antwort ist leider ernüchternd. Die eigenen Dashlets sind alle tot. Komischerweise scheinen sie registriert worden zu sein, aber bei den Widget-Dialogen werden sie nicht angeboten.

Hier die Antwort der KI:

The official Checkmk documentation distinguishes between custom check plugins and custom widgets (dashlets). While fully custom check plugins are implemented in Python using the Plugin API, custom widgets for dashboards are typically created by configuring dashboards and dashlets via the Checkmk UI rather than by writing source code.

There is no direct example or source code snippet in the documentation for writing fully custom visual widgets (dashlets) as standalone code modules. Instead, custom widgets are usually created by:

  • Building custom views with specific filters and visualizations.
  • Adding these views or other dashlets (graphs, tables, gauges, etc.) to dashboards.
  • Using macros and dashboard layout options to customize appearance and behavior.

For advanced or fully custom visual widgets beyond the built-in dashlets, the documentation suggests developing plugins using the Plugin API, but the provided examples and guides focus on check plugins (monitoring logic) rather than UI widgets.

In summary:

  • Custom check plugins are implemented in Python with a defined structure (parsing, discovery, check functions) as shown in the “Writing Simple Check Plugins” example.
  • Custom widgets (dashlets) are created and configured via the Checkmk dashboard UI by adding and customizing dashlets and views.
  • There is no documented source code example for fully custom widget development as a separate code artifact.
  • For any advanced UI extension beyond dashboards and dashlets, you would need to explore Checkmk’s Plugin API and developer resources, but no direct source code example is provided in the official docs.

If you want to create a custom widget, the recommended approach is to create a custom dashboard and add/configure dashlets (views, graphs, tables) through the UI. For fully custom visualizations beyond that, you may need to contact Checkmk support or consult the developer community for guidance.

For more details on dashboards and dashlets, see the Checkmk documentation on dashboards and views.

If you need further help with custom widget development beyond the UI, please contact Checkmk support at Contact the Checkmk team .

This answer is based on the absence of direct source code examples for custom widgets in the official documentation and the focus on check plugins for code-based extensions Check Plugin Structure Guide, Writing Simple Check Plugins.

Schauen wir dann wenn es soweit ist bis dahin haben wir auch hier noch einige offene Ideas zum voten … oder selber machen

Dashboard Buttons, with link to other dashboards, and with status of the other dashboard. - Checkmk

Beautiful dashboards need some colors! Add color option to dashlet title/background and Static text! - Checkmk

Create Pie-Chart as Dashlet - Checkmk

Bubble & Rectangle Dashboard Widgets - Checkmk

Dashboard Separator Bar / Divider / Line / Formatting options etc. - Checkmk

@martin.hirschvogel hast du da mehr Infos wie es für die 2.5 aussehen wird ?

2 Likes

2.5: Erstmal Dashboards nutzbar gemacht, um die Einstiegshürde zu senken. Sowie responsive Dashboards (passen sich der Bildschirmgröße an). Mit der nächsten Version kommt dann hoffentlich gaaanz viel!

@Mathieu here is input for you :slight_smile: enjoy learning German :smiley:

4 Likes

nice !!! … es geht vorran :wink:

Hast du auch evtl. noch Infos zur Inventory API ob das nun komplett auf die neue Struktur umgestellt wird ?

1 Like

Yep, wir haben den ganzen alten Code rausgeworfen in den Dashboards und in unser neues UI Framework migriert… Damit können wir jetzt viel viel leichter solche Sachen wie von dir genannt bauen…

Inventory API: Werk #18600: Preview of upcoming new plug-in APIs

2 Likes

Hallo Martin,

Ja, die 2.5 habe ich schon reichlich untersucht und meine “Befürchtungen” schonmal bei den Vorschlagen abgekippt (https://ideas.checkmk.com/suggestions/688810/dashboards-add-custom-widgets-under-locallibpython3cmk_addons).

Der Vorschlag enthält aber auch schon Ideen zur Lösung. Und mit dem Committer des neuen Konzeptes habe ich mich auch schon in Verbindung gesetzt.

Gruß
Stefan

2 Likes

Hallo @StefanM

Danke hast du dich da schon tief eingefuchst! und frühzeitig den Alarm Button getriggert. Wir hoffen das zumindest der folgende Teil es noch in die 2.5 schafft, wenn gewisse funktionen halt leider immer noch nicht verfügbar sind.

War und ist ja eigentlich eine Stärk von CMK, dass man selber erweitern kann solange es nicht “Core” ist.

2. Make the Vue-stuff (widget flyout-content) customizable and provide a valid path for placing dashlets under “~/local/python3/cmk_addons/plugins/*/dashlets”

Cheers

D

1 Like