Repository: cobbler/cobbler

Issue:
Functions defined in cheetah_macros unavailable in templates
### Describe the bug

Cheetah macros/functions defined in /etc/cobbler/cheetah_macros are not available for use in templates as documented in extending_cobbler.

### To Reproduce

Define a function in /etc/cobbler/cheetah_macros.

```
#def hello($str)
Hello, $str
#end def
```

Attempt to use it in a template. For example, in the main section of a kickstart template;

```
$hello('Grant')
```

Check the rendered kickstart;
```
# curl -s http://127.0.0.1/cblr/svc/op/autoinstall/system/test-host
# This automatic OS installation file had errors that prevented it from being rendered correctly.
# The cobbler.log should have information relating to this failure.
```

Check cobbler logs;
```
# cannot find 'hello'
#   File "/usr/lib/python3.6/site-packages/cobbler/templar.py", line 212, in render_cheetah
#     data_out = t.respond()
```

### Expected behavior

Rendered output should include the raw string "Hello, Grant"

### Version

````
# cobbler version
Cobbler 3.1.2
  source: ?, ?
  build time: Sun May 31 02:32:34 2020
````

### Screenshots

N/A

### Additional context

N/A



Discussion:
I find that the docs about this are rather vague or missing. Let's fix that for the next release.
I looked through the code and saw that the file is loaded but never actually used. I will dive into it to fix it.

Resolving patch:
diff --git a/cobbler/templar.py b/cobbler/templar.py
--- a/cobbler/templar.py
+++ b/cobbler/templar.py
@@ -22,8 +22,6 @@
 02110-1301  USA
 """
 
-from builtins import str
-from builtins import object
 import re
 import Cheetah
 import functools
@@ -47,8 +45,10 @@
 major, minor, release = Cheetah.Version.split('.')[0:3]
 fix_cheetah_class = (int(major), int(minor), int(release)) >= (2, 4, 2)
 
+CHEETAH_MACROS_FILE = '/etc/cobbler/cheetah_macros'
 
-class Templar(object):
+
+class Templar:
 
     def __init__(self, collection_mgr, logger=None):
         """
@@ -206,15 +206,20 @@ def render_cheetah(self, raw_data, search_table, subject=None):
         })
 
         # Now do full templating scan, where we will also templatify the snippet insertions
-        t = Template(source=raw_data, searchList=[search_table], compilerSettings={'useStackFrame': False})
+        t = Template().compile(
+            source=raw_data,
+            compilerSettings={'useStackFrame': False},
+            baseclass=Template.compile(file=CHEETAH_MACROS_FILE)
+        )
 
         if fix_cheetah_class:
             t.SNIPPET = functools.partial(t.SNIPPET, t)
             t.read_snippet = functools.partial(t.read_snippet, t)
 
         try:
-            data_out = t.respond()
-            self.last_errors = t.errorCatcher().listErrors()
+            generated_template_class = t(searchList=[search_table])
+            data_out = str(generated_template_class)
+            self.last_errors = generated_template_class.errorCatcher().listErrors()
             if self.last_errors:
                 self.logger.warning("errors were encountered rendering the template")
                 self.logger.warning("\n" + pprint.pformat(self.last_errors))
diff --git a/cobbler/template_api.py b/cobbler/template_api.py
--- a/cobbler/template_api.py
+++ b/cobbler/template_api.py
@@ -29,10 +29,8 @@
 from cobbler.cexceptions import FileNotFoundException
 from cobbler import utils
 
-CHEETAH_MACROS_FILE = '/etc/cobbler/cheetah_macros'
-
 # This class is defined using the Cheetah language. Using the 'compile' function
-# we can compile the source directly into a python class. This class will allow
+# we can compile the source directly into a Python class. This class will allow
 # us to define the cheetah builtins.
 
 
@@ -49,7 +47,6 @@ def __init__(self, **kwargs):
 
         :param kwargs: These arguments get passed to the super constructor of this class.
         """
-        self.MacrosTemplate = Template.compile(file=CHEETAH_MACROS_FILE)
         self.BuiltinTemplate = Template.compile(source="\n".join([
 
             # This part (see 'Template' below
@@ -92,8 +89,9 @@ def __init__(self, **kwargs):
     @classmethod
     def compile(cls, *args, **kwargs):
         """
-        Compile a cheetah template with Cobbler modifications. Modifications include SNIPPET:: syntax replacement and
-        inclusion of Cobbler builtin methods.
+        Compile a cheetah template with Cobbler modifications. Modifications include ``SNIPPET::`` syntax replacement
+        and inclusion of Cobbler builtin methods. Please be aware that you cannot use the ``baseclass`` attribute of
+        Cheetah anymore due to the fact that we are using it in our implementation to enable the Cheetah Macros.
 
         :param args: These just get passed right to Cheetah.
         :param kwargs: We just execute our own preprocessors and remove them and let afterwards handle Cheetah the rest.


Test patch: