On Mon, Oct 15, 2012 at 08:18:36PM +0200, Jakov Sosic wrote:
> On 10/15/2012 07:23 PM, Stefan Schulte wrote:
> 
> > The fact that your exists? method does not really answer the question if
> > a resource is present or absent is a bit strange. And inside the create
> > method you are basically reimplementing properties with parameters. If
> > something has to be checked for correctness it should be a property.
> > Otherwise it is a parameter. Like the service resource: enable is a
> > property because it can be out of sync. hasstatus is a parameter because
> > it cannot be out of sync but only changes the behaviour of the provider
> 
> OK, I've figured that out through this conversation...
> 
> Now this is somewhat fixed code:
> 
> http://pastebin.com/q0TBX4KB
> 
> I've moved some params to properties.
> 
> 
> > Your main concern against properties if I got you correctly was about
> > speed because puppet would run one query for each property. One way around
> > that is to implement a query method that will query all properties at once
> > and store them in a hash (@property_hash). Every get-method now check
> > if @property_hash[:some_property] does already exist and return that
> > value if it does or run the query method that would populate the
> > @property_hash hash.
> 
> That sounds interesting, and more important it seems to me that complete
> rewrite is not necessary in this case. Do you have some examples of this
> idea?
> 
> 
> > Another speed improvement is to implement an `instances` and `prefetch`
> > method. That has the benefit that puppet does "react" on such methods
> > if they are implemented:
> > 
> > * you are able to run "puppet resource cobblersystem" on the command
> >   line to get the current configuration of all systems (that depends on
> >   an instances classmethod)
> > * you can use the resources type to purge unmanaged systems
> > 
> >     resources { 'cobblersystem':
> >       purge => true
> >     }
> > * the prefetch method is automatically called by puppet if implemented
> >   to create provider instances
> > * your get methods become trivial
> 
> Wow, sounds very interesting.
> 
> I would plea for possible examples :)

The instances method is a class method and has to return an array of
providers. So this often looks like this

    def self.instances
      systems = []
      my_fancy_command.each_line do |line|
        somehow_split_line_into_different_fields_like_name_and_interfaces
        systems << new(
          :name       => name,
          :interfaces => interfaces,
          :ensure     => :present
        )
      end
      systems
    end

One important thing: If you create a new provider instance you can pass
a hash (like I did in new(:name => name, :interfaces => interface)) and
this hash is stored in the member variable @property_hash of that new
provider.

An example of a simple instances method:
https://github.com/stschulte/puppet-rpmkey/blob/master/lib/puppet/provider/rpmkey/rpm.rb

The rpmkey type can make sure that a certain gpg key is imported into
rpm. To get the currently installed keys the provider runs

    rpm -q gpg-key

This command can either return with a non zero exit code (no packages
found) in case we have zero keys or it will print one line per key.
For each line a provider instance is added to the array that is finally
returned.

prefetch:
The prefetch method is called by puppet for each providerclass that
implements such a method (see lib/puppet/transaction.rb#prefetch). The
prefetch method is called with a hash of every resource that is defined
in the user's manifest (=every resource puppet should manage). The
hash will have the form resource[:name] as a key and resource as the
value. What the prefetch method can do now is create provider instances
and bind the provider instances to resources. A common prefetch method
that is also shown in the rpm provider for rpmkey:

    def self.prefetch(resources)
      instances.each do |prov|
        if resource = resources[prov.name]
          resource.provider = prov
        end
      end
    end

The prefetch method first calls instances that will return a list of
every key that is currently present. Then I check if that key is also
managed by puppet. If the lookup succeeds (the key is indeed managed by
puppet), I'll bind the provider to the resource. At this point the provider
instance already has @property_hash[:ensure] set, so when puppet later
handles the different rpmkey resources and asks exists? I can simply
return the cached value.

    def exists?
      get(:ensure) != :absent
    end

Note: get(:ensure) is implemented in lib/provider.rb as

    def get(param)
      @property_hash[param.intern] || :absent
    end

A more complex provider that has to manage multiple properties:
https://github.com/stschulte/puppetlabs-solaris/blob/feature/master/projects/lib/puppet/provider/project/projadd.rb

The instances method is more complicated here but it basically does the
same thing: return an array of provider instances. You'll notice that I
do not implement any method to get the current values of the properties.
The methodcall "mk_resource_methods" already creates these for me.
"mk_resource_methods" will create a get method for every property that
is essentially (if your property is named interfaces)

    def interfaces
      @property_hash[:interfaces] || :absent
    end

I hope that helps. An example of a query method can be found in the
package provider in puppet core. The package provider is a bit special
here because it does not trust its prefetched values. This is because
one resource can change the state of another resource:

    package { "mod_ssl": ensure => installed }
    package { "httpd": ensure => installed }

The ensure property of "httpd" can change in the middle of a puppetrun (if
mod_ssl is installed first httpd will be installed as a dependency)
so the prefetched value of :absent can be obsolete when puppet actually
handles the resource Package["httpd"]. For you cobblersystem type I do
not think that one resource may influence another resource right?

-Stefan

-- 
You received this message because you are subscribed to the Google Groups 
"Puppet Users" group.
To post to this group, send email to puppet-users@googlegroups.com.
To unsubscribe from this group, send email to 
puppet-users+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/puppet-users?hl=en.

Reply via email to