running a "cmd" if a file contains a regex

2,232 views
Skip to first unread message

Sitaram Chamarty

unread,
Jan 19, 2013, 2:41:21 PM1/19/13
to salt-...@googlegroups.com
Hello,

I am trying to run a cmd if a file contains a line. I got as far as
knowing there is a function called contains_regex() in the file
module, but am unable to tie that into the "cmd.run" state.

Are there any examples? Is this even possible? Or should I just do
the whole thing in the external command?

--
Sitaram

Mike Chesnut

unread,
Jan 19, 2013, 3:00:57 PM1/19/13
to salt-...@googlegroups.com
cmd.run takes an "onlyif" parameter, which is an arbitrary shell command to be run.  If it returns true (exit 0) the command will be run, otherwise it will not be.  Conveniently, grep works exactly this way... Will something like that work for you?

/your/command:
  cmd.run:
    - onlyif: grep <line> <file>



--
Sitaram

--



Sitaram Chamarty

unread,
Jan 19, 2013, 11:18:58 PM1/19/13
to salt-...@googlegroups.com
On Sun, Jan 20, 2013 at 1:30 AM, Mike Chesnut <mche...@gmail.com> wrote:
> cmd.run takes an "onlyif" parameter, which is an arbitrary shell command to
> be run. If it returns true (exit 0) the command will be run, otherwise it
> will not be. Conveniently, grep works exactly this way... Will something
> like that work for you?
>
> /your/command:
> cmd.run:
> - onlyif: grep <line> <file>

Thanks. I figured that sometime early this morning, but it forks bash
which then forks grep, and when 999 times out of 1000 the file is
"correct" it seems like a bit of a waste.

I'd like your (and others') opinion on how idiomatic this is:

{% if salt['file.contains_regex']('/tmp/flagfile', 'pattern') %}
/tmp/command-to-run args:
cmd.run
{% endif %}

Seems to work ok. I don't know what the implications of moving a test
to jinja are, but I figure it should be ok.

Sam Lai

unread,
Jan 19, 2013, 11:55:54 PM1/19/13
to salt-...@googlegroups.com
It depends on how your states are configured. The jinja template is
rendered before any of the states execute. Therefore if your
/tmp/flagfile only exists after a particular state is executed (say a
pkg.installed state), then it won't work reliably (if the package
isn't installed initially, your cmd.run state would not ever run
because it doesn't exist).

I'd argue the minimal extra time/resources needed to run grep is worth
it as it means Salt knows at runtime exactly what you want it to do.
> --
>
>

Sitaram Chamarty

unread,
Jan 20, 2013, 12:13:43 AM1/20/13
to salt-...@googlegroups.com
On Sun, Jan 20, 2013 at 10:25 AM, Sam Lai <samue...@gmail.com> wrote:
> It depends on how your states are configured. The jinja template is
> rendered before any of the states execute. Therefore if your

I was wondering about that. The examples I saw use grains in the
tests, so I assume grains *are* computed before any states execute?

> /tmp/flagfile only exists after a particular state is executed (say a
> pkg.installed state), then it won't work reliably (if the package
> isn't installed initially, your cmd.run state would not ever run
> because it doesn't exist).

cmd.run was one specific example. As a general case, should not the
'onlyif' and 'unless' be generalised in two directions? (1) make them
run a module function, (like file.contains_regex, or indeed anything
else) with supplied parameters instead of only being an external
command and (2) apply them to any state, not just cmd.run.

> I'd argue the minimal extra time/resources needed to run grep is worth
> it as it means Salt knows at runtime exactly what you want it to do.

Agreed.

I just can't figure out how to write a state that really just does a
file.contains_regex (or indeed any module function) so that some other
state can "watch" it.

That would be pretty cool!

Thomas S Hatch

unread,
Jan 20, 2013, 12:18:28 AM1/20/13
to salt-...@googlegroups.com
Using a module function is a good idea, you could use a cmd.wait that watches another state, maybe the state module "module" plus the cmd.wait state will pull this off for you.

Thomas S. Hatch  |  Founder, CTO


5272 South College Drive, Suite 301 | Murray, UT 84123



--



Sam Lai

unread,
Jan 20, 2013, 12:43:55 AM1/20/13
to salt-...@googlegroups.com
To add to Thomas' answer -

On 20 January 2013 16:13, Sitaram Chamarty <sita...@gmail.com> wrote:
> On Sun, Jan 20, 2013 at 10:25 AM, Sam Lai <samue...@gmail.com> wrote:
>> It depends on how your states are configured. The jinja template is
>> rendered before any of the states execute. Therefore if your
>
> I was wondering about that. The examples I saw use grains in the
> tests, so I assume grains *are* computed before any states execute?

Anything specified with Jinja is processed before any states execute.
In fact, Salt itself never actually sees the Jinja code; all it sees
is the output once it has been processed by Jinja.

Sitaram Chamarty

unread,
Jan 20, 2013, 2:13:28 AM1/20/13
to salt-...@googlegroups.com
Heh!  If *you* are saying "maybe" it will pull this off, you'll forgive me for not trying.  I was hoping for some sample code to start things off :-)

