Only change text line in file when file exists and grain value matches?

240 views
Skip to first unread message

Jibun no Kage

unread,
Oct 20, 2022, 3:35:48 PM10/20/22
to Salt-users
I know this works...

boot-config-file-exist:
  file.exists:
    - name: /boot/config.txt

# Enable 64 Bit?  Get Architecture Type?
{% if grains.cpuarch == 'aarch64' %}

boot-config-file-set-architecture:
  file.replace:
    - name: /boot/config.txt
    - pattern: arm_64bit=0
    - repl: arm_64bit=1
    - require:
      - file: boot-config-file-exist

{% endif %}

But was wondering if there was a way to do the using the require? Or match?  Or Test?  Say something like this...

boot-config-file-exist:
  file.exists:
    - name: /boot/config.txt

boot-config-file-get:
  file.managed:
    - name: /boot/config.txt
    - source: salt://os/common/boot/config.txt
    - backup: minion
    - require:
      - file: boot-config-file-exist

# Enable 64 Bit?  Get Architecture Type?

boot-config-file-set-architecture:
  file.replace:
    - name: /boot/config.txt
    - pattern: arm_64bit=0
    - repl: arm_64bit=1
    - require:
      - file: boot-config-file-exist
      - match:  grains.cpuarch == 'aarch64'

Or maybe...
      - test:  grains.cpuarch == 'aarch64'

Nothing wrong with the working method, but seems like the other method would be more user friendly?

If anyone is wondering what /boot/config.txt file is, it is specific to Pi OS (Raspbian) For Raspberry Pi hardware.  The boot configuration file instructs the core processor as to desired initial state when the kernel loads, enable/disable features, load kernel overlays, a bit like sysctl in typical Linux, in the specific example above.  Pi devices support also has a /boot/cmdline.txt which just about identical to kernel parameter specification at boot, or under grub configuration as  GRUB_CMDLINE_LINUX, if memory serves.

Phipps, Thomas

unread,
Oct 20, 2022, 4:01:54 PM10/20/22
to salt-...@googlegroups.com

so, first. you should never file.managed and file.replace in the same file. either file.managed OR file.replace. this is because they are mutually exclusive and will always end up registering as changing the file. the file.managed should handle the piece that is being replaced through templating.

that being said. yes you can use onlyif or unless. which puts the test into the state instead of makes it it’s own state.

https://docs.saltproject.io/en/latest/ref/states/requisites.html#unless
https://docs.saltproject.io/en/latest/ref/states/requisites.html#onlyif

something like the following

 boot-config-file-get:
  file.managed:
    - name: /boot/config.txt
    - source: salt://os/common/boot/config.txt
    - template: jinja
    - backup: minion
    - onlyif:
      - fun: file.file_exists
        arg: 
        - /boot/config.txt
      - fun: match.grain
        tgt: cpuarch:aarch64

https://docs.saltproject.io/en/3000/ref/modules/all/salt.modules.match.html#salt.modules.match.grain
https://docs.saltproject.io/en/latest/ref/modules/all/salt.modules.file.html#salt.modules.file.file_exists


--
You received this message because you are subscribed to the Google Groups "Salt-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to salt-users+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/salt-users/dbdcfb0b-5e28-490b-b5a1-3059f39a684en%40googlegroups.com.

Jibun no Kage

unread,
Oct 21, 2022, 4:29:45 PM10/21/22
to Salt-users
Two separate files?  So I can't manage the file and do a text replacement in same file because the result will always be a changed state?  How can I ensure then that 1) the file is deployed, and the file is changed to the correct architecture?  You example does not address the need for arm_64bit=0 or arm_64bit=1 as applicable.  If separate files, the order of execution can not be assumed, right?  I believe read somewhere in the documentation that the order of state files execution is not guaranteed.  So the existing file could be changed only to be replaced?

Also, a side question, is the explicit template as jinja required, that is the default, no?  Or it is just a best practice to be explicit?

Phipps, Thomas

unread,
Oct 21, 2022, 5:43:58 PM10/21/22
to salt-...@googlegroups.com

so, I never said anything about separate files. and don’t recommend that path either. I’m saying template the file so that you just have the file.managed without having the file.replace later. aka have the file.managed put it together correctly in the first place.

herre is a full example. of me both running the commands as well as all of the files involved. Now this isn’t a complete example of your exact information. but should be enough that you start to get the point.

root@salt00:/srv/salt/tests# salt-call state.apply tests.file
local:
----------
          ID: windows file test
    Function: file.managed
        Name: /tmp/test
      Result: True
     Comment: onlyif condition is false
     Started: 21:37:59.940536
    Duration: 2686.292 ms
     Changes:

Summary for local
------------
Succeeded: 1
Failed:    0
------------
Total states run:     1
Total run time:   2.686 s
root@salt00:/srv/salt/tests# touch /tmp/test
root@salt00:/srv/salt/tests# salt-call state.apply tests.file
local:
----------
          ID: windows file test
    Function: file.managed
        Name: /tmp/test
      Result: True
     Comment: File /tmp/test updated
     Started: 21:38:58.632514
    Duration: 2725.411 ms
     Changes:
              ----------
              diff:
                  ---
                  +++
                  @@ -0,0 +1,3 @@
                  +
                  +
                  +arm_64bit=0

