Merge pull request #26903 from bersace/fix-defaults-modules

Review defaults.get
This commit is contained in:
Mike Place 2015-09-23 08:52:20 -06:00
commit c2efb291e2

View File

@ -1,17 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import inspect
import json
import logging
import os
import yaml
import salt.fileclient
import salt.utils
import salt.ext.six as six
__virtualname__ = 'defaults'
log = logging.getLogger(__name__)
def _mk_client():
'''
Create a file client and add it to the context
@ -21,34 +24,44 @@ def _mk_client():
salt.fileclient.get_file_client(__opts__)
def _get_files(pillar_name):
def _load(formula):
'''
Generates a list of salt://<pillar_name>/defaults.(json|yaml) files
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()
pillar_name = pillar_name.replace('.', '/')
paths = []
for ext in ('yaml', 'json'):
source_url = 'salt://{0}/{1}'.format(pillar_name, 'defaults.' + ext)
source_url = 'salt://{0}/{1}'.format(formula, 'defaults.' + ext)
paths.append(source_url)
# Fetch files from master
defaults_files = __context__['cp.fileclient'].cache_files(paths)
return __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 = yaml
elif suffix == 'json':
loader = json
else:
log.debug("Failed to determine loader for %r", file_)
continue
def _load(defaults_path):
'''
Given a pillar_name and the template cache location, attempt to load
the defaults.json from the cache location. If it does not exist, try
defaults.yaml.
'''
for loader in json, yaml:
defaults_file = os.path.join(defaults_path, 'defaults.' + loader.__name__)
if os.path.exists(defaults_file):
with salt.utils.fopen(defaults_file) as fhr:
if os.path.exists(file_):
log.debug("Reading defaults from %r", file_)
with salt.utils.fopen(file_) as fhr:
defaults = loader.load(fhr)
return defaults
log.debug("Read defaults %r", defaults)
return defaults or {}
def get(key, default=''):
@ -57,79 +70,30 @@ def get(key, default=''):
a default value for a pillar from defaults.json or defaults.yaml
files that are stored in the root of a salt formula.
When called from the CLI it works exactly like pillar.get.
CLI Example:
.. code-block:: bash
salt '*' defaults.get core:users:root
When called from an SLS file, it works by first reading a defaults.json
and second a defaults.yaml file. If the key exists in these files and
does not exist in a pillar named after the formula, the value from the
defaults file is used.
The defaults is computed from pillar key. The first entry is considered as
the formula namespace.
Example core/defaults.json file for the 'core' formula:
.. code-block:: json
{
"users": {
"root": 0
}
}
With this, from a state file you can use salt['defaults.get']('users:root')
to read the '0' value from defaults.json if a core:users:root pillar
key is not defined.
For example, querying ``core:users:root`` will try to load
``salt://core/defaults.yaml`` and ``salt://core/defaults.json``.
'''
sls = None
tmplpath = None
# Determine formula namespace from query
if ':' in key:
namespace, key = key.split(':', 1)
else:
namespace, key = key, None
for frame in inspect.stack():
if sls is not None and tmplpath is not None:
break
# Fetch and load defaults formula files from states.
defaults = _load(namespace)
frame_args = inspect.getargvalues(frame[0]).locals
for _sls in (
None if not isinstance(frame_args.get('context'), dict) else frame_args.get('context').get('__sls__'),
frame_args.get('mods', [None])[0],
frame_args.get('sls')
):
if sls is not None:
break
sls = _sls
for _tmpl in (
frame_args.get('tmplpath'),
frame_args.get('tmplsrc')
):
if tmplpath is not None:
break
tmplpath = _tmpl
if not sls: # this is the case when called from CLI
return __salt__['pillar.get'](key, default)
pillar_name = sls.split('.')[0]
defaults_path = tmplpath.split(pillar_name)[0] + pillar_name
_get_files(pillar_name)
defaults = _load(defaults_path)
value = __salt__['pillar.get']('{0}:{1}'.format(pillar_name, key), None)
if value is None:
value = salt.utils.traverse_dict_and_list(defaults, key, None)
if value is None:
value = default
if isinstance(value, six.text_type):
value = str(value)
return value
# Fetch value
if key:
return salt.utils.traverse_dict_and_list(defaults, key, default)
else:
return defaults