From: Volker Theile Date: Thu, 30 Jul 2020 12:24:34 +0000 (+0200) Subject: mgr/cephadm: Enhance AlertManagerSpec to allow adding additional webhook receiver... X-Git-Tag: v16.1.0~1516^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=4b87eeb97861929130c4aa7e2c99e29d2a1652ac;p=ceph.git mgr/cephadm: Enhance AlertManagerSpec to allow adding additional webhook receiver URLs Fixes: https://tracker.ceph.com/issues/46775 Signed-off-by: Volker Theile --- diff --git a/src/pybind/mgr/cephadm/services/monitoring.py b/src/pybind/mgr/cephadm/services/monitoring.py index f910a6444693..cc4d1416b65a 100644 --- a/src/pybind/mgr/cephadm/services/monitoring.py +++ b/src/pybind/mgr/cephadm/services/monitoring.py @@ -3,11 +3,13 @@ import os from typing import List, Any, Tuple, Dict from orchestrator import DaemonDescription +from ceph.deployment.service_spec import AlertManagerSpec from cephadm.services.cephadmservice import CephadmService, CephadmDaemonSpec from mgr_util import verify_tls, ServerConfigException, create_self_signed_cert logger = logging.getLogger(__name__) + class GrafanaService(CephadmService): TYPE = 'grafana' DEFAULT_SERVICE_PORT = 3000 @@ -73,20 +75,29 @@ class GrafanaService(CephadmService): service_url ) + class AlertmanagerService(CephadmService): TYPE = 'alertmanager' DEFAULT_SERVICE_PORT = 9093 - def create(self, daemon_spec: CephadmDaemonSpec) -> str: + def create(self, daemon_spec: CephadmDaemonSpec[AlertManagerSpec]) -> str: assert self.TYPE == daemon_spec.daemon_type + assert daemon_spec.spec return self.mgr._create_daemon(daemon_spec) - def generate_config(self, daemon_spec: CephadmDaemonSpec) -> Tuple[Dict[str, Any], List[str]]: + def generate_config(self, daemon_spec: CephadmDaemonSpec[AlertManagerSpec]) -> Tuple[Dict[str, Any], List[str]]: assert self.TYPE == daemon_spec.daemon_type - deps = [] # type: List[str] + deps: List[str] = [] + default_webhook_urls: List[str] = [] + + if daemon_spec.spec: + user_data = daemon_spec.spec.user_data + if 'default_webhook_urls' in user_data and isinstance( + user_data['default_webhook_urls'], list): + default_webhook_urls.extend(user_data['default_webhook_urls']) # dashboard(s) - dashboard_urls = [] + dashboard_urls: List[str] = [] mgr_map = self.mgr.get('mgr_map') port = None proto = None # http: or https: @@ -110,7 +121,8 @@ class AlertmanagerService(CephadmService): port)) context = { - 'dashboard_urls': dashboard_urls + 'dashboard_urls': dashboard_urls, + 'default_webhook_urls': default_webhook_urls } yml = self.mgr.template.render('services/alertmanager/alertmanager.yml.j2', context) @@ -232,6 +244,7 @@ class PrometheusService(CephadmService): service_url ) + class NodeExporterService(CephadmService): TYPE = 'node-exporter' diff --git a/src/pybind/mgr/cephadm/templates/services/alertmanager/alertmanager.yml.j2 b/src/pybind/mgr/cephadm/templates/services/alertmanager/alertmanager.yml.j2 index 69d5e73bbecc..03c1479721b8 100644 --- a/src/pybind/mgr/cephadm/templates/services/alertmanager/alertmanager.yml.j2 +++ b/src/pybind/mgr/cephadm/templates/services/alertmanager/alertmanager.yml.j2 @@ -5,12 +5,20 @@ global: resolve_timeout: 5m route: - group_by: ['alertname'] - group_wait: 10s - group_interval: 10s - repeat_interval: 1h - receiver: 'ceph-dashboard' + receiver: 'default' + routes: + - group_by: ['alertname'] + group_wait: 10s + group_interval: 10s + repeat_interval: 1h + receiver: 'ceph-dashboard' + receivers: +- name: 'default' + webhook_configs: +{% for url in default_webhook_urls %} + - url: '{{ url }}' +{% endfor %} - name: 'ceph-dashboard' webhook_configs: {% for url in dashboard_urls %} diff --git a/src/pybind/mgr/cephadm/tests/test_spec.py b/src/pybind/mgr/cephadm/tests/test_spec.py index 338759cf11ce..41b33ac9f185 100644 --- a/src/pybind/mgr/cephadm/tests/test_spec.py +++ b/src/pybind/mgr/cephadm/tests/test_spec.py @@ -3,7 +3,7 @@ import json import pytest from ceph.deployment.service_spec import ServiceSpec, NFSServiceSpec, RGWSpec, \ - ServiceSpecValidationError, IscsiServiceSpec, PlacementSpec + IscsiServiceSpec, AlertManagerSpec from orchestrator import DaemonDescription, OrchestratorError @@ -517,3 +517,15 @@ def test_daemon_description_service_name(spec: ServiceSpec, with pytest.raises(OrchestratorError): dd.service_name() + +def test_alertmanager_spec_1(): + spec = AlertManagerSpec() + assert spec.service_type == 'alertmanager' + assert isinstance(spec.user_data, dict) + assert len(spec.user_data.keys()) == 0 + + +def test_alertmanager_spec_2(): + spec = AlertManagerSpec(user_data={'default_webhook_urls': ['foo']}) + assert isinstance(spec.user_data, dict) + assert 'default_webhook_urls' in spec.user_data.keys() diff --git a/src/python-common/ceph/deployment/service_spec.py b/src/python-common/ceph/deployment/service_spec.py index efbc512106d6..a8251d1bfc8f 100644 --- a/src/python-common/ceph/deployment/service_spec.py +++ b/src/python-common/ceph/deployment/service_spec.py @@ -386,6 +386,7 @@ class ServiceSpec(object): 'nfs': NFSServiceSpec, 'osd': DriveGroupSpec, 'iscsi': IscsiServiceSpec, + 'alertmanager': AlertManagerSpec }.get(service_type, cls) if ret == ServiceSpec and not service_type: raise ServiceSpecValidationError('Spec needs a "service_type" key.') @@ -737,3 +738,38 @@ class IscsiServiceSpec(ServiceSpec): yaml.add_representer(IscsiServiceSpec, ServiceSpec.yaml_representer) + + +class AlertManagerSpec(ServiceSpec): + def __init__(self, + service_type: str = 'alertmanager', + service_id: Optional[str] = None, + placement: Optional[PlacementSpec] = None, + unmanaged: bool = False, + preview_only: bool = False, + user_data: Optional[Dict[str, Any]] = None, + ): + assert service_type == 'alertmanager' + super(AlertManagerSpec, self).__init__( + 'alertmanager', service_id=service_id, + placement=placement, unmanaged=unmanaged, + preview_only=preview_only) + + # Custom configuration. + # + # Example: + # service_type: alertmanager + # service_id: xyz + # user_data: + # default_webhook_urls: + # - "https://foo" + # - "https://bar" + # + # Documentation: + # default_webhook_urls - A list of additional URL's that are + # added to the default receivers' + # configuration. + self.user_data = user_data or {} + + +yaml.add_representer(AlertManagerSpec, ServiceSpec.yaml_representer)