From: Aashish Sharma Date: Thu, 2 Apr 2026 13:29:40 +0000 (+0530) Subject: mgr/dashboard: Add option to set motd via api X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=e4c4561ec54fe17e4034d237d33c5959c31732d4;p=ceph.git mgr/dashboard: Add option to set motd via api Signed-off-by: Aashish Sharma --- diff --git a/src/pybind/mgr/dashboard/openapi.yaml b/src/pybind/mgr/dashboard/openapi.yaml index 5f6024ca9de3..49275bc5b51a 100644 --- a/src/pybind/mgr/dashboard/openapi.yaml +++ b/src/pybind/mgr/dashboard/openapi.yaml @@ -11183,6 +11183,92 @@ paths: summary: Get Monitor Details tags: - Monitor + /api/motd: + post: + parameters: [] + requestBody: + content: + application/json: + schema: + properties: + expires: + type: string + message: + type: string + severity: + type: string + required: + - severity + - expires + - message + type: object + responses: + '201': + content: + application/json: + schema: + type: object + application/vnd.ceph.api.v1.0+json: + schema: + type: object + description: Resource created. + '202': + content: + application/json: + schema: + type: object + application/vnd.ceph.api.v1.0+json: + schema: + type: object + description: Operation is still executing. Please check the task queue. + '400': + description: Operation exception. Please check the response body for details. + '401': + description: Unauthenticated access. Please login first. + '403': + description: Unauthorized access. Please check your permissions. + '500': + description: Unexpected error. Please check the response body for the stack + trace. + security: + - jwt: [] + tags: + - Motd + /api/motd/clear: + delete: + parameters: [] + responses: + '202': + content: + application/json: + schema: + type: object + application/vnd.ceph.api.v1.0+json: + schema: + type: object + description: Operation is still executing. Please check the task queue. + '204': + content: + application/json: + schema: + type: object + application/vnd.ceph.api.v1.0+json: + schema: + type: object + description: Resource deleted. + '400': + description: Operation exception. Please check the response body for details. + '401': + description: Unauthenticated access. Please login first. + '403': + description: Unauthorized access. Please check your permissions. + '500': + description: Unexpected error. Please check the response body for the stack + trace. + security: + - jwt: [] + tags: + - Motd /api/multi-cluster/auth: post: parameters: [] @@ -27189,6 +27275,8 @@ tags: name: MonPerfCounter - description: Get Monitor Details name: Monitor +- description: Message of the day API + name: Motd - description: Multi-cluster Management API name: Multi-cluster - description: NFS-Ganesha Cluster Management API diff --git a/src/pybind/mgr/dashboard/plugins/motd.py b/src/pybind/mgr/dashboard/plugins/motd.py index cd673817f672..c148c9bcca75 100644 --- a/src/pybind/mgr/dashboard/plugins/motd.py +++ b/src/pybind/mgr/dashboard/plugins/motd.py @@ -3,11 +3,12 @@ import hashlib import json from enum import Enum -from typing import Dict, NamedTuple, Optional +from typing import Dict, NamedTuple, Optional, Union from ceph.utils import datetime_now, datetime_to_str, parse_timedelta, str_to_datetime from ..cli import DBCLICommand +from ..exceptions import DashboardException from . import PLUGIN_MANAGER as PM from .plugin import SimplePlugin as SP @@ -38,6 +39,35 @@ class Motd(SP): ) ] + def _normalize_severity(self, severity: Union[MotdSeverity, str]) -> Optional[MotdSeverity]: + if isinstance(severity, MotdSeverity): + return severity + try: + return MotdSeverity(severity) + except ValueError: + return None + + def _set_motd(self, severity: Union[MotdSeverity, str], expires: str, message: str): + severity = self._normalize_severity(severity) + if severity is None: + return 1, '', 'Invalid severity, use "info", "warning" or "danger"' + + if expires != '0': + delta = parse_timedelta(expires) + if not delta: + return 1, '', 'Invalid expires format, use "2h", "10d" or "30s"' + expires = datetime_to_str(datetime_now() + delta) + else: + expires = '' + value: str = json.dumps({ + 'message': message, + 'md5': hashlib.md5(message.encode()).hexdigest(), + 'severity': severity.value, + 'expires': expires + }) + self.set_option(self.NAME, value) + return 0, 'Message of the day has been set.', '' + @PM.add_hook def register_commands(self): @DBCLICommand("dashboard {name} get".format(name=self.NAME)) @@ -56,21 +86,7 @@ class Motd(SP): @DBCLICommand("dashboard {name} set".format(name=self.NAME)) def _set(_, severity: MotdSeverity, expires: str, message: str): - if expires != '0': - delta = parse_timedelta(expires) - if not delta: - return 1, '', 'Invalid expires format, use "2h", "10d" or "30s"' - expires = datetime_to_str(datetime_now() + delta) - else: - expires = '' - value: str = json.dumps({ - 'message': message, - 'md5': hashlib.md5(message.encode()).hexdigest(), - 'severity': severity.value, - 'expires': expires - }) - self.set_option(self.NAME, value) - return 0, 'Message of the day has been set.', '' + return self._set_motd(severity, expires, message) @DBCLICommand("dashboard {name} clear".format(name=self.NAME)) def _clear(_): @@ -79,7 +95,7 @@ class Motd(SP): @PM.add_hook def get_controllers(self): - from ..controllers import RESTController, UIRouter + from ..controllers import APIDoc, APIRouter, Endpoint, RESTController, UIRouter @UIRouter('/motd') class MessageOfTheDay(RESTController): @@ -95,4 +111,21 @@ class Motd(SP): return None return data._asdict() - return [MessageOfTheDay] + @APIRouter('/motd') + @APIDoc('Message of the day API', "Motd") + class MessageOfTheDayApi(RESTController): + def create(_, severity: MotdSeverity, expires: str, message: str): # pylint: disable=no-self-argument,line-too-long # noqa: E501 + # pylint: disable=W0212 + _, _, stderr = self._set_motd(severity, expires, message) + if stderr: + raise DashboardException( + code='invalid_motd', + msg="Invalid MOTD input", + component='dashboard' + ) + + @Endpoint('DELETE') + def clear(_): # pylint: disable=no-self-argument + self.set_option(self.NAME, '') + + return [MessageOfTheDay, MessageOfTheDayApi]