Summary for local
------------
Succeeded: 1 (changed=1)
Failed:    0
------------
Total states run:     1
Total run time:   2.725 s
root@salt00:/srv/salt/tests# salt-call state.apply tests.file
local:
----------
          ID: windows file test
    Function: file.managed
        Name: /tmp/test
      Result: True
     Comment: File /tmp/test is in the correct state
     Started: 21:39:33.669573
    Duration: 3475.972 ms
     Changes:

Summary for local
------------
Succeeded: 1
Failed:    0
------------
Total states run:     1
Total run time:   3.476 s
root@salt00:/srv/salt/tests# cat /tmp/test

arm_64bit=0
root@salt00:/srv/salt/tests# cat file.sls
windows file test:
  file.managed:
    - name: /tmp/test
    - source: salt://tests/files/testfile.txt
    - template: jinja
    - onlyif:
      - fun: file.file_exists
        path: /tmp/test
root@salt00:/srv/salt/tests# cat files/testfile.txt
{% set arm_64bit=0 %}
{% if salt["match.grain"]('cpuarch:aarch64') %}
{% set arm_64bit=1 %}
{% endif %}
arm_64bit={{arm_64bit}}

Jibun no Kage

unread,
Oct 21, 2022, 7:25:42 PM10/21/22
to Salt-users
Thanks for example, that was very informative. 

Also, I thought about how I was implementing the entire effort.  I decided that having 1 single config.txt file across all architecture types was going to be a bit of a PITA, over time would likely have to have many individual tweaks,  Where have a base template per architecture type seemed like more complex initially but over time would be easier, especially if as I add architecture types, or Pi device alternates, which may be the same SoC processor but different configuration files, Banana Pi, Nano Pi, etc.  I already wrote a couple of custom grains to do some deeper hardware identification, so that will be a factor in future as well.  Thus, I was going to have to setup a more complex directory structure on the salt repo at some point anyway.  There are elements of this that remind me of when I setup an enterprise pxe/tftp boot infrastructure for stateless OS deployments, where I silo'd directory trees per architecture type.  This was of course be for Foreman and other deployment frameworks that make pxe boot OS deployment easier.

So I came with the following, the arm_64bit logic is now just part of the config.txt per architecture file, which reduces the work salt does via explicit state logic/scripting:

# Method 1 -  Not sure I like this method, given the shell requirement?
#boot-config-file-get:
#  file.managed:
#    - name: /boot/config.txt
#    - source: salt://os/{{grains['cpuarch']}}/boot/config.txt
#    - template: jinja
#    - backup: minion
#    - onlyif:
#        - test -f /boot/config.txt

# Method 2

boot-config-file-get:
  file.managed:
    - name: /boot/config.txt
    - source: salt://os/{{grains['cpuarch']}}/boot/config.txt

    - template: jinja
    - backup: minion
    - onlyif:
      - file: /boot/config.txt

I may need to adjust the above, given the example you provided of course.  One question...

    - onlyif:
      - file: /boot/config.txt

versus...

- onlyif:
  - fun: file.file_exists
    path: /boot/config.txt

Is there any significant difference between these two approaches?  Of course, in my first method, the test -f generates a shell, right?  So that would be clearly different.  But your example and my second method onlyif seem identical in concept?

You example of the explicit scripting at the end of your reply confirmed something I was going to focus on next, the part of all of this that will take explicit logic across all architecture types, is which 'dtoverlays' I have to load based on the unique hardware sensors that are present on each Pi device.  Another reason I started working on a few custom grains as well.  Thanks again for the assistance, greatly appreciated!

Phipps, Thomas

unread,
Oct 21, 2022, 7:45:20 PM10/21/22
to salt-...@googlegroups.com
so, the onlyif can take either a cmd. which is what the test -f /boot/config.txt is. and checks the ret code of that command to see if it returned true or false. but starting in version 3000 onlyif and unless also started being able to take a module which is what my example is. but yes in effect are the same concept.

the example that has just file: /boot/config.txt won't work. onlyif isn't require it isn't based off of state information. i was telling onlyif to run the execution module file.file_exists and have it return true or false based on the path given.



Jibun no Kage

unread,
Oct 21, 2022, 7:52:48 PM10/21/22
to Salt-users
Interesting, it seems to work, but i have not done a lot of testing with it as yet.  So it just is not creating any obvious fault.  Calling the explicit module based check makes sense, still like it better than test method, but as I type this, my years of bash scripting, are screaming in my ear... "No..." LOL.  Thanks again.

Jibun no Kage

unread,
Oct 21, 2022, 8:00:46 PM10/21/22
to Salt-users
I fired up a x86 VM, sure enough, it fails to work.  I believed you, definitely, just wanted to see exactly how it would be reported/act, with the broken file exists checking, salt of course tried to manage the file, when it should not.
Reply all
Reply to author
Forward
0 new messages