How to have "ensure" routines notify something changed

57 views
Skip to first unread message

Philip Brown

unread,
May 4, 2012, 2:08:29 PM5/4/12
to puppe...@googlegroups.com
I've looked through a few "ensure" providing .rb files,  and would like to verify something.

None of the ones I've looked at, seem to explicitly return any kind of value for "Hey, I updated something".
Nor do even may other kinds of utils that I look at. For example:

  def mode=(value)
    begin
      File.chmod(value.to_i(8), resource[:path])
     .. (and nothing else normal here)
     end

In OTHER code, however I see syntax such as
    return "Some user visible string here"

So, given the lack of documentation, I just wanted confirm that the "Right way" to log a change, is that style.
eg:

def exists?
   if ! is_it_there?
     return "Hey I made it for you"
   end
end

I dont see how to send some kind of message to the logs, "hey, something changed, but I'm fixing it".

Luke Kanies

unread,
May 4, 2012, 4:29:19 PM5/4/12
to puppe...@googlegroups.com
In all cases I can think of, Puppet will do the appropriate logging, so you only need to worry about extra information like debugging.

E.g., if you are using 'ensure' present, then when the transaction successfully calls 'ensure' on the provider, it will create an Event that has the property set to 'ensure', the previous_value set to 'absent', and the current_value set to 'present' (or something like that).  Then this Event calls the 'notice' log method with the appropriate log message.

So, your provider should focus 100% on function, and throwing errors where it can't do what it is supposed to, and the transaction should handle all logging and error management.

In case it's not clear, you should very rarely have a conditional like you do with the 'exists?' method above - return true if it's there, false if it's not, but don't return a string.  The system will treat the string as true, but that's probably not what you want.

Please let me know if that isn't clear.

Philip Brown

unread,
May 4, 2012, 4:46:01 PM5/4/12
to puppe...@googlegroups.com


On Friday, May 4, 2012 1:29:19 PM UTC-7, Luke Kanies wrote:
On May 4, 2012, at 11:08 AM, Philip Brown wrote:
....

I dont see how to send some kind of message to the logs, "hey, something changed, but I'm fixing it".
In all cases I can think of, Puppet will do the appropriate logging, so you only need to worry about extra information like debugging.

E.g., if you are using 'ensure' present, then when the transaction successfully calls 'ensure' on the provider, it will create an Event that has the property set to 'ensure', the previous_value set to 'absent', and the current_value set to 'present' (or something like that).  Then this Event calls the 'notice' log method with the appropriate log message.

So, your provider should focus 100% on function, and throwing errors where it can't do what it is supposed to, and the transaction should handle all logging and error management.

In case it's not clear, you should very rarely have a conditional like you do with the 'exists?' method above - return true if it's there, false if it's not, but don't return a string.  The system will treat the string as true, but that's probably not what you want.

Okay, so looks like i picked a bad example. Thank you for your informative stuff: "exists?" must return either "absent" or "present".
But.. what about create routines then?
What's the internal flow ? something like:
* puppet calls exists?
* if it returns absent, call create
* puppet calls exists? again to verify
* puppet decides what message to log?


Philip Brown

unread,
May 4, 2012, 5:03:59 PM5/4/12
to puppe...@googlegroups.com
Hmm, i guess I slightly misunderstood: my ensure routine should return false or true, i guess.

But I'm still missing info :(
In my trivial example below, the "ensure" routine gets called and prints out its debug line. But the other routines do not.
Instead, I see
debug: /Stage[main]//Sysprop[system/sac]: The container Class[Main] will propagate my refresh event
debug: Class[Main]: The container Stage[main] will propagate my refresh event

