Search code examples
pythonsalt-project

Call state module from within state module


I'm writing a custom state module with the purpose of creating a file in a given location with (partly) configurable content.

Basically I'm looking to shorten the following SLS (simplified for the purpose of this question, more complex in reality)...

/etc/foo/bar:
  file.managed:
    - contents: Hello World

...to this:

bar:
  my_module.foo:
    - message: World

Since this functionality is basically a more specialized version of the file.managed state, it would be useful for me to re-use the file.managed state in my custom state module.

Is there any way to call the file.managed state module from my own state module?


What I've tried already (unsuccesfully):

  1. Importing salt.states.file and calling salt.states.file.managed:

    import salt.states.file
    
    def foo(name, message, **kwargs):
        return salt.states.file.managed(name='/etc/foo/%s' % name,
                                        contents='Hello %s' % message, 
                                        **kwargs)
    

    This results in an error message:

      File "/usr/lib/python2.7/dist-packages/salt/state.py", line 1626, in call
        **cdata['kwargs'])
      File "/usr/lib/python2.7/dist-packages/salt/loader.py", line 1492, in wrapper
        return f(*args, **kwargs)
      File "/var/cache/salt/minion/extmods/states/my_module.py", line 14, in static_pod
        contents=yaml.dump(data, default_flow_style=False), **kwargs)
      File "/usr/lib/python2.7/dist-packages/salt/states/file.py", line 1499, in managed
        mode = __salt__['config.manage_mode'](mode)
    NameError: global name '__salt__' is not defined
    
  2. Using __salt__['file.managed']:

    def foo(name, message, **kwargs):
        return __salt__['file.managed'](name='/etc/foo/%s' % name,
                                        contents='Hello %s' % message, 
                                        **kwargs)
    

    Which also results in a (different) error message (which is unsurprising, as the documentation explicitly states that __salt__ only contains execution modules, and not state modules):

      File "/usr/lib/python2.7/dist-packages/salt/state.py", line 1626, in call
        **cdata['kwargs'])
      File "/usr/lib/python2.7/dist-packages/salt/loader.py", line 1492, in wrapper
        return f(*args, **kwargs)
      File "/var/cache/salt/minion/extmods/states/my_module.py", line 13, in static_pod
        return __salt__['file.managed'](name='/etc/foo/%s' % name,
      File "/usr/lib/python2.7/dist-packages/salt/loader.py", line 900, in __getitem__
        func = super(LazyLoader, self).__getitem__(item)
      File "/usr/lib/python2.7/dist-packages/salt/utils/lazy.py", line 93, in __getitem__
        raise KeyError(key)
    KeyError: 'file.managed'
    

Solution

  • I found a solution using the state.single execution module:

    def foo(name, messsage):
        result = __salt__['state.single'](fun='file.managed',
                                          name='/etc/foo/%s' % name,
                                          contents='Hello %s' % message,
                                          test=__opts__['test'])
        return result.items()[0][1]
    

    The state.single module returns a data structure like the following:

    {
      'file_|-/etc/foo/bar_|-/etc/foo/bar_|-managed': {
        'comment': 'The file /etc/foo/bar is set to be changed',
        'name': '/etc/foo/bar',
        'start_time': '08:24:45.022518',
        'result': None,
        'duration': 18.019,
        '__run_num__': 0,
        'changes': {'diff': '...'}
      }
    }
    

    The inner object (everything under the file_|-/etc/foo/bar_|-/etc/foo/bar_|-managed) is what the file.managed module would return by itself, so you can re-use this value as your custom state's return value.