Custom Notes for Host and Services

CMK version: 2.4.0p11
OS version: v1.7.12

Error message: None

Hello,

I was looking for a way of creating custom notes/comments for host or services that may be needed, like for a host “Number of the it support, phyisical address of the host itself…”

From this KB seems like the only way is by configuring it in ssh on every single remote site…

This is not doable in our distributed environment, plus it is really cumbersome and overly complicated just for this small add on…

Is there really no way to add notes by GUI??

1 Like

You can add using custom fields:

2 Likes

it is a way …

for custom Service notes and for custom host notes:

image

image

OMD[test]:~$ cat "/opt/omd/sites/test/etc/check_mk/notes/services/APX-01/XIQ SSID Staging"

test by Bernd

explanation and howto is following

Komponente File Function
Part 1 views/paint_custom_notes.py Monkey-Patch, show Notes + Edit-Button
Part 2 pages/custom_notes_editor.py Editor-Page: read, show, save, redirect
View (Part 1)                      Editor (Part 2)
──────────────────                 ──────────────────────────
_my_paint_custom_notes()           CustomNotesEditorPage.page()
  → reads notes files                → reads/writes notes files
  → renders edit button   ────→      → renders textarea
                                     → saves on POST
                                     → redirects back to view

You have to work with a is a Check_MK GUI patch (monkey-patch) that replaces the internal function _paint_custom_notes in the painter module at runtime with a custom implementation.

But be careful

Detailed Walkthrough

1. Imports

from cmk.gui.painter.v0 import painters
from cmk.gui.painter.v0.painters import match_path_entries_with_item

Two important imports from the CMK painter system. match_path_entries_with_item is an internal function that matches files in a directory against a host or service name. At the end of the script, painters._paint_custom_notes is overwritten directly – that is the actual monkey-patch.


2. _get_back_url_data(row)

url = request.url
view_name_match = re.findall(r"view_name=\w+", url)
view_name = (
    str(view_name_match[0]).replace("view_name=", "")
    if view_name_match
    else "allhosts"
)

Reads the current URL from the HTTP request and extracts the view_name parameter via regex (e.g. allhosts, host, service). This is later used as the return URL for the editor, so that after saving the user is taken back to the correct view. Fallback is "allhosts".

Returns: Dictionary containing site and view_name.


3. _render_edit_icon_button(data)

url = makeuri_contextless(request,
                          filename="custom_notes_editor.py",
                          vars_=[("type", ...), ("item", ...), ...])
return html.render_a(
    content=HTML(html.render_icon("edit", cssclass="iconbutton"), escape=False),
    ...
)

Builds a URL to custom_notes_editor.py with all required parameters (type, item, host, site, view name) and renders it as a clickable edit icon link. The icon (edit) is a standard CMK icon. target="main" opens the editor in the main frame of the CMK frameset.

Returns: A fully rendered HTML <a> tag as a CMK HTML object.


4. _format_item_to_valid_file_name(item)

return item.replace("/", "_slash_").replace(":", "_colon_")

Simple sanitisation: slashes and colons in the service name are replaced so the name can be used as a filename on the filesystem. A service like Filesystem /var/log becomes Filesystem _slash_var_slash_log.


5. _my_paint_custom_notes(what, row, config) – Core function

This is the actual replacement for the original CMK function. The flow:

Step 1 – Determine context:

if what == "service":
    notes_dir = default_config_dir + "/notes/services"
    dirs = match_path_entries_with_item([Path(notes_dir)], host)
    item = svc
else:
    dirs = [Path(default_config_dir) / "notes/hosts"]
    item = host

Depending on whether what == "service" or "host", the correct notes directory is selected. For services, match_path_entries_with_item first looks for host-specific subdirectories inside the service notes directory. For hosts it points directly to the notes/hosts directory.

Step 2 – Normalise filename and find files:

item_ = _format_item_to_valid_file_name(item)
files = sorted(match_path_entries_with_item(dirs, item_), reverse=True)

The item name (host or service) is made filesystem-safe, then all matching notes files are found and sorted in descending order (newest / alphabetically last first).

Step 3 – Tag replacement:

def replace_tags(text: str) -> str:
    sitename = row["site"]
    url_prefix = get_site_config(config, sitename)["url_prefix"]
    return (
        text.replace("$URL_PREFIX$", url_prefix)
            .replace("$SITE$", sitename)
            .replace("$HOSTNAME$", host)
            ...
    )

An inner function that replaces placeholders in the notes text with real values. This is the same variable system as in the original CMK Custom Notes – just now extensible. Supported tags:

Tag Replaced with
$URL_PREFIX$ URL prefix of the site
$SITE$ Site name
$HOSTNAME$ Hostname
$HOSTNAME_LOWER$ Hostname in lowercase
$HOSTNAME_UPPER$ Hostname in uppercase
$HOSTNAME_TITLE$ Hostname in title case
$HOSTADDRESS$ IP address
$SERVICEOUTPUT$ Plugin output of the service
$HOSTOUTPUT$ Plugin output of the host
$SERVICEDESC$ Service description

Step 4 – Read and assemble content:

for f in files:
    contents.append(replace_tags(f.read_text(encoding="utf8").strip()))

All matching notes files are read, stripped, and processed through replace_tags.

Step 5 – Return value:

return "", _render_edit_icon_button(data) + "<hr>".join(contents)

Returns a CellSpec tuple: the first element is the CSS class name (empty), the second is the HTML content. The edit button comes first, followed by all notes contents separated by <hr>.


6. The Monkey-Patch

painters._paint_custom_notes = _my_paint_custom_notes

This last line is the actual intervention. CMK loads this script as a GUI extension (most likely from local/share/check_mk/web/plugins/views/) and overwrites the original function in the already-loaded painters module. From this point on, every painter call invokes the new function.

2nd Option
Go to allhosts and export your hosts to CSV Add the notes in the CSV and write a small script that creates the notes files on the server from the CSV

bash

#!/bin/bash

# Configuration
CSV_FILE="hosts.csv"
SITENAME="your_site_name"  # Replace with your actual site name
NOTES_DIR="/opt/omd/sites/${SITENAME}/etc/check_mk/notes/hosts/"

# Create directory if it doesn't exist
mkdir -p "$NOTES_DIR"

# Read CSV file (skip header line)
tail -n +2 "$CSV_FILE" | while IFS=',' read -r hostname notes; do
    # Remove leading/trailing whitespace
    hostname=$(echo "$hostname" | xargs)
    notes=$(echo "$notes" | xargs)
    
    # Skip empty hostnames
    if [ -z "$hostname" ]; then
        continue
    fi
    
    # Create notes file with UTF-8 encoding
    echo -n "$notes" | iconv -f UTF-8 -t UTF-8 > "${NOTES_DIR}${hostname}"
    
    echo "Created notes file for: $hostname"
done

echo "Done! All notes files created in $NOTES_DIR"

@paulosantanabr Thanks but this just applies an attribute to all hosts… and there is no way of configuring a rule to apply it just to a specific folder.

What I need is to be able to interact via GUI to set notes for specific hosts.

@BH2005 Yep thanks for the effort this is doable but it just makes no sense to me that there is no implementation of adding notes via GUI it makes no sense.

BR

You can apply a host custom attribute to a folder and all the child objects will inherit at as any other values.

2 Likes

This is it, sorry I did not understand it at first, I thought it was a comment, it’s not, it is an actual field you can edit for each host.

Thank you so much Paulo!!

Still, I don’t know why in the KB its suggested to go around and use the cli.. oh well

1 Like