From: Sebastian Wagner Date: Fri, 16 Jul 2021 14:20:32 +0000 (+0200) Subject: mgr/cephadm: Add GrafanaSpec.initial_admin_password X-Git-Tag: v17.1.0~356^2~1 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=fdae665a2fe645d3f76b212f440ffe2c96281bc0;p=ceph.git mgr/cephadm: Add GrafanaSpec.initial_admin_password By default, we're not creating any admin accout for Grafana now, but we're adding an option to set the grafana password manually using: ```yaml service_type: grafana spec: initial_admin_password: mypassword ``` Users can then easily log into Grafana with the given password. Fixes: https://tracker.ceph.com/issues/48291 Signed-off-by: Sebastian Wagner --- diff --git a/doc/cephadm/services/monitoring.rst b/doc/cephadm/services/monitoring.rst index 91b8742f3cfb9..5cb1537dbb4c3 100644 --- a/doc/cephadm/services/monitoring.rst +++ b/doc/cephadm/services/monitoring.rst @@ -161,6 +161,8 @@ For example, if you had changed the prometheus image ceph config rm mgr mgr/cephadm/container_image_prometheus +.. _cephadm-overwrite-jinja2-templates: + Using custom configuration files ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -180,7 +182,7 @@ preserved and automatically applied on future deployments of these services. The configuration of the custom template is also preserved when the default configuration of cephadm changes. If the updated configuration is to be used, - the custom template needs to be migrated *manually*. + the custom template needs to be migrated *manually* after each upgrade of Ceph. Option names """""""""""" @@ -338,6 +340,30 @@ update its configuration: The ``reconfig`` command also sets the proper URL for Ceph Dashboard. +Setting the initial admin password +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, Grafana will not create an initial +admin user. In order to create the admin user, please create a file +``grafana.yaml`` with this content: + +.. code-block:: yaml + + service_type: grafana + spec: + initial_admin_password: mypassword + +Then apply this specification: + +.. code-block:: bash + + ceph orch apply -i grafana.yaml + ceph orch redeploy grafana + +Grafana will now create an admin user called ``admin`` with the +given password. + + Setting up Alertmanager ----------------------- diff --git a/src/pybind/mgr/cephadm/services/monitoring.py b/src/pybind/mgr/cephadm/services/monitoring.py index 3ec2a66789e39..627673d4dd65b 100644 --- a/src/pybind/mgr/cephadm/services/monitoring.py +++ b/src/pybind/mgr/cephadm/services/monitoring.py @@ -7,7 +7,7 @@ from urllib.parse import urlparse from mgr_module import HandleCommandResult from orchestrator import DaemonDescription -from ceph.deployment.service_spec import AlertManagerSpec, ServiceSpec +from ceph.deployment.service_spec import AlertManagerSpec, GrafanaSpec, ServiceSpec from cephadm.services.cephadmservice import CephadmService, CephadmDaemonDeploySpec from cephadm.services.ingress import IngressSpec from mgr_util import verify_tls, ServerConfigException, create_self_signed_cert, build_url @@ -57,8 +57,11 @@ class GrafanaService(CephadmService): 'value': 'false', }) + spec: GrafanaSpec = cast( + GrafanaSpec, self.mgr.spec_store.active_specs[daemon_spec.service_name]) grafana_ini = self.mgr.template.render( 'services/grafana/grafana.ini.j2', { + 'initial_admin_password': spec.initial_admin_password, 'http_port': daemon_spec.ports[0] if daemon_spec.ports else self.DEFAULT_SERVICE_PORT, 'http_addr': daemon_spec.ip if daemon_spec.ip else '' }) diff --git a/src/pybind/mgr/cephadm/templates/services/grafana/grafana.ini.j2 b/src/pybind/mgr/cephadm/templates/services/grafana/grafana.ini.j2 index 3d6303f4466c0..642d0647f3fcd 100644 --- a/src/pybind/mgr/cephadm/templates/services/grafana/grafana.ini.j2 +++ b/src/pybind/mgr/cephadm/templates/services/grafana/grafana.ini.j2 @@ -13,6 +13,10 @@ http_port = {{ http_port }} http_addr = {{ http_addr }} [security] +{% if not initial_admin_password %} + disable_initial_admin_creation = true +{% else %} admin_user = admin - admin_password = admin + admin_password = {{ initial_admin_password }} +{% endif %} allow_embedding = true diff --git a/src/pybind/mgr/cephadm/tests/test_services.py b/src/pybind/mgr/cephadm/tests/test_services.py index 09a5b2b411c63..b8e6f784ef454 100644 --- a/src/pybind/mgr/cephadm/tests/test_services.py +++ b/src/pybind/mgr/cephadm/tests/test_services.py @@ -4,7 +4,7 @@ import yaml import pytest -from unittest.mock import MagicMock, call, patch +from unittest.mock import MagicMock, call, patch, ANY from cephadm.serve import CephadmServe from cephadm.services.cephadmservice import MonService, MgrService, MdsService, RgwService, \ @@ -16,7 +16,7 @@ from cephadm.services.monitoring import GrafanaService, AlertmanagerService, Pro NodeExporterService from cephadm.module import CephadmOrchestrator from ceph.deployment.service_spec import IscsiServiceSpec, MonitoringSpec, AlertManagerSpec, \ - ServiceSpec, RGWSpec + ServiceSpec, RGWSpec, GrafanaSpec from cephadm.tests.fixtures import with_host, with_service, _run_cephadm, async_side_effect from orchestrator import OrchestratorError @@ -332,7 +332,7 @@ class TestMonitoring: cephadm_module.set_store('grafana_crt', 'c') cephadm_module.set_store('grafana_key', 'k') with with_service(cephadm_module, MonitoringSpec('prometheus')) as _, \ - with_service(cephadm_module, MonitoringSpec('grafana')) as _: + with_service(cephadm_module, GrafanaSpec('grafana')) as _: files = { 'grafana.ini': dedent(""" # This file is generated by cephadm. @@ -350,8 +350,7 @@ class TestMonitoring: http_port = 3000 http_addr = [security] - admin_user = admin - admin_password = admin + disable_initial_admin_creation = true allow_embedding = true""").lstrip(), # noqa: W291 'provisioning/datasources/ceph-dashboard.yml': dedent(""" # This file is generated by cephadm. @@ -389,6 +388,47 @@ class TestMonitoring: stdin=json.dumps({"files": files}), image='') + @patch("cephadm.serve.CephadmServe._run_cephadm", _run_cephadm('{}')) + def test_grafana_initial_admin_pw(self, cephadm_module: CephadmOrchestrator): + with with_host(cephadm_module, 'test'): + with with_service(cephadm_module, GrafanaSpec(initial_admin_password='secure')): + out = cephadm_module.cephadm_services['grafana'].generate_config( + CephadmDaemonDeploySpec('test', 'daemon', 'grafana')) + assert out == ( + { + 'files': + { + 'certs/cert_file': ANY, + 'certs/cert_key': ANY, + 'grafana.ini': + '# This file is generated by cephadm.\n' + '[users]\n' + ' default_theme = light\n' + '[auth.anonymous]\n' + ' enabled = true\n' + " org_name = 'Main Org.'\n" + " org_role = 'Viewer'\n" + '[server]\n' + " domain = 'bootstrap.storage.lab'\n" + ' protocol = https\n' + ' cert_file = /etc/grafana/certs/cert_file\n' + ' cert_key = /etc/grafana/certs/cert_key\n' + ' http_port = 3000\n' + ' http_addr = \n' + '[security]\n' + ' admin_user = admin\n' + ' admin_password = secure\n' + ' allow_embedding = true', + 'provisioning/datasources/ceph-dashboard.yml': + '# This file is generated by cephadm.\n' + 'deleteDatasources:\n' + '\n' + 'datasources:\n' + } + }, + [], + ) + @patch("cephadm.serve.CephadmServe._run_cephadm") def test_monitoring_ports(self, _run_cephadm, cephadm_module: CephadmOrchestrator): _run_cephadm.side_effect = async_side_effect(('{}', '', 0)) diff --git a/src/python-common/ceph/deployment/service_spec.py b/src/python-common/ceph/deployment/service_spec.py index 37776bb8a9617..48d511fc3dcf8 100644 --- a/src/python-common/ceph/deployment/service_spec.py +++ b/src/python-common/ceph/deployment/service_spec.py @@ -432,7 +432,7 @@ class ServiceSpec(object): 'alertmanager': AlertManagerSpec, 'ingress': IngressSpec, 'container': CustomContainerSpec, - 'grafana': MonitoringSpec, + 'grafana': GrafanaSpec, 'node-exporter': MonitoringSpec, 'prometheus': MonitoringSpec, }.get(service_type, cls) @@ -1093,3 +1093,27 @@ class AlertManagerSpec(MonitoringSpec): yaml.add_representer(AlertManagerSpec, ServiceSpec.yaml_representer) + + +class GrafanaSpec(MonitoringSpec): + def __init__(self, + service_type: str = 'grafana', + service_id: Optional[str] = None, + placement: Optional[PlacementSpec] = None, + unmanaged: bool = False, + preview_only: bool = False, + config: Optional[Dict[str, str]] = None, + networks: Optional[List[str]] = None, + port: Optional[int] = None, + initial_admin_password: Optional[str] = None + ): + assert service_type == 'grafana' + super(GrafanaSpec, self).__init__( + 'grafana', service_id=service_id, + placement=placement, unmanaged=unmanaged, + preview_only=preview_only, config=config, networks=networks, port=port) + + self.initial_admin_password = initial_admin_password + + +yaml.add_representer(GrafanaSpec, ServiceSpec.yaml_representer) diff --git a/src/python-common/ceph/tests/test_service_spec.py b/src/python-common/ceph/tests/test_service_spec.py index b0541d653ef6e..e2e30944cebbf 100644 --- a/src/python-common/ceph/tests/test_service_spec.py +++ b/src/python-common/ceph/tests/test_service_spec.py @@ -249,6 +249,12 @@ service_name: grafana spec: port: 1234 --- +service_type: grafana +service_name: grafana +spec: + initial_admin_password: secure + port: 1234 +--- service_type: ingress service_id: rgw.foo service_name: ingress.rgw.foo