How *do* you call a module function from a state without jinja templating?  And even if you do, how do you specify a "returner" inside a state declaration?

--
 
 



--
Sitaram

Thomas S Hatch

unread,
Jan 20, 2013, 2:16:50 AM1/20/13
to salt-...@googlegroups.com
I don't need to post sample code, it is in the docs, and *I* say maybe because I am never sure exactly what your end goal is:

Thomas S. Hatch  |  Founder, CTO


5272 South College Drive, Suite 301 | Murray, UT 84123





--
Sitaram

--
 
 

Sitaram Chamarty

unread,
Jan 20, 2013, 4:24:12 AM1/20/13
to salt-...@googlegroups.com
On Sun, Jan 20, 2013 at 12:46 PM, Thomas S Hatch <that...@gmail.com> wrote:
>
> I don't need to post sample code, it is in the docs, and *I* say maybe because I am never sure exactly what your end goal is:
> http://docs.saltstack.org/en/latest/ref/states/all/salt.states.module.html#module-salt.states.module

Fair enough.

Here's my end goal in this thread:

* check if file '/tmp/flagf' contains_regex 'foo'
* if it doesn't, then do nothing
* if it does, run a given command ('echo hello' will do for our purposes).

Solutions seen so far were:

* cmd.run with an 'onlyif' that calls grep. This is fine, but I am
trying to avoid the bash/grep forks, even if only for academic
interest for now (i.e., it's not hurting me but I would like to learn
perhaps more elegant ways to do this)
* wrapping the whole thing in a jinja "if". This, as Sam pointed
out, is subject to failure if the condition to be tested was expected
to be setup by an earlier state, because jinja processing happens
before that.

I am looking for a way to make that test happen within salt (using
file.contains_regex) and condition the cmd.run on that.

Here's the code I tried so far, but it always runs the "echo hello",
regardless of whether the file contains the pattern or not.

Even if you don't need to post code, perhaps you could tell me what I
am missing here?

grep:
module.run:
- name: file.contains_regex
- path: /tmp/flagf
- regex: foo

say_hello:
cmd.wait:
- name: echo hello
- cwd: /
- watch:
- module: grep

Here's the output when the file /tmp/flagf contains the pattern "foo":

----------
State: - module
Name: file.contains_regex
Function: run
Result: True
Comment: Module function file.contains_regex executed
Changes: ret: True

----------
State: - cmd
Name: echo hello
Function: wait
Result: True
Comment: Command "echo hello" run
Changes: pid: 17191
retcode: 0
stderr:
stdout: hello

and here's the output when the file does not contain the text:

----------
State: - module
Name: file.contains_regex
Function: run
Result: True
Comment: Module function file.contains_regex executed
Changes: ret: False

----------
State: - cmd
Name: echo hello
Function: wait
Result: True
Comment: Command "echo hello" run
Changes: pid: 17236
retcode: 0
stderr:
stdout: hello

As you can see, the "ret:" dutifully changes from True to False, but
the "Result" is always True. How do I make it reflect the "ret"?

sitaram

Thomas S Hatch

unread,
Jan 21, 2013, 12:07:46 AM1/21/13
to salt-...@googlegroups.com
First off, I should apologize for my short responses, hopefully I can answer this more cleanly. I should be more careful to not to cause issues by not being more polite at the end of a long day!

The problem here is that the module.run function currently always returns changes because it always adds the "ret" data to changes. Given your situation I think that it is easy to say that modules which are meant to return a true or false should reflect this in the changes dict. Execution module functions do not have mandatory return values, as they are meant to create a generic api.

With this data in mind though I think it makes sense that functions that return a false value should, by that nature, reflect nothing in changes, and therefore not trigger the watcher. So I have made a minor modification to the module state to reflect this:

I will get this into 0.12.1 which should be released in a day or two.

Your sls formulas are sound and make sense, and are very well thought out.



Thomas S. Hatch  |  Founder, CTO


5272 South College Drive, Suite 301 | Murray, UT 84123



sitaram

--



Sitaram Chamarty

unread,
Jan 21, 2013, 9:54:32 AM1/21/13
to salt-...@googlegroups.com
On Mon, Jan 21, 2013 at 10:37 AM, Thomas S Hatch <that...@gmail.com> wrote:
First off, I should apologize for my short responses, hopefully I can answer this more cleanly. I should be more careful to not to cause issues by not being more polite at the end of a long day!

No problem.  Anyway it's not like I've never done that myself :-)
 
The problem here is that the module.run function currently always returns changes because it always adds the "ret" data to changes. Given your situation I think that it is easy to say that modules which are meant to return a true or false should reflect this in the changes dict. Execution module functions do not have mandatory return values, as they are meant to create a generic api.

With this data in mind though I think it makes sense that functions that return a false value should, by that nature, reflect nothing in changes, and therefore not trigger the watcher. So I have made a minor modification to the module state to reflect this:

I will get this into 0.12.1 which should be released in a day or two.

Thanks; appreciate it!

regards

sitaram


 

--
 
 



--
Sitaram
Reply all
Reply to author
Forward
0 new messages