but nothing else relevant  happens :(


Puppet::Type.type(:sysprop).provide(:solaris) do
  desc "Provider for Solaris system properties"
  defaultfor :operatingsystem => :solaris

  def create
    Puppet.debug "sysprop.create called"
  end
  def destroy
    Puppet.debug "sysprop.destroy called"
  end
  def exists?
    Puppet.debug "sysprop.exists? has not been written yet"
    return false
  end
end

Philip Brown

unread,
May 4, 2012, 5:06:02 PM5/4/12
to puppe...@googlegroups.com


On Friday, May 4, 2012 2:03:59 PM UTC-7, Philip Brown wrote:

Puppet::Type.type(:sysprop).provide(:solaris) do
  desc "Provider for Solaris system properties"
  defaultfor :operatingsystem => :solaris

  def create
    Puppet.debug "sysprop.create called"
  end
  def destroy
    Puppet.debug "sysprop.destroy called"
  end
  def exists?
    Puppet.debug "sysprop.exists? has not been written yet"
    return false
  end
end


Sigh. sorry, forgot to also mention that manifest is sysprop { 
"system/sac":
        ensure => present,
        property => "stop/type",
        value => "method",
}

 

Dan Bode

unread,
May 5, 2012, 1:07:33 AM5/5/12
to puppe...@googlegroups.com
The main thing that Puppet really 'logs' is state transitions. (it also logs command on debug, but the thing it is really designe to do is describe system state transitions)

- it a resource exists and is ensurable,
- then resource can use the ensure property to describe if the thing should exist
- exists will be used to query if the thing currently exists 
  - true means it currently exists on the system
  - false indicates it does not
- if the resource was specified as being in the ensure state present, then if the thing does not exist, then create will be called to ensure that it is in the state of present

If this process occurs, the Puppet should log the state transition

      This resource was purged, now its present

 


--
You received this message because you are subscribed to the Google Groups "Puppet Developers" group.
To view this discussion on the web visit https://groups.google.com/d/msg/puppet-dev/-/hIaCGhTtRO4J.

To post to this group, send email to puppe...@googlegroups.com.
To unsubscribe from this group, send email to puppet-dev+...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/puppet-dev?hl=en.

Dan Bode

unread,
May 5, 2012, 1:08:28 AM5/5/12
to puppe...@googlegroups.com
On Fri, May 4, 2012 at 2:03 PM, Philip Brown <ph...@bolthole.com> wrote:
Hmm, i guess I slightly misunderstood: my ensure routine should return false or true, i guess.

But I'm still missing info :(
In my trivial example below, the "ensure" routine gets called and prints out its debug line. But the other routines do not.
Instead, I see
debug: /Stage[main]//Sysprop[system/sac]: The container Class[Main] will propagate my refresh event
debug: Class[Main]: The container Stage[main] will propagate my refresh event

 can you share the type code? Is it possible that you did not specify that the resource type was ensurable with the ensurable method? 


 
but nothing else relevant  happens :(


Puppet::Type.type(:sysprop).provide(:solaris) do
  desc "Provider for Solaris system properties"
  defaultfor :operatingsystem => :solaris

  def create
    Puppet.debug "sysprop.create called"
  end
  def destroy
    Puppet.debug "sysprop.destroy called"
  end
  def exists?
    Puppet.debug "sysprop.exists? has not been written yet"
    return false
  end
end

--
You received this message because you are subscribed to the Google Groups "Puppet Developers" group.
To view this discussion on the web visit https://groups.google.com/d/msg/puppet-dev/-/qW6qeTMEq1QJ.

Philip Brown

unread,
May 5, 2012, 9:42:31 AM5/5/12
to puppe...@googlegroups.com


On Friday, May 4, 2012 10:07:33 PM UTC-7, Dan Bode wrote:

..

The main thing that Puppet really 'logs' is state transitions. (it also logs command on debug, but the thing it is really designe to do is describe system state transitions)

- it a resource exists and is ensurable,
- then resource can use the ensure property to describe if the thing should exist
- exists will be used to query if the thing currently exists 
  - true means it currently exists on the system
  - false indicates it does not
- if the resource was specified as being in the ensure state present, then if the thing does not exist, then create will be called to ensure that it is in the state of present

If this process occurs, the Puppet should log the state transition

Okay, thanks for confirming that.

btw, I found solution to my "create not called" problem. I think it was that I had forgotten to force "ensure=present" in the manifest.
At any rate, that part is working now :-}

*Almost* done with this beastie!  Now I just have to force it to return "true" when things already look good!
ruby is wierd. sigh.   I have
@resource[:value]    claiming its value is "true"
and the output of a command, in variable 'output', claming its value is "true".
BUT...

    if output == @resource[:value]
      Puppet.debug "svcprop value is desired value. sysprop return true"
      return true
    end

is NOT returning true. "exists?" falling through and returning false. sighh...

Philip Brown

unread,
May 5, 2012, 10:48:50 AM5/5/12
to puppe...@googlegroups.com


On Saturday, May 5, 2012 6:42:31 AM UTC-7, Philip Brown wrote:


*Almost* done with this beastie!  Now I just have to force it to return "true" when things already look good!
ruby is wierd. sigh.   I have
@resource[:value]    claiming its value is "true"
and the output of a command, in variable 'output', claming its value is "true".
BUT...

    if output == @resource[:value]
      Puppet.debug "svcprop value is desired value. sysprop return true"
      return true
    end

is NOT returning true. "exists?" falling through and returning false. sighh...

bah. had to call .strip  on the output. Works!

Now the only major ugliness left, is that on successful trigger, the --verbose output is:
notice: /Stage[main]//Sysprop[network/smtp:sendmail]/ensure: created

I'd rather it said something different from "ensure: created" . A custom message would be nice.
But I suppose I dont have any control over that.

Luke Kanies

unread,
May 5, 2012, 4:38:08 PM5/5/12
to puppe...@googlegroups.com
It's great to hear you got it all working.  I agree that it can be difficult figuring out when things are truly equal - I recommend using something like:

Puppet.debug "property value is #{output.inspect}, should be #{@resource[:value].inspect}"

The 'inspect' calls will make sure you know exactly what you have - whether it's a string, symbol, boolean, etc.  You can still be confused with quoting sometimes, but it's usually much clearer this way.

You can set a custom message with the change_to_s method; see exec.rb for an example.

Philip Brown

unread,
May 5, 2012, 6:35:10 PM5/5/12
to puppe...@googlegroups.com


On Saturday, May 5, 2012 1:38:08 PM UTC-7, Luke Kanies wrote:

On Saturday, May 5, 2012 6:42:31 AM UTC-7, Philip Brown wrote:
...

Now the only major ugliness left, is that on successful trigger, the --verbose output is:
notice: /Stage[main]//Sysprop[network/smtp:sendmail]/ensure: created

I'd rather it said something different from "ensure: created" . A custom message would be nice.
But I suppose I dont have any control over that.
You can set a custom message with the change_to_s method; see exec.rb for an example.


Errr.. my copy of exec.rb does not have this.

(puppet 2.7.6)

judging by all the xxx_to_s methods in the type directory though, there's some interesting functionality here, that *really* needs to be documented :(

are a great start.. but seems like the provider page, is only half finished, as it were. It could really use information like this.

It also needs a bit of help from the point of view of someone coming in "cold" to those pages.
Statements like
"By default, you have to define all of your getter and setter methods."  is lacking information, in what exactly that means :-}

