]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/cephadm/template.py
Import ceph 15.2.8
[ceph.git] / ceph / src / pybind / mgr / cephadm / template.py
CommitLineData
e306af50 1import copy
f91f0fd5 2from typing import Optional, TYPE_CHECKING
e306af50 3
f91f0fd5 4from jinja2 import Environment, PackageLoader, select_autoescape, StrictUndefined, Template
e306af50
TL
5from jinja2 import exceptions as j2_exceptions
6
f91f0fd5
TL
7if TYPE_CHECKING:
8 from cephadm.module import CephadmOrchestrator
9
e306af50
TL
10
11class TemplateError(Exception):
12 pass
13
14
15class UndefinedError(TemplateError):
16 pass
17
18
19class TemplateNotFoundError(TemplateError):
20 pass
21
22
23class TemplateEngine:
24 def render(self, name: str, context: Optional[dict] = None) -> str:
25 raise NotImplementedError()
26
27
28class Jinja2Engine(TemplateEngine):
29 def __init__(self):
30 self.env = Environment(
31 loader=PackageLoader('cephadm', 'templates'),
f91f0fd5 32 autoescape=select_autoescape(['html', 'xml'], default_for_string=False),
e306af50
TL
33 trim_blocks=True,
34 lstrip_blocks=True,
35 undefined=StrictUndefined
36 )
37
38 def render(self, name: str, context: Optional[dict] = None) -> str:
39 try:
40 template = self.env.get_template(name)
41 if context is None:
42 return template.render()
43 return template.render(context)
44 except j2_exceptions.UndefinedError as e:
45 raise UndefinedError(e.message)
46 except j2_exceptions.TemplateNotFound as e:
47 raise TemplateNotFoundError(e.message)
48
f91f0fd5
TL
49 def render_plain(self, source, context):
50 try:
51 template = self.env.from_string(source)
52 if context is None:
53 return template.render()
54 return template.render(context)
55 except j2_exceptions.UndefinedError as e:
56 raise UndefinedError(e.message)
57 except j2_exceptions.TemplateNotFound as e:
58 raise TemplateNotFoundError(e.message)
59
e306af50
TL
60
61class TemplateMgr:
f91f0fd5 62 def __init__(self, mgr: "CephadmOrchestrator"):
e306af50
TL
63 self.engine = Jinja2Engine()
64 self.base_context = {
65 'cephadm_managed': 'This file is generated by cephadm.'
66 }
f91f0fd5 67 self.mgr = mgr
e306af50 68
f91f0fd5
TL
69 def render(self, name: str,
70 context: Optional[dict] = None,
71 managed_context=True,
72 host: Optional[str] = None) -> str:
e306af50
TL
73 """Render a string from a template with context.
74
75 :param name: template name. e.g. services/nfs/ganesha.conf.j2
76 :type name: str
77 :param context: a dictionary that contains values to be used in the template, defaults
78 to None
79 :type context: Optional[dict], optional
80 :param managed_context: to inject default context like managed header or not, defaults
81 to True
82 :type managed_context: bool, optional
f91f0fd5
TL
83 :param host: The host name used to build the key to access
84 the module's persistent key-value store.
85 :type host: Optional[str], optional
e306af50
TL
86 :return: the templated string
87 :rtype: str
88 """
89 ctx = {}
90 if managed_context:
91 ctx = copy.deepcopy(self.base_context)
92 if context is not None:
93 ctx = {**ctx, **context}
f91f0fd5
TL
94
95 # Check if the given name exists in the module's persistent
96 # key-value store, e.g.
97 # - blink_device_light_cmd
98 # - <host>/blink_device_light_cmd
99 # - services/nfs/ganesha.conf
100 store_name = name.rstrip('.j2')
101 custom_template = self.mgr.get_store(store_name, None)
102 if host and custom_template is None:
103 store_name = '{}/{}'.format(host, store_name)
104 custom_template = self.mgr.get_store(store_name, None)
105
106 if custom_template:
107 return self.engine.render_plain(custom_template, ctx)
108 else:
109 return self.engine.render(name, ctx)