Create a Salt Execution Module

Updated by Linode Written by Linode

Contribute on GitHub

Report an Issue | View File | Edit File

A Salt execution module is a Python module that runs on a Salt minion. It perform tasks and returns data to the Salt master. In this tutorial you will create and install an execution module that will call the US National Weather Service API and return the current temperature at a specified weather station. This example could easily be adapted to access any API.

Before You Begin

If you haven’t already, set up a Salt master and at least one Salt minion. You can follow the first few steps of our Getting Started with Salt - Basic Installation and Setup guide.

Note
The steps in this guide require root privileges. Be sure to run the steps below with the sudo prefix. For more information on privileges, see our Users and Groups guide.

Prepare Salt

The files created in the following steps will be located in the /srv/salt directory. If you have changed Salt’s default file_roots configuration, use that directory location instead.

  1. Begin by creating the /srv/salt directory if it does not already exist. This is where you will place your top file and your Salt state file:

    mkdir /srv/salt
    
  2. Create a top file in /srv/salt which will be Salt’s point of entry for our Salt configuration:

    /srv/salt/top.sls
    1
    2
    3
    
    base:
      '*':
        - weather

  3. Create a state file named weather.sls and instruct Salt to make sure our minions have PIP installed, as well as the required Python library.

    /srv/salt/weather.sls
    1
    2
    3
    4
    5
    6
    7
    
    python-pip:
      pkg.installed
    
    requests:
      pip.installed:
        - require:
          - pkg: python-pip
  4. Apply these state changes:

    salt '*' state.apply
    
  5. Finally, create the /srv/salt/_modules directory which will contain our execution module:

    mkdir /srv/salt/_modules
    

Create the Execution Module

  1. Create a file called weather.py in the /srv/salt/_modules directory, and add the following lines to set up Salt logging and import the requests module.

    /srv/salt/_modules/weather.py
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    import logging
    try:
        import requests
        HAS_REQUESTS = True
    except ImportError:
        HAS_REQUESTS = False
    
    log = logging.getLogger(__name__)
    
    . . .
  2. Add the __virtualname__ variable and the __virtual__ function.

    /srv/salt/_modules/weather.py
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    . . .
    
    __virtualname__ = 'weather'
    
    def __virtual__():
        '''
        Only load weather if requests is available
        '''
        if HAS_REQUESTS:
            return __virtualname__
        else:
            return False, 'The weather module cannot be loaded: requests package unavailable.'
    
    . . .

    The __virtual__ function either returns the module’s virtual name and loads the module, or returns False with an error string and the module is not loaded. The if HAS_REQUESTS conditional is tied to the try/except block created in the previous step through the use of the HAS_REQUESTS variable.

  3. Add the public get() function and the private _make_request() function:

    /srv/salt/_modules/weather.py
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
    . . .
    
    def get(signs=None):
        '''
        Gets the Current Weather
    
        CLI Example::
    
            salt minion weather.get KPHL
    
        This module also accepts multiple values in a comma separated list::
    
            salt minion weather.get KPHL,KACY
        '''
        log.debug(signs)
        return_value = {}
        signs = signs.split(',')
        for sign in signs:
            return_value[sign] = _make_request(sign)
        return return_value
    
    def _make_request(sign):
        '''
        The function that makes the request for weather data from the National Weather Service.
        '''
        request = requests.get('https://api.weather.gov/stations/{}/observations/current'.format(sign))
        conditions = {
            "description:": request.json()["properties"]["textDescription"],
            "temperature": round(request.json()["properties"]["temperature"]["value"], 1)
        }
        return conditions

    There are two functions in this step. The get() function accepts one or more weather station call signs as a comma separated list. It calls _make_request() to make the HTTP request and returns a text description of the current weather and the temperature.

    It’s important to note that by adding an underscore to the beginning of the _make_request() function it becomes a private function, which means it is not directly accessible through the Salt command line or a state file.

    The complete file looks like this:

    /srv/salt/_modules/weather.py
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    
    import logging
    try:
        import requests
        HAS_REQUESTS = True
    except ImportError:
        HAS_REQUESTS = False
    
    log = logging.getLogger(__name__)
    
    __virtual_name__ = 'weather'
    
    def __virtual__():
        '''
        Only load weather if requests is available
        '''
        if HAS_REQUESTS:
            return __virtual_name__
        else:
            return False, 'The weather module cannot be loaded: requests package unavailable.'
    
    
    def get(signs=None):
        '''
        Gets the Current Weather
    
        CLI Example::
    
            salt minion weather.get KPHL
    
        This module also accepts multiple values in a comma seperated list::
    
            salt minion weather.get KPHL,KACY
        '''
        log.debug(signs)
        return_value = {}
        signs = signs.split(',')
        for sign in signs:
            return_value[sign] = _make_request(sign)
        return return_value
    
    def _make_request(sign):
        '''
        The function that makes the request for weather data from the National Weather Service.
        '''
        request = requests.get('https://api.weather.gov/stations/{}/observations/current'.format(sign))
        conditions = {
            "description:": request.json()["properties"]["textDescription"],
            "temperature": round(request.json()["properties"]["temperature"]["value"], 1)
        }
        return conditions

Run the Execution Module

  1. To run the execution module, you need to first sync it to your minions. To do this, you can call a highstate with state.apply, which will also try to apply the state changes you specified earlier in the weather.sls state file. Since the weather.sls state was already applied in the Preparing Salt section, use the saltutil.sync_modules function:

    salt '*' saltutil.sync_modules
    
  2. Run the execution module on your Salt master:

    salt '*' weather.get KPHL
    

    You should see an output like the following:

      
    salt-minion:
    ----------
    KPHL:
        ----------
        description::
            Cloudy
        temperature:
            17.2
    
    

  3. Alternatively, you can run the Salt execution module locally on your Salt minion by entering the following:

    salt-call weather.get KVAY,KACY
    

    You should get an output like the following:

      
    local:
        ----------
        KACY:
            ----------
            description::
                Cloudy
            temperature:
                18.9
        KVAY:
            ----------
            description::
                Cloudy
            temperature:
                16.7
    
    

You have now successfully created and installed a Salt execution module.

More Information

You may wish to consult the following resources for additional information on this topic. While these are provided in the hope that they will be useful, please note that we cannot vouch for the accuracy or timeliness of externally hosted materials.

Join our Community

Find answers, ask questions, and help others.

comments powered by Disqus

This guide is published under a CC BY-ND 4.0 license.