Source code for cobbler.items.template

"""
All code blonging to Cobbler Templates.

Changelog:

V3.4.0 (unrelased):
    * Initial add of datatype.
"""

import copy
import os
import pathlib
from typing import TYPE_CHECKING, Any, List, Set, Type

from cobbler import enums, validate
from cobbler.cexceptions import CX
from cobbler.items.abstract.base_item import BaseItem
from cobbler.items.options.uri import URIOption

if TYPE_CHECKING:
    from cobbler.api import CobblerAPI

    LazyProperty = property
else:
    from cobbler.decorator import LazyProperty


[docs] class Template(BaseItem): """ A Cobbler template object. """ TYPE_NAME = "template" COLLECTION_TYPE = "template" def __init__(self, api: "CobblerAPI", *args: Any, **kwargs: Any): """ Constructor :param api: The Cobbler API object which is used for resolving information. """ super().__init__(api) # Prevent attempts to clear the to_dict cache before the object is initialized. self._has_initialized = False def autoinstall_path_validation( self: "URIOption", cobbler_api: "CobblerAPI", value: str ) -> bool: """ Autoinstall Path validation hook that allows to validate if the path is a valid one. :param api: The Cobbler API object to search for data. :param value: The new value for the path component. :returns: True in case the path is valid, False otherwhise. """ return validate.validate_autoinstall_template_file_path( cobbler_api, enums.TemplateSchema.to_enum(self.schema), self._item.template_type, # type: ignore value, ) self._template_type = "" self._uri = URIOption(api, self, path_validator=autoinstall_path_validation) self._built_in = False self._tags: Set[str] = set() self.__content = "" self._inmemory = True if len(kwargs) > 0: if "template_type" in kwargs: self.template_type = kwargs["template_type"] self.from_dict(kwargs) if self._uri.schema == enums.TemplateSchema.IMPORTLIB.value: # All templates that are sourced via importlib are built-in self._built_in = True self.__content = pathlib.Path(self._uri.path).read_text(encoding="UTF-8") self._inmemory = True if not self._has_initialized: self._has_initialized = True
[docs] def make_clone(self) -> "Template": seed_dict = copy.deepcopy(self.to_dict()) seed_dict.pop("uid", None) seed_dict.pop("built_in", None) return Template(self.api, **seed_dict)
[docs] def check_if_valid(self): """ Check if a distro object is valid. If invalid an exception is raised. """ super().check_if_valid() if not self.inmemory: return if self.template_type == "": raise CX("Template Type cannot be empty!")
def _resolve(self, property_name: List[str]) -> Any: settings_name = property_name[-1] if property_name[-1] == "owners": settings_name = "default_ownership" raw_value = self.__get_raw_value(self, property_name) if raw_value == enums.VALUE_INHERITED: return getattr(self.api.settings(), settings_name) else: return raw_value def _resolve_enum( self, property_name: List[str], enum_type: Type[enums.ConvertableEnum] ) -> Any: # The template doesn't have any enum types that need resolving return None def _resolve_list(self, property_name: List[str]) -> Any: # The template doesn't have any enum types that need resolving return None def __get_raw_value(self, obj: Any, property_name: List[str]) -> Any: """ Retrieves the raw value of a nested attribute from an object using a list of property names. :returns: The raw value of the property. :raises AttributeError: In case the property doesn't have the requested attribute. """ if hasattr(obj, f"_{property_name[0]}"): property_key = property_name.pop(0) if len(property_name) > 0: return self.__get_raw_value(getattr(obj, property_key), property_name) return getattr(obj, f"_{property_key}") raise AttributeError( f'Could not retrieve "{property_name[0]}" with obj "{obj}!' )
[docs] def refresh_content(self) -> bool: """ Reads the template from the currently set uri and caches it in-memory. In case a given URI is unavailable, the old template content is kept and a message is written to the log. :returns: If reading the template was successful. """ if self._uri.schema == enums.TemplateSchema.IMPORTLIB.value: # Built-In templates never change. If users edit them, this is undefined behavior. return True elif self._uri.schema == enums.TemplateSchema.ENVIRONMENT.value: self.__content = os.environ[self._uri.path] return True elif self._uri.schema == enums.TemplateSchema.FILE.value: self.__content = ( pathlib.Path(self.api.settings().autoinstall_templates_dir) / self._uri.path ).read_text(encoding="UTF-8") return True return False
@property def content(self) -> str: """ Property for the content of the template. :getter: Returns a cached version of the template content. :setter: In case a supported template type is set this will write the given content to the target URI. """ return self.__content @content.setter def content(self, val: str) -> None: """ The setter for the content of a given template. Accept a string. :raises ValueError: In case the URI is empty, the schema is of type improtlib or the template schema is unsupported. """ if not self._uri.path: raise ValueError("Setting the content with an empty URI is not possible!") if self._uri.schema == enums.TemplateSchema.IMPORTLIB: raise ValueError("The content of built-in templates cannot be updated.") elif self._uri.schema == enums.TemplateSchema.ENVIRONMENT: os.environ[self.uri.path] = val elif self._uri.schema == enums.TemplateSchema.FILE: pathlib.Path(self._uri.path).write_text(val, encoding="UTF-8") else: raise ValueError("Unspported template type!") self.__content = val @property def uri(self) -> URIOption: """ Represents the location where the template is located at. :getter: Returns the current URIOption object. """ return self._uri @LazyProperty def template_type(self) -> str: """ This represents the template type/language that must be used to render the template. :getter: Returns the current template_type. :setter: Sets the new value for the template_type property. """ return self._template_type @template_type.setter def template_type(self, val: str): """ Setter for the template_type property. :param val: The string with the new value. """ template_providers = self.api.templar.available_template_providers if val not in template_providers: raise ValueError( "Given template type not in the list of available template providers" f" ({', '.join(template_providers)})!" ) self._template_type = val @LazyProperty def built_in(self) -> bool: """ Property that represents if a template is built-in or not. If a template is built-in it is not persisted to disk and none of it effectively becomes read-only. :getter: Returns True if the template is built-in. """ return self._built_in @LazyProperty def tags(self) -> Set[str]: """ Property that represents the set of tags a template has. A tag can be any string, there are however strings with special meanings that means that a template is used for a special purpose inside of Cobbler. :getter: Returns the current Set of tags. :setter: Sets the new value for the tags property. """ return self._tags @tags.setter def tags(self, val: Set[str]): """ Setter for the tags property. :param val: The string with the new value. """ self._tags = val