(Side comment: i wonder how I get "google groups' to turn off the fancy formatting in my posts... sigh...)

Hmm. I'm trying out
 def change_to_s(oldval,newval)
    Puppet.debug "old is '#{oldval}', newval is '#{newval}'"
  end

and it is not getting called

 

Philip Brown

unread,
May 5, 2012, 6:52:41 PM5/5/12
to puppe...@googlegroups.com


On Saturday, May 5, 2012 3:35:10 PM UTC-7, Philip Brown wrote:


judging by all the xxx_to_s methods in the type directory though, there's some interesting functionality here, that *really* needs to be documented :(

wow. yes, it reaaaaly does.
I ran across it in maillist.rb, as
      def change_to_s(current_value, newvalue)
        return "Purged #{resource}" if newvalue == :purged
        super
      end

But that really didnt tell me much of what the heck it is.  and it still wasnt getting triggered for my prog! :(
It took me digging around in the actual puppet core to realize that that routine would really be better named as

  change_to_state(oldstate,newstate)

!!


PS: oops, you didnt say which "exec.rb". provider/exec.rb does not have, but type/exec.rb does.

I tried moving it into my type file, but it still does not seem to get called.




Luke Kanies

unread,
May 5, 2012, 6:56:13 PM5/5/12
to puppe...@googlegroups.com
On May 5, 2012, at 3:52 PM, Philip Brown wrote:



On Saturday, May 5, 2012 3:35:10 PM UTC-7, Philip Brown wrote:


judging by all the xxx_to_s methods in the type directory though, there's some interesting functionality here, that *really* needs to be documented :(

wow. yes, it reaaaaly does.
I ran across it in maillist.rb, as
      def change_to_s(current_value, newvalue)
        return "Purged #{resource}" if newvalue == :purged
        super
      end

But that really didnt tell me much of what the heck it is.  and it still wasnt getting triggered for my prog! :(
It took me digging around in the actual puppet core to realize that that routine would really be better named as

  change_to_state(oldstate,newstate)

!!

The reason it's called 'change_to_s' is that most Ruby objects support a 'to_s' method that converts them to a string, and this is a means of overriding how a given change gets converted to a string.  It would normally go on the Change class, but that doesn't work for this case because of how transactions work (the types don't have a special Change subclass, and, um, they shouldn't).


PS: oops, you didnt say which "exec.rb". provider/exec.rb does not have, but type/exec.rb does.

I tried moving it into my type file, but it still does not seem to get called.

It needs to be inside the :ensure property - and yes, I meant type/exec.rb.

Philip Brown

unread,
May 5, 2012, 8:37:44 PM5/5/12
to puppe...@googlegroups.com
You lost me.
$ grep ensure type/exec.rb
$

 

Luke Kanies

unread,
May 5, 2012, 10:42:43 PM5/5/12
to puppe...@googlegroups.com
Sorry - I meant it needs to be in the definition of the relevant property, which in this case is 'ensure' (but in exec is 'returns').

Philip Brown

unread,
May 5, 2012, 11:21:23 PM5/5/12
to puppe...@googlegroups.com


On Saturday, May 5, 2012 7:42:43 PM UTC-7, Luke Kanies wrote:

Sorry - I meant it needs to be in the definition of the relevant property, which in this case is 'ensure' (but in exec is 'returns').

 
For those "following along at home", that apparently means that, rather than the nice simple (but apparently, OVERLY simplified)
single line of

"ensurable" 
in a type definition, I now have to do:

    ensurable do
      defaultvalues
      
      # This appears to be "change to state", for "ensurable"s.
      def change_to_s(oldstate,newstate)
        if ( oldstate == :absent && newstate == :present )
          return "set #{@resource[:property]} = #{@resource[:value]}"
        end
      end
    end 

This stuff may as well be black magic. it reaaally needs to be added to the "how to write your own type" documentation!! :(
It is not at all apparent, than a single line,
"ensurable" is some short cut for an expandable "property definition"

Similarly for the "defaultvalues" line. not particular understandable, but  "it just has to be there".

(At least I have C++/java experience, so I can guess how it works. but for someone else trying to learn how to write their own types, etc. .. yuck)


Luke Kanies

unread,
May 6, 2012, 2:44:42 AM5/6/12
to puppe...@googlegroups.com
I largely agree - for the very simple cases of the very complex cases, it works well enough, but for anything in between it's a bit rough.

I think the RAL is in drastic need of refactoring, but it's a large job with a larger backward compatibility problem and isn't directly necessary to get anything else done, so it never quite gets prioritized like I'd like.

Philip Brown

unread,
May 7, 2012, 12:30:12 PM5/7/12
to puppe...@googlegroups.com
Refactoring it is overly drastic. I think that improved documentation would mostly alleviate the issue.
 

Luke Kanies

unread,
May 8, 2012, 9:43:56 PM5/8/12
to puppe...@googlegroups.com
Good point.   I know the warts too well. :)

Can you open tickets for that lack of documentation?

Philip Brown

unread,
May 8, 2012, 10:36:01 PM5/8/12
to puppe...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages