ceph config rm mgr mgr/cephadm/container_image_prometheus
+.. _cephadm-overwrite-jinja2-templates:
+
Using custom configuration files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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
""""""""""""
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
-----------------------
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
'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 ''
})
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
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, \
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
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.
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.
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))
'alertmanager': AlertManagerSpec,
'ingress': IngressSpec,
'container': CustomContainerSpec,
- 'grafana': MonitoringSpec,
+ 'grafana': GrafanaSpec,
'node-exporter': MonitoringSpec,
'prometheus': MonitoringSpec,
}.get(service_type, cls)
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)
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