]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/cephadm: add template engine Jinja2
authorKiefer Chang <kiefer.chang@suse.com>
Fri, 29 May 2020 09:22:07 +0000 (17:22 +0800)
committerSebastian Wagner <sebastian.wagner@suse.com>
Mon, 8 Jun 2020 11:52:24 +0000 (13:52 +0200)
Signed-off-by: Kiefer Chang <kiefer.chang@suse.com>
(cherry picked from commit 64bb27f2f62010de44d50bc1a876436159770b56)

ceph.spec.in
debian/control
src/pybind/mgr/cephadm/module.py
src/pybind/mgr/cephadm/template.py [new file with mode: 0644]
src/pybind/mgr/cephadm/tests/test_template.py [new file with mode: 0644]
src/pybind/mgr/requirements.txt

index 6c6179578bfd3b7326b41f2a240b1bf9843f0b19..244c45170f1e54d5132f72ec1398093ea5d30c92 100644 (file)
@@ -645,9 +645,11 @@ Requires:       python%{python3_pkgversion}-remoto
 Requires:       cephadm = %{_epoch_prefix}%{version}-%{release}
 %if 0%{?suse_version}
 Requires:       openssh
+Requires:       python%{python3_pkgversion}-Jinja2
 %endif
 %if 0%{?rhel} || 0%{?fedora}
 Requires:       openssh-clients
+Requires:       python%{python3_pkgversion}-jinja2
 %endif
 %description mgr-cephadm
 ceph-mgr-cephadm is a ceph-mgr module for orchestration functions using
index d98b16111a45ea9bee767e27c813efd21f1fef87..9abc2bd752dd076c74b1de08d5b50f7c8ac40117 100644 (file)
@@ -351,7 +351,8 @@ Depends: ceph-mgr (= ${binary:Version}),
         python3-six,
          ${misc:Depends},
          ${python:Depends},
-         openssh-client
+         openssh-client,
+         python3-jinja2
 Description: cephadm orchestrator module for ceph-mgr
  Ceph is a massively scalable, open-source, distributed
  storage system that runs on commodity hardware and delivers object,
index 0f5c825a18a84736cd2d6a2dfe9d1e826fe36a05..de9673aa039320f7540a3117a78b999721349969 100644 (file)
@@ -42,6 +42,7 @@ from .services.monitoring import GrafanaService, AlertmanagerService, Prometheus
 from .schedule import HostAssignment
 from .inventory import Inventory, SpecStore, HostCache
 from .upgrade import CEPH_UPGRADE_ORDER, CephadmUpgrade
+from .template import TemplateMgr
 
 try:
     import remoto
@@ -345,6 +346,8 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule):
             'iscsi': self.iscsi_service,
         }
 
+        self.template = TemplateMgr()
+
     def shutdown(self):
         self.log.debug('shutdown')
         self._worker_pool.close()
diff --git a/src/pybind/mgr/cephadm/template.py b/src/pybind/mgr/cephadm/template.py
new file mode 100644 (file)
index 0000000..f8e2aff
--- /dev/null
@@ -0,0 +1,73 @@
+import copy
+from typing import Optional
+
+from jinja2 import Environment, PackageLoader, select_autoescape, StrictUndefined
+from jinja2 import exceptions as j2_exceptions
+
+
+class TemplateError(Exception):
+    pass
+
+
+class UndefinedError(TemplateError):
+    pass
+
+
+class TemplateNotFoundError(TemplateError):
+    pass
+
+
+class TemplateEngine:
+    def render(self, name: str, context: Optional[dict] = None) -> str:
+        raise NotImplementedError()
+
+
+class Jinja2Engine(TemplateEngine):
+    def __init__(self):
+        self.env = Environment(
+            loader=PackageLoader('cephadm', 'templates'),
+            autoescape=select_autoescape(['html', 'xml']),
+            trim_blocks=True,
+            lstrip_blocks=True,
+            undefined=StrictUndefined
+        )
+
+    def render(self, name: str, context: Optional[dict] = None) -> str:
+        try:
+            template = self.env.get_template(name)
+            if context is None:
+                return template.render()
+            return template.render(context)
+        except j2_exceptions.UndefinedError as e:
+            raise UndefinedError(e.message)
+        except j2_exceptions.TemplateNotFound as e:
+            raise TemplateNotFoundError(e.message)
+
+
+class TemplateMgr:
+    def __init__(self):
+        self.engine = Jinja2Engine()
+        self.base_context = {
+            'cephadm_managed': 'This file is generated by cephadm.'
+        }
+
+    def render(self, name: str, context: Optional[dict] = None, managed_context=True) -> str:
+        """Render a string from a template with context.
+
+        :param name: template name. e.g. services/nfs/ganesha.conf.j2
+        :type name: str
+        :param context: a dictionary that contains values to be used in the template, defaults
+            to None
+        :type context: Optional[dict], optional
+        :param managed_context: to inject default context like managed header or not, defaults
+            to True
+        :type managed_context: bool, optional
+        :return: the templated string
+        :rtype: str
+        """
+        ctx = {}
+        if managed_context:
+            ctx = copy.deepcopy(self.base_context)
+        if context is not None:
+            ctx = {**ctx, **context}
+        return self.engine.render(name, ctx)
diff --git a/src/pybind/mgr/cephadm/tests/test_template.py b/src/pybind/mgr/cephadm/tests/test_template.py
new file mode 100644 (file)
index 0000000..962b306
--- /dev/null
@@ -0,0 +1,33 @@
+import pathlib
+
+import pytest
+
+from cephadm.template import TemplateMgr, UndefinedError, TemplateNotFoundError
+
+
+def test_render(fs):
+    template_base = (pathlib.Path(__file__).parent / '../templates').resolve()
+    fake_template = template_base / 'foo/bar'
+    fs.create_file(fake_template, contents='{{ cephadm_managed }}{{ var }}')
+
+    template_mgr = TemplateMgr()
+    value = 'test'
+
+    # with base context
+    expected_text = '{}{}'.format(template_mgr.base_context['cephadm_managed'], value)
+    assert template_mgr.render('foo/bar', {'var': value}) == expected_text
+
+    # without base context
+    with pytest.raises(UndefinedError):
+        template_mgr.render('foo/bar', {'var': value}, managed_context=False)
+
+    # override the base context
+    context = {
+        'cephadm_managed': 'abc',
+        'var': value
+    }
+    assert template_mgr.render('foo/bar', context) == 'abc{}'.format(value)
+
+    # template not found
+    with pytest.raises(TemplateNotFoundError):
+        template_mgr.render('foo/bar/2', {})
index 0956073bb12586e007571db92fb285baa134c466..b7c8a12a5895ddc44dfa31518f7a093c6ad5bb83 100644 (file)
@@ -8,4 +8,7 @@ pyyaml
 prettytable
 pyOpenSSL
 execnet
-remoto
\ No newline at end of file
+remoto
+Jinja2
+pyfakefs
+