Tuesday December 12th, 2023 NCLUG Meeting

Aaron D. Johnson adj at fnord.greeley.co.us
Wed Dec 13 17:51:50 UTC 2023


Sean Reifschneider writes:
> So on the topic of Salt and YAML...  Months ago I showed off a "mini
> Ansible", which had some of the Ansible ideas for declarative system
> configuration, similar to what Salt does, though focused on a single
> computer or smaller (where Ansible and the like are focused on
> working with a fleet of machines).
>
> I came to realize that, despite working with YAML in Ansible for
> ~5-7 years now, I really despise YAML for this.

I picked up Salt as my preferred solution on account of it being my
team's tool of choice at work.  Overall, I am pretty happy with it.
In a large environment, I do especially enjoy it's fine grained
targetting options.  For instance, being able to say "run this chunk
of code against any system with an AMD processor" or "run the Ethernet
LLDP, ethtool, network bonding states against bare-metal machines
only."

I didn't go into this last night, but the times I have seen looping
and conditional constructs in Ansible playbooks, it was a less than
great experience.

The Salt alternative to this is that, while the state files are in
fact in YAML syntax, they are first fed through the Jinja templating
engine which allow the user to do something like this fragment:

    # Loop over each defined VRRP interface, drop a Jinja-templated
    # systemd unit file for it, then start and enable said service.
    {% for iface in pillar['vrrp']['interfaces'].keys() %}
    /usr/local/lib/systemd/system/vrrp-{{- iface -}}.service:
      file.managed:
        - source: salt://files/per-role/ipv4-router-vrrp/vrrp-interface.service.jinja
        - template: jinja
        - user: root
        - group: root
        - mode: 0644
        - backup: minion
        # These are all passed to the Jinja source file for expansion
        - iface: {{ iface }}
        - auth_secret: {{ pillar['vrrp']['interfaces'][iface]['auth-secret'] }}
        - virtual_ipv4: {{ pillar['vrrp']['interfaces'][iface]['virtual-ipv4'] }}
        - virtual_router_id: {{ pillar['vrrp']['interfaces'][iface]['virtual-router-id'] }}
        - virtual_router_priority: {{ pillar['vrrp']['interfaces'][iface]['virtual-router-priority'] }}
    
    vrrp-{{- iface -}}.service:
      service.running:
        - enable: True
    {% endfor %}

The curly braces introduce each Jinja element.  {% ... %} for logic
and {{ ... }} for simple variable substitution.  While this is not the
prettiest block of code ever written, I do find it not-too-terrible.

> Back at the beginning of October I started an experiment: What would
> it be like if Ansible had a Python syntax?  I've come up with
> something I'm fairly happy with that achieves this, though again at
> a much smaller scale.  One of the examples from the tutorial is:
>
>     from uplaybook import pyinfra, fs, core
>
>     def restart_apache():
>         pyinfra.systemd.service(service="apache2", restarted=True)
>
>     pyinfra.apt.packages(packages=["apache2", "libapache2-mod-uwsgi"])
>     for module_name in ["proxy", "uwsgi"]:
>         fs.ln(src="/etc/apache2/mods-available/{{ module_name }}.load",
>               path="/etc/apache2/mods-enabled",
>               symbolic=True).notify(restart_apache)

I'd much rather use this syntax to the equivalent Ansible playbook
YAML loop.  Am pretty well settled on Salt at this point in time,
though.  So many options out there...  :)

Thanks!

- Aaron


More information about the NCLUG mailing list