Merge pull request #3546 from kjkuan/pydsl_include

Add support for #3483
This commit is contained in:
Thomas S Hatch 2013-02-04 11:58:22 -08:00
commit f4005c0e0f
8 changed files with 368 additions and 144 deletions

View file

@ -71,7 +71,6 @@ Example of a ``cmd`` state calling a python function::
#TODOs:
#
# - support exclude declarations
# - support include declarations with env
#
# - allow this:
# state('X').cmd.run.cwd = '/'
@ -92,8 +91,9 @@ try:
except ImportError:
Dict = dict
from salt.state import HighState
REQUISITES = set("require watch use require_in watch_in use_in".split())
REQUISITES = set('require watch use require_in watch_in use_in'.split())
class PyDslError(Exception):
pass
@ -102,31 +102,77 @@ class Options(dict):
return self.get(name)
def sls(sls):
return Sls(sls)
SLS_MATCHES = None
def sls(*args):
return Sls(*args)
class Sls(object):
# tracks all state declarations globally across sls files
all_decls = weakref.WeakValueDictionary()
def __init__(self, sls):
# a stack of current rendering Sls objects, maintained and used by the pydsl renderer.
render_stack = []
def __init__(self, sls, mod, rendered_sls):
self.name = sls
self.includes = []
self.included_highstate = {}
self.extends = []
self.decls = []
self.options = Options()
self.funcs = [] # track the ordering of state func declarations
self.slsmod = mod # the current rendering sls python module
self.rendered_sls = rendered_sls # a set of names of rendered sls modules
def set(self, **options):
self.options.update(options)
def include(self, *sls_names):
self.includes.extend(sls_names)
def include(self, *sls_names, **kws):
env = kws.get('env', self.slsmod.__env__)
if kws.get('delayed', False):
for incl in sls_names:
self.includes.append((env, incl))
return
HIGHSTATE = HighState.current
if not HIGHSTATE:
raise PyDslError('include() only works when running state.highstate!')
global SLS_MATCHES
if SLS_MATCHES is None:
SLS_MATCHES = HIGHSTATE.top_matches(HIGHSTATE.get_top())
highstate = self.included_highstate
slsmods = [] # a list of pydsl sls modules rendered.
for sls in sls_names:
if sls not in self.rendered_sls:
self.rendered_sls.add(sls) # needed in case the starting sls uses the pydsl renderer.
histates, errors = HIGHSTATE.render_state(sls, env, self.rendered_sls, SLS_MATCHES)
HIGHSTATE.merge_included_states(highstate, histates, errors)
if errors:
raise PyDslError('\n'.join(errors))
HIGHSTATE.clean_duplicate_extends(highstate)
id = '_slsmod_'+sls
if id not in highstate:
slsmods.append(None)
else:
for arg in highstate[id]['stateconf']:
if isinstance(arg, dict) and iter(arg).next() == 'slsmod':
slsmods.append(arg['slsmod'])
break
if not slsmods:
return None
return slsmods[0] if len(slsmods) == 1 else slsmods
def extend(self, *state_funcs):
if self.options.ordered and self.last_func():
raise PyDslError("Can't extend while the ordered option is turned on!")
raise PyDslError('Cannot extend() while the ordered option is turned on!')
for f in state_funcs:
id = f.mod._state_id
self.extends.append(self.all_decls[id])
@ -158,26 +204,33 @@ class Sls(object):
def track_func(self, statefunc):
self.funcs.append(statefunc)
def to_highstate(self, slsmod=None):
def to_highstate(self):
# generate a state that uses the stateconf.set state, which
# is a no-op state, to hold a reference to the sls module
# containing the DSL statements. This is to prevent the module
# from being GC'ed, so that objects defined in it will be
# available while salt is executing the states.
if slsmod:
self.state().stateconf.set(slsmod=slsmod)
self.state('_slsmod_'+self.name).stateconf.set(slsmod=self.slsmod)
highstate = Dict()
if self.includes:
highstate['include'] = self.includes[:]
highstate['include'] = [{t[0]: t[1]} for t in self.includes]
if self.extends:
highstate['extend'] = extend = Dict()
for ext in self.extends:
extend[ext._id] = ext._repr(context='extend')
for ext in self.extends:
extend[ext._id] = ext._repr(context='extend')
for decl in self.decls:
highstate[decl._id] = decl._repr()
if self.included_highstate:
errors = []
HighState.current.merge_included_states(highstate, self.included_highstate, errors)
if errors:
raise PyDslError('\n'.join(errors))
return highstate
def load_highstate(self, highstate):
for sid, decl in highstate.iteritems():
s = self.state(sid)
@ -292,8 +345,8 @@ class StateFunction(object):
def _repr(self, context=None):
if not self.name and context != 'extend':
raise PyDslError("No state function specified for module: "
"{0}".format(self.mod._name))
raise PyDslError('No state function specified for module: '
'{0}'.format(self.mod._name))
if not self.name and context == 'extend':
return self.args
return [self.name]+self.args
@ -323,7 +376,7 @@ class StateFunction(object):
ref = mod._state_id
elif not (mod and ref):
raise PyDslError(
"Invalid a requisite reference declaration! {0}: {1}".format(
'Invalid a requisite reference declaration! {0}: {1}'.format(
mod, ref))
self.args.append({req_type: [{str(mod): str(ref)}]})
@ -333,3 +386,4 @@ class StateFunction(object):
del ns
del req_type

View file

@ -133,6 +133,7 @@ def highstate(test=None, **kwargs):
opts['test'] = test
st_ = salt.state.HighState(opts)
salt.state.HighState.current = st_
ret = st_.call_highstate(exclude=kwargs.get('exclude', []))
serial = salt.payload.Serial(__opts__)
cache_file = os.path.join(__opts__['cachedir'], 'highstate.p')

View file

@ -18,18 +18,15 @@ from salt.utils.dictupdate import update
log = logging.getLogger(__name__)
PILLAR = None
def get_pillar(opts, grains, id_, env=None):
'''
Return the correct pillar driver based on the file_client option
'''
try:
return {
'remote': RemotePillar,
'local': Pillar
}.get(opts['file_client'], 'local')(opts, grains, id_, env)
except KeyError:
return Pillar(opts, grains, id_, env)
return {
'remote': RemotePillar,
'local': Pillar
}.get(opts['file_client'], Pillar)(opts, grains, id_, env)
class RemotePillar(object):

View file

@ -146,6 +146,41 @@ whose arguments are just :term:`function declaration` objects.
include('edit.vim', 'http.server')
extend(state('apache2').service.watch(file='/etc/httpd/httpd.conf')
The ``include`` function, by default, causes the included sls file to be rendered
as soon as the ``include`` function is called. It returns a list of rendered module
objects; sls files not rendered with the pydsl renderer return ``None``'s.
This behavior creates no :term:`include declaration`'s in the resulting high state
data structure.
.. code-block:: python
import types
# including multiple sls returns a list.
_, mod = include('a-non-pydsl-sls', 'a-pydsl-sls')
assert _ is None
assert isinstance(slsmods[1], types.ModuleType)
# including a single sls returns a single object
mod = include('a-pydsl-sls')
# myfunc is a function that calls state(...) to create more states.
mod.myfunc(1, 2, "three")
Notice how you can define a reusable function in your pydsl sls module and then
call it via the module returned by ``include``.
It's still possible to do late includes by passing the ``delayed=True`` keyword
argument to ``include``.
.. code-block:: python
include('edit.vim', 'http.server', delayed=True)
Above will just create a :term:`include declaration` in the rendered result, and
such call always returns ``None``.
Special integration with the `cmd` state
-----------------------------------------
@ -246,8 +281,9 @@ or after a state in the including sls file.
import imp
__all__ = ['render']
def render(template, env='', sls='', tmplpath=None, **kws):
def render(template, env='', sls='', tmplpath=None, rendered_sls=None, **kws):
mod = imp.new_module(sls)
# Note: mod object is transient. It's existence only lasts as long as
# the lowstate data structure that the highstate in the sls file
@ -255,15 +291,15 @@ def render(template, env='', sls='', tmplpath=None, **kws):
mod.__name__ = sls
# to workaround state.py's use of copy.deepcopy(chunck)
# to workaround state.py's use of copy.deepcopy(chunk)
mod.__deepcopy__ = lambda x: mod
dsl_sls = __salt__['pydsl.sls'](sls)
dsl_sls = __salt__['pydsl.sls'](sls, mod, rendered_sls)
mod.__dict__.update(
__pydsl__=dsl_sls,
include=dsl_sls.include,
extend=dsl_sls.extend,
state=dsl_sls.state,
include=_wrap_sls(dsl_sls.include),
extend=_wrap_sls(dsl_sls.extend),
state=_wrap_sls(dsl_sls.state),
__salt__=__salt__,
__grains__=__grains__,
__opts__=__opts__,
@ -272,6 +308,17 @@ def render(template, env='', sls='', tmplpath=None, **kws):
__sls__=sls,
__file__=tmplpath,
**kws)
dsl_sls.render_stack.append(dsl_sls)
exec template.read() in mod.__dict__
return dsl_sls.to_highstate(mod)
highstate = dsl_sls.to_highstate()
dsl_sls.render_stack.pop()
return highstate
def _wrap_sls(method):
def _sls_method(*args, **kws):
sls = method.im_class.render_stack[-1]
return getattr(sls, method.__name__)(*args, **kws)
return _sls_method

View file

@ -123,7 +123,7 @@ def render(input, env='', sls='', argline='', **kws):
data = copy.deepcopy(high)
try:
rewrite_single_shorthand_state_decl(data)
rewrite_sls_includes_excludes(data, sls)
rewrite_sls_includes_excludes(data, sls, env)
if not extract and implicit_require:
sid = has_names_decls(data)
@ -137,7 +137,7 @@ def render(input, env='', sls='', argline='', **kws):
add_implicit_requires(data)
if gen_start_state:
add_start_state(data)
add_start_state(data, sls)
if not extract and not no_goal_state:
add_goal_state(data)
@ -265,7 +265,7 @@ def _parent_sls(sls):
return sls[:i] + '.' if i != -1 else ''
def rewrite_sls_includes_excludes(data, sls):
def rewrite_sls_includes_excludes(data, sls, env):
# if the path of the included/excluded sls starts with a leading dot(.)
# then it's taken to be relative to the including/excluding sls.
sls = _parent_sls(sls)
@ -273,8 +273,13 @@ def rewrite_sls_includes_excludes(data, sls):
if sid == 'include':
includes = data[sid]
for i, each in enumerate(includes):
if each.startswith('.'):
includes[i] = sls + each[1:]
if isinstance(each, dict):
slsenv, incl = each.popitem()
else:
slsenv = env
incl = each
if incl.startswith('.'):
includes[i] = {slsenv: sls+incl[1:]}
elif sid == 'exclude':
for sdata in data[sid]:
if 'sls' in sdata and sdata['sls'].startswith('.'):
@ -331,9 +336,13 @@ def nvlist2(thelist, names=None):
def statelist(states_dict, sid_excludes=set(['include', 'exclude'])):
for sid, states in states_dict.iteritems():
if sid.startswith('__'):
continue
if sid in sid_excludes:
continue
for sname, args in states.iteritems():
if sname.startswith('__'):
continue
yield sid, states, sname, args
@ -448,7 +457,7 @@ def add_implicit_requires(data):
prev_state = (state_name(sname), sid)
def add_start_state(data):
def add_start_state(data, sls):
start_sid = __opts__['stateconf_start_state']
if start_sid in data:
raise SaltRenderError(
@ -457,8 +466,19 @@ def add_start_state(data):
)
if not data:
return
first = statelist(data, set(['include', 'exclude', 'extend'])).next()[0]
reqin = {state_name(data[first].iterkeys().next()): first}
# the start state is either the first state whose id declaration has
# no __sls__, or it's the first state whose id declaration has a
# __sls__ == sls.
non_sids = set(['include', 'exclude', 'extend'])
for sid, states in data.iteritems():
if sid in non_sids or sid.startswith('__'):
continue
if '__sls__' not in states or states['__sls__'] == sls:
break
else:
raise SaltRenderError('Can\'t determine the first state in the sls file!')
reqin = {state_name(data[sid].iterkeys().next()): sid}
data[start_sid] = { STATE_FUNC: [ {'require_in': [reqin]} ] }
@ -471,8 +491,13 @@ def add_goal_state(data):
)
else:
reqlist = []
for sid, _, state, _ in \
for sid, states, state, _ in \
statelist(data, set(['include', 'exclude', 'extend'])):
if '__sls__' in states:
# Then id declaration must have been included from a
# rendered sls. Currently, this is only possible with
# pydsl's high state output.
continue
reqlist.append({state_name(state): sid})
data[goal_sid] = {STATE_FUNC: [dict(require=reqlist)]}

View file

@ -1763,12 +1763,12 @@ class BaseHighState(object):
state = None
try:
state = compile_template(
fn_, self.state.rend, self.state.opts['renderer'], env, sls)
fn_, self.state.rend, self.state.opts['renderer'], env, sls, rendered_sls=mods)
except Exception as exc:
errors.append(('Rendering SLS {0} failed, render error:\n{1}'
.format(sls, exc)))
import traceback
errors.append(('Rendering SLS {0} failed, render error:\n{1}\n{2}'
.format(sls, traceback.format_exc(), exc)))
mods.add(sls)
nstate = None
if state:
if not isinstance(state, dict):
errors.append(('SLS {0} does not render to a dictionary'
@ -1809,16 +1809,17 @@ class BaseHighState(object):
# the include must exist in the current environment
if len(resolved_envs) == 1 or env in resolved_envs:
if inc_sls not in mods:
nstate, mods, err = self.render_state(
nstate, err = self.render_state(
inc_sls,
resolved_envs[0] if len(resolved_envs) == 1 else env,
mods,
matches
)
if nstate:
state.update(nstate)
if err:
errors += err
if nstate:
self.merge_included_states(state, nstate, errors)
state.update(nstate)
if err:
errors.extend(err)
else:
msg = ''
if not resolved_envs:
@ -1836,41 +1837,8 @@ class BaseHighState(object):
log.error(msg)
if self.opts['failhard']:
errors.append(msg)
if 'extend' in state:
ext = state.pop('extend')
for name in ext:
if not isinstance(ext[name], dict):
errors.append(('Extension name {0} in sls {1} is '
'not a dictionary'
.format(name, sls)))
continue
if '__sls__' not in ext[name]:
ext[name]['__sls__'] = sls
if '__env__' not in ext[name]:
ext[name]['__env__'] = env
for key in ext[name]:
if key.startswith('_'):
continue
if not isinstance(ext[name][key], list):
continue
if '.' in key:
comps = key.split('.')
ext[name][comps[0]] = ext[name].pop(key)
ext[name][comps[0]].append(comps[1])
if '__extend__' not in state:
state['__extend__'] = [ext]
else:
state['__extend__'].append(ext)
if 'exclude' in state:
exc = state.pop('exclude')
if not isinstance(exc, list):
err = ('Exclude Declaration in SLS {0} is not formed '
'as a list'.format(sls))
errors.append(err)
if '__exclude__' not in state:
state['__exclude__'] = exc
else:
state['__exclude__'].extend(exc)
self._handle_extend(state, sls, env, errors)
self._handle_exclude(state, sls, env, errors)
for name in state:
if not isinstance(state[name], dict):
if name == '__extend__':
@ -1925,7 +1893,40 @@ class BaseHighState(object):
state[name]['__env__'] = env
else:
state = {}
return state, mods, errors
return state, errors
def _handle_extend(self, state, sls, env, errors):
if 'extend' in state:
ext = state.pop('extend')
for name in ext:
if not isinstance(ext[name], dict):
errors.append(('Extension name {0} in sls {1} is '
'not a dictionary'
.format(name, sls)))
continue
if '__sls__' not in ext[name]:
ext[name]['__sls__'] = sls
if '__env__' not in ext[name]:
ext[name]['__env__'] = env
for key in ext[name]:
if key.startswith('_'):
continue
if not isinstance(ext[name][key], list):
continue
if '.' in key:
comps = key.split('.')
ext[name][comps[0]] = ext[name].pop(key)
ext[name][comps[0]].append(comps[1])
state.setdefault('__extend__', []).append(ext)
def _handle_exclude(self, state, sls, env, errors):
if 'exclude' in state:
exc = state.pop('exclude')
if not isinstance(exc, list):
err = ('Exclude Declaration in SLS {0} is not formed '
'as a list'.format(sls))
errors.append(err)
state.setdefault('__exclude__', []).extend(exc)
def render_highstate(self, matches):
'''
@ -1933,55 +1934,56 @@ class BaseHighState(object):
high data structure.
'''
highstate = {}
errors = []
for env, states in matches.items():
mods = set()
for sls_match in states:
for sls in fnmatch.filter(self.avail[env], sls_match):
state, mods, err = self.render_state(sls, env, mods, matches)
# The extend members can not be treated as globally unique:
if '__extend__' in state and '__extend__' in highstate:
highstate['__extend__'].extend(state.pop('__extend__'))
if '__exclude__' in state and '__exclude__' in highstate:
highstate['__exclude__'].extend(state.pop('__exclude__'))
for id_ in state:
if id_ in highstate:
if highstate[id_] != state[id_]:
errors.append(('Detected conflicting IDs, SLS'
' IDs need to be globally unique.\n The'
' conflicting ID is "{0}" and is found in SLS'
' "{1}:{2}" and SLS "{3}:{4}"').format(
id_,
highstate[id_]['__env__'],
highstate[id_]['__sls__'],
state[id_]['__env__'],
state[id_]['__sls__'])
)
state, errors = self.render_state(sls, env, mods, matches)
if state:
try:
highstate.update(state)
except ValueError:
errors.append(
'Error when rendering state with '
'contents: {0}'.format(
state
)
)
if err:
errors += err
# Clean out duplicate extend data
self.merge_included_states(highstate, state, errors)
self.clean_duplicate_extends(highstate)
return highstate, errors
def clean_duplicate_extends(self, highstate):
if '__extend__' in highstate:
highext = []
for ext in highstate['__extend__']:
for key, val in ext.items():
exists = False
for hext in highext:
if hext == {key: val}:
exists = True
if not exists:
highext.append({key: val})
highstate['__extend__'] = highext
return highstate, errors
for items in (ext.items() for ext in highstate['__extend__']):
for item in items:
if item not in highext:
highext.append(item)
highstate['__extend__'] = [{t[0]: t[1]} for t in highext]
def merge_included_states(self, highstate, state, errors):
# The extend members can not be treated as globally unique:
if '__extend__' in state:
highstate.setdefault('__extend__', []).extend(state.pop('__extend__'))
if '__exclude__' in state:
highstate.setdefault('__exclude__', []).extend(state.pop('__exclude__'))
for id_ in state:
if id_ in highstate:
if highstate[id_] != state[id_]:
errors.append(('Detected conflicting IDs, SLS'
' IDs need to be globally unique.\n The'
' conflicting ID is "{0}" and is found in SLS'
' "{1}:{2}" and SLS "{3}:{4}"').format(
id_,
highstate[id_]['__env__'],
highstate[id_]['__sls__'],
state[id_]['__env__'],
state[id_]['__sls__'])
)
try:
highstate.update(state)
except ValueError:
errors.append(
'Error when rendering state with contents: {0}'.format(state)
)
def call_highstate(self, exclude=None):
'''
@ -2073,6 +2075,7 @@ class HighState(BaseHighState):
compound state derived from a group of template files stored on the
salt master or in the local cache.
'''
current = None # Current active HighState object during a state.highstate run.
def __init__(self, opts):
self.client = salt.fileclient.get_file_client(opts)
BaseHighState.__init__(self, opts)

View file

@ -107,7 +107,8 @@ state('H').cmd.run('echo world')
include(
'some.sls.file',
'another.sls.file',
'more.sls.file'
'more.sls.file',
delayed=True
)
A = state('A').cmd.run('echo hoho', cwd='/')
state('B').cmd.run('echo hehe', cwd='/')
@ -120,7 +121,7 @@ extend(
''')
self.assertEqual(len(result), 4)
self.assertEqual(result['include'],
'some.sls.file another.sls.file more.sls.file'.split())
[{'base': sls} for sls in 'some.sls.file another.sls.file more.sls.file'.split()])
extend = result['extend']
self.assertEqual(extend['X']['cmd'][0], 'run')
self.assertEqual(extend['X']['cmd'][1]['cwd'], '/a/b/c')
@ -249,6 +250,7 @@ state('B').file.managed(source='/a/b/c')
yyy = os.path.join(dirpath, 'yyy.sls')
with open(yyy, 'w') as yyy:
yyy.write('''#!pydsl|stateconf -ps
__pydsl__.set(ordered=True)
state('.D').cmd.run('echo D >> {0}', cwd='/')
state('.E').cmd.run('echo E >> {1}', cwd='/')
state('.F').cmd.run('echo F >> {2}', cwd='/')
@ -259,10 +261,10 @@ state('.F').cmd.run('echo F >> {2}', cwd='/')
aaa.write('''#!pydsl|stateconf -ps
include('xxx', 'yyy')
# make all states in yyy run BEFORE states in this sls.
# make all states in xxx run BEFORE states in this sls.
extend(state('.start').stateconf.require(stateconf='xxx::goal'))
# make all states in xxx run AFTER this sls.
# make all states in yyy run AFTER this sls.
extend(state('.goal').stateconf.require_in(stateconf='yyy::start'))
__pydsl__.set(ordered=True)
@ -272,17 +274,7 @@ state('.B').cmd.run('echo B >> {1}', cwd='/')
state('.C').cmd.run('echo C >> {2}', cwd='/')
'''.format(output, output, output))
OPTS['file_roots'] = dict(base=[dirpath])
HIGHSTATE = HighState(OPTS)
HIGHSTATE.state.load_modules()
sys.modules['salt.loaded.int.render.pydsl'].__salt__ = HIGHSTATE.state.functions
high, errors = HIGHSTATE.render_highstate({'base': ['aaa']})
# import pprint
# pprint.pprint(errors)
# pprint.pprint(high)
out = HIGHSTATE.state.call_high(high)
# pprint.pprint(out)
state_highstate({'base': ['aaa']}, dirpath)
with open(output, 'r') as f:
self.assertEqual(''.join(f.read().split()), "XYZABCDEF")
@ -290,4 +282,109 @@ state('.C').cmd.run('echo C >> {2}', cwd='/')
shutil.rmtree(dirpath, ignore_errors=True)
def test_rendering_includes(self):
if sys.version_info < (2, 7):
self.skipTest('OrderedDict is not available')
dirpath = tempfile.mkdtemp()
output = os.path.join(dirpath, 'output')
try:
aaa = os.path.join(dirpath, 'aaa.sls')
with open(aaa, 'w') as aaa:
aaa.write('''#!pydsl|stateconf -ps
include('xxx')
yyy = include('yyy')
# ensure states in xxx are run first, then those in yyy and then those in aaa last.
extend(state('yyy::start').stateconf.require(stateconf='xxx::goal'))
extend(state('.start').stateconf.require(stateconf='yyy::goal'))
extend(state('yyy::Y2').cmd.run('echo Y2 extended >> {0}'))
__pydsl__.set(ordered=True)
yyy.hello('red', 1)
yyy.hello('green', 2)
yyy.hello('blue', 3)
'''.format(output))
xxx = os.path.join(dirpath, 'xxx.sls')
with open(xxx, 'w') as xxx:
xxx.write('''#!stateconf -os yaml . jinja
include:
- yyy
extend:
yyy::start:
stateconf.set:
- require:
- stateconf: .goal
yyy::Y1:
cmd.run:
- name: 'echo Y1 extended >> {0}'
.X1:
cmd.run:
- name: echo X1 >> {1}
- cwd: /
.X2:
cmd.run:
- name: echo X2 >> {2}
- cwd: /
.X3:
cmd.run:
- name: echo X3 >> {3}
- cwd: /
'''.format(output, output, output, output))
yyy = os.path.join(dirpath, 'yyy.sls')
with open(yyy, 'w') as yyy:
yyy.write('''#!pydsl|stateconf -ps
include('xxx')
__pydsl__.set(ordered=True)
state('.Y1').cmd.run('echo Y1 >> {0}', cwd='/')
state('.Y2').cmd.run('echo Y2 >> {1}', cwd='/')
state('.Y3').cmd.run('echo Y3 >> {2}', cwd='/')
def hello(color, number):
state(color).cmd.run('echo hello '+color+' '+str(number)+' >> {3}', cwd='/')
'''.format(output, output, output, output))
state_highstate({'base': ['aaa']}, dirpath)
expected = '''
X1
X2
X3
Y1 extended
Y2 extended
Y3
hello red 1
hello green 2
hello blue 3
'''.lstrip()
with open(output, 'r') as f:
self.assertEqual(f.read(), expected)
finally:
shutil.rmtree(dirpath, ignore_errors=True)
def state_highstate(matches, dirpath):
OPTS['file_roots'] = dict(base=[dirpath])
HIGHSTATE = HighState(OPTS)
HighState.current = HIGHSTATE
HIGHSTATE.state.load_modules()
sys.modules['salt.loaded.int.render.pydsl'].__salt__ = \
HIGHSTATE.state.functions
high, errors = HIGHSTATE.render_highstate({'base': ['aaa']})
if errors:
import pprint
pprint.pprint('\n'.join(errors))
pprint.pprint(high)
out = HIGHSTATE.state.call_high(high)
# pprint.pprint(out)

View file

@ -152,7 +152,7 @@ state_id:
- {0}:
- cmd: .utils::some_state
'''.format(req), sls='test.work')
self.assertEqual(result['include'][1], 'test.utils')
self.assertEqual(result['include'][1], {'base': 'test.utils'})
self.assertEqual(result['state_id']['cmd.run'][2][req][0]['cmd'],
'test.utils::some_state')