]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/cephadm: Add GrafanaSpec.initial_admin_password
authorSebastian Wagner <sewagner@redhat.com>
Fri, 16 Jul 2021 14:20:32 +0000 (16:20 +0200)
committerSebastian Wagner <sewagner@redhat.com>
Fri, 26 Nov 2021 10:20:11 +0000 (11:20 +0100)
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 <sewagner@redhat.com>
doc/cephadm/services/monitoring.rst
src/pybind/mgr/cephadm/services/monitoring.py
src/pybind/mgr/cephadm/templates/services/grafana/grafana.ini.j2
src/pybind/mgr/cephadm/tests/test_services.py
src/python-common/ceph/deployment/service_spec.py
src/python-common/ceph/tests/test_service_spec.py

index 91b8742f3cfb9fa3653424e2bd6f4834ca79bed4..5cb1537dbb4c3d9c453c8ccf8da5cad57a5774d2 100644 (file)
@@ -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
 -----------------------
 
index 3ec2a66789e391421ad1f0dbb792f13003a24f6d..627673d4dd65b1e09f2f798d923aff8a6f913236 100644 (file)
@@ -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 ''
             })
index 3d6303f4466c00acb406eed95386750879f4c0f8..642d0647f3fcd7b1511a51841730f7b87a310001 100644 (file)
   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
index 09a5b2b411c6334a0833295ec6d724141a0eb5a9..b8e6f784ef4540b954b33db4b9e8ee3a8bb8ba88 100644 (file)
@@ -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))
index 37776bb8a961793e47a5e95961050082af760de6..48d511fc3dcf8b1a036ae4aa7978748568c43ee7 100644 (file)
@@ -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)
index b0541d653ef6e56b6d4cfd17a8c38d891fdd818c..e2e30944cebbf230eedbb6fa57480d88938fccce 100644 (file)
@@ -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