]> 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)
committerKiefer Chang <kiefer.chang@suse.com>
Thu, 4 Jun 2020 08:23:39 +0000 (16:23 +0800)
Signed-off-by: Kiefer Chang <kiefer.chang@suse.com>
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 eda4c6a8da7ee50e9bf4250a689208412e3e1d02..7595eac21733dfc682c58553815e52fb32c7b732 100644 (file)
@@ -656,9 +656,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 6fc2f14025782847837e970b5161d08e49d3c9ec..c4b2eee356cf30efa4ad2ce6904cf173bab201f6 100644 (file)
@@ -40,6 +40,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
@@ -355,6 +356,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 54615fccf79f4da6f2d6f3ee7e3aec5d25e9a564..7d7cb1fbe3838929db916eb05009e336b6d94768 100644 (file)
@@ -6,4 +6,7 @@ pyyaml
 prettytable
 pyOpenSSL
 execnet
-remoto
\ No newline at end of file
+remoto
+Jinja2
+pyfakefs
+