Edit file File name : defaults.py Content :""" Module to work with salt formula defaults files """ import copy import logging import os import salt.fileclient import salt.utils.data import salt.utils.dictupdate as dictupdate import salt.utils.files import salt.utils.json import salt.utils.url import salt.utils.yaml __virtualname__ = "defaults" log = logging.getLogger(__name__) def _mk_client(): """ Create a file client and add it to the context """ if "cp.fileclient" not in __context__: __context__["cp.fileclient"] = salt.fileclient.get_file_client(__opts__) def _load(formula): """ Generates a list of salt://<formula>/defaults.(json|yaml) files and fetches them from the Salt master. Returns first defaults file as python dict. """ # Compute possibilities _mk_client() paths = [] for ext in ("yaml", "json"): source_url = salt.utils.url.create(formula + "/defaults." + ext) paths.append(source_url) # Fetch files from master defaults_files = __context__["cp.fileclient"].cache_files(paths) for file_ in defaults_files: if not file_: # Skip empty string returned by cp.fileclient.cache_files. continue suffix = file_.rsplit(".", 1)[-1] if suffix == "yaml": loader = salt.utils.yaml.safe_load elif suffix == "json": loader = salt.utils.json.load else: log.debug("Failed to determine loader for %r", file_) continue if os.path.exists(file_): log.debug("Reading defaults from %r", file_) with salt.utils.files.fopen(file_) as fhr: defaults = loader(fhr) log.debug("Read defaults %r", defaults) return defaults or {} def get(key, default=""): """ defaults.get is used much like pillar.get except that it will read a default value for a pillar from defaults.json or defaults.yaml files that are stored in the root of a salt formula. CLI Example: .. code-block:: bash salt '*' defaults.get core:users:root The defaults is computed from pillar key. The first entry is considered as the formula namespace. For example, querying ``core:users:root`` will try to load ``salt://core/defaults.yaml`` and ``salt://core/defaults.json``. """ # Determine formula namespace from query if ":" in key: namespace, key = key.split(":", 1) else: namespace, key = key, None # Fetch and load defaults formula files from states. defaults = _load(namespace) # Fetch value if key: return salt.utils.data.traverse_dict_and_list(defaults, key, default) else: return defaults def merge(dest, src, merge_lists=False, in_place=True): """ defaults.merge Allows deep merging of dicts in formulas. merge_lists : False If True, it will also merge lists instead of replace their items. in_place : True If True, it will merge into dest dict, if not it will make a new copy from that dict and return it. CLI Example: .. code-block:: bash salt '*' defaults.merge '{a: b}' '{d: e}' It is more typical to use this in a templating language in formulas, instead of directly on the command-line. """ if in_place: merged = dest else: merged = copy.deepcopy(dest) return dictupdate.update(merged, src, merge_lists=merge_lists) def deepcopy(source): """ defaults.deepcopy Allows deep copy of objects in formulas. By default, Python does not copy objects, it creates bindings between a target and an object. It is more typical to use this in a templating language in formulas, instead of directly on the command-line. """ return copy.deepcopy(source) def update(dest, defaults, merge_lists=True, in_place=True): """ defaults.update Allows setting defaults for group of data set e.g. group for nodes. This function is a combination of defaults.merge and defaults.deepcopy to avoid redundant in jinja. Example: .. code-block:: yaml group01: defaults: enabled: True extra: - test - stage nodes: host01: index: foo upstream: bar host02: index: foo2 upstream: bar2 .. code-block:: jinja {% do salt['defaults.update'](group01.nodes, group01.defaults) %} Each node will look like the following: .. code-block:: yaml host01: enabled: True index: foo upstream: bar extra: - test - stage merge_lists : True If True, it will also merge lists instead of replace their items. in_place : True If True, it will merge into dest dict. if not it will make a new copy from that dict and return it. It is more typical to use this in a templating language in formulas, instead of directly on the command-line. """ if in_place: nodes = dest else: nodes = deepcopy(dest) for node_name, node_vars in nodes.items(): defaults_vars = deepcopy(defaults) node_vars = merge(defaults_vars, node_vars, merge_lists=merge_lists) nodes[node_name] = node_vars return nodes Save