diff --git a/lib/puppet/parser/resource.rb b/lib/puppet/parser/resource.rb index 0802dd882..7ea85dded 100644 --- a/lib/puppet/parser/resource.rb +++ b/lib/puppet/parser/resource.rb @@ -1,348 +1,348 @@ # A resource that we're managing. This handles making sure that only subclasses # can set parameters. class Puppet::Parser::Resource require 'puppet/parser/resource/param' require 'puppet/parser/resource/reference' ResParam = Struct.new :name, :value, :source, :line, :file include Puppet::Util include Puppet::Util::MethodHelper include Puppet::Util::Errors include Puppet::Util::Logging attr_accessor :source, :line, :file, :scope, :rails_id attr_accessor :virtual, :override, :params, :translated attr_reader :exported attr_writer :tags # Proxy a few methods to our @ref object. [:builtin?, :type, :title].each do |method| define_method(method) do @ref.send(method) end end # Set up some boolean test methods [:exported, :translated, :override].each do |method| newmeth = (method.to_s + "?").intern define_method(newmeth) do self.send(method) end end def [](param) param = symbolize(param) if param == :title return self.title end if @params.has_key?(param) @params[param].value else nil end end # Add default values from our definition. def adddefaults defaults = scope.lookupdefaults(self.type) defaults.each do |name, param| unless @params.include?(param.name) self.debug "Adding default for %s" % param.name @params[param.name] = param end end end # Add any metaparams defined in our scope. This actually adds any metaparams # from any parent scope, and there's currently no way to turn that off. def addmetaparams Puppet::Type.eachmetaparam do |name| next if self[name] if val = scope.lookupvar(name.to_s, false) unless val == :undefined set Param.new(:name => name, :value => val, :source => scope.source) end end end end # Add any overrides for this object. def addoverrides overrides = scope.lookupoverrides(self) overrides.each do |over| self.merge(over) end overrides.clear end def builtin=(bool) @ref.builtin = bool end # Retrieve the associated definition and evaluate it. def evaluate if klass = @ref.definedtype finish() scope.deleteresource(self) return klass.evaluate(:scope => scope, :type => self.type, :name => self.title, :arguments => self.to_hash, :scope => self.scope, :exported => self.exported ) elsif builtin? devfail "Cannot evaluate a builtin type" else self.fail "Cannot find definition %s" % self.type end # if builtin? # devfail "Cannot evaluate a builtin type" # end # # unless klass = scope.finddefine(self.type) # self.fail "Cannot find definition %s" % self.type # end # # finish() # # scope.deleteresource(self) # # return klass.evaluate(:scope => scope, # :type => self.type, # :name => self.title, # :arguments => self.to_hash, # :scope => self.scope, # :exported => self.exported # ) ensure @evaluated = true end def exported=(value) if value @virtual = true @exported = value else @exported = value end end def evaluated? if defined? @evaluated and @evaluated true else false end end # Do any finishing work on this object, called before evaluation or # before storage/translation. def finish addoverrides() adddefaults() addmetaparams() end def initialize(options) options = symbolize_options(options) # Collect the options necessary to make the reference. refopts = [:type, :title].inject({}) do |hash, param| hash[param] = options[param] options.delete(param) hash end @params = {} tmpparams = nil if tmpparams = options[:params] options.delete(:params) end # Now set the rest of the options. set_options(options) @ref = Reference.new(refopts) requiredopts(:scope, :source) @ref.scope = self.scope if tmpparams tmpparams.each do |param| # We use the method here, because it does type-checking. set(param) end end end # Merge an override resource in. def merge(resource) # Some of these might fail, but they'll fail in the way we want. resource.params.each do |name, param| set(param) end end # This *significantly* reduces the number of calls to Puppet.[]. def paramcheck? unless defined? @@paramcheck @@paramcheck = Puppet[:paramcheck] end @@paramcheck end # Verify that all passed parameters are valid. This throws an error if there's # a problem, so we don't have to worry about the return value. def paramcheck(param) # Now make sure it's a valid argument to our class. These checks # are organized in order of commonhood -- most types, it's a valid argument # and paramcheck is enabled. if @ref.typeclass.validattr?(param) true elsif (param == "name" or param == "title") # always allow these true elsif paramcheck? self.fail Puppet::ParseError, "Invalid parameter '%s' for type '%s'" % [param.inspect, @ref.type] end end # A temporary occasion, until I get paths in the scopes figured out. def path to_s end # Return the short version of our name. def ref @ref.to_s end # You have to pass a Resource::Param to this. def set(param) # Because definitions are now parse-time, I can paramcheck immediately. paramcheck(param.name) if current = @params[param.name] # XXX Should we ignore any settings that have the same values? if param.source.child_of?(current.source) # Replace it, keeping all of its info. @params[param.name] = param else if Puppet[:trace] puts caller end fail Puppet::ParseError, "Parameter %s is already set on %s by %s" % [param.name, self.to_s, param.source] end else if self.source == param.source or param.source.child_of?(self.source) @params[param.name] = param else fail Puppet::ParseError, "Only subclasses can set parameters" end end end # Store our object as a Rails object. We need the host object we're storing it # with. def store(host) args = {} #FIXME: support files/lines, etc. #%w{type title tags file line exported}.each do |param| %w{type title exported}.each do |param| if value = self.send(param) args[param] = value end end # 'type' isn't a valid column name, so we have to use something else. args = symbolize_options(args) - #args[:type] = args[:type] - #args.delete(:type) + args[:restype] = args[:type] + args.delete(:type) # Let's see if the object exists - if obj = host.resources.find_by_type_and_title(self.type, self.title) + if obj = host.resources.find_by_restype_and_title(self.type, self.title) # We exist args.each do |param, value| obj[param] = value end else # Else create it anew obj = host.resources.build(args) end # Either way, now add our parameters @params.each do |name, param| param.store(obj) end return obj end #def tags # unless defined? @tags # @tags = scope.tags # @tags << self.type # end # @tags #end def to_hash @params.inject({}) do |hash, ary| param = ary[1] hash[param.name] = param.value hash end end def to_s self.ref end # Translate our object to a transportable object. def to_trans unless builtin? devfail "Tried to translate a non-builtin resource" end return nil if virtual? # Now convert to a transobject obj = Puppet::TransObject.new(@ref.title, @ref.type) to_hash.each do |p, v| if v.is_a?(Reference) v = v.to_ref elsif v.is_a?(Array) v = v.collect { |av| if av.is_a?(Reference) av = av.to_ref end av } end obj[p.to_s] = v end obj.file = self.file obj.line = self.line #obj.tags = self.tags return obj end def virtual? self.virtual end end # $Id$ diff --git a/lib/puppet/rails/database/schema.rb b/lib/puppet/rails/database/schema.rb index 9151bee46..609d6abdd 100644 --- a/lib/puppet/rails/database/schema.rb +++ b/lib/puppet/rails/database/schema.rb @@ -1,64 +1,64 @@ class Puppet::Rails::Schema def self.init ActiveRecord::Schema.define do create_table :resources do |t| t.column :title, :string, :null => false - t.column :type, :string + t.column :restype, :string t.column :host_id, :integer t.column :source_file_id, :integer t.column :exported, :boolean end create_table :source_files do |t| t.column :filename, :string t.column :path, :string end create_table :puppet_classes do |t| t.column :name, :string t.column :host_id, :integer t.column :source_file_id, :integer end create_table :hosts do |t| t.column :name, :string, :null => false t.column :ip, :string t.column :connect, :date #Use updated_at to automatically add timestamp on save. t.column :updated_at, :date t.column :source_file_id, :integer end create_table :fact_names do |t| t.column :name, :string, :null => false t.column :host_id, :integer, :null => false end create_table :fact_values do |t| t.column :value, :string, :null => false t.column :fact_name_id, :integer, :null => false end create_table :param_values do |t| t.column :value, :string, :null => false t.column :param_name_id, :integer, :null => false end create_table :param_names do |t| t.column :name, :string, :null => false t.column :resource_id, :integer end create_table :tags do |t| t.column :name, :string end create_table :taggings do |t| t.column :tag_id, :integer t.column :taggable_id, :integer t.column :taggable_type, :string end end end end diff --git a/lib/puppet/rails/host.rb b/lib/puppet/rails/host.rb index 55b33621a..fd0642722 100644 --- a/lib/puppet/rails/host.rb +++ b/lib/puppet/rails/host.rb @@ -1,138 +1,127 @@ require 'puppet/rails/resource' class Puppet::Rails::Host < ActiveRecord::Base has_many :fact_values, :through => :fact_names has_many :fact_names belongs_to :puppet_classes has_many :source_files has_many :resources, :include => [ :param_names, :param_values ] acts_as_taggable def facts(name) if fv = self.fact_values.find(:first, :conditions => "fact_names.name = '#{name}'") return fv.value else return nil end end # If the host already exists, get rid of its objects def self.clean(host) if obj = self.find_by_name(host) obj.rails_objects.clear return obj else return nil end end # Store our host in the database. def self.store(hash) unless hash[:name] raise ArgumentError, "You must specify the hostname for storage" end create = true args = {} if hash[:facts].include?("ipaddress") args[:ip] = hash[:facts]["ipaddress"] end host = nil Puppet::Util.benchmark(:info, "Found/created host") do host = self.find_or_create_by_name(hash[:facts]["hostname"], args) end Puppet::Util.benchmark(:info, "Converted facts") do hash[:facts].each do |name, value| if create fn = host.fact_names.find_or_create_by_name(name) fv = fn.fact_values.find_or_create_by_value(value) else fn = host.fact_names.find_by_name(name) || host.fact_names.new(:name => name) unless fv = fn.fact_values.find_by_value(value) fn.fact_values << fn.fact_values.new(:value => value) end end host.fact_names << fn end end unless hash[:resources] raise ArgumentError, "You must pass resources" end - typenames = [] - Puppet::Type.loadall - Puppet::Type.eachtype do |type| - typenames << type.name.to_s - end - Puppet::Util.benchmark(:info, "Converted resources") do hash[:resources].each do |resource| resargs = resource.to_hash.stringify_keys - - if typenames.include?(resource.type) - rtype = "Puppet#{resource.type.to_s.capitalize}" - end - if create - res = host.resources.find_or_create_by_type_and_title(rtype, resource[:title]) + res = host.resources.find_or_create_by_restype_and_title(resource[:type], resource[:title]) else - unless res = host.resources.find_by_type_and_title(rtype, resource[:title]) - res = host.resources.new(:type => rtype, :title => resource[:title]) + unless res = host.resources.find_by_restype_and_title(resource[:type], resource[:title]) + res = host.resources.new(:restype => resource[:type], :title => resource[:title]) host.resources << res end end resargs.each do |param, value| if create pn = res.param_names.find_or_create_by_name(param) pv = pn.param_values.find_or_create_by_value(value) else unless pn = res.param_names.find_by_name(param) pn = res.param_names.new(:name => param) end unless pn.param_values.find_by_value(value) pn.param_values << pn.param_values.new(:value => value) end end res.param_names << pn end end end Puppet::Util.benchmark(:info, "Saved host to database") do host.save end return host end # Add all of our RailsObjects def addobjects(objects) objects.each do |tobj| params = {} tobj.each do |p,v| params[p] = v end args = {:ptype => tobj.type, :name => tobj.name} [:tags, :file, :line].each do |param| if val = tobj.send(param) args[param] = val end end robj = rails_objects.build(args) robj.addparams(params) if tobj.collectable robj.toggle(:collectable) end end end end # $Id$ diff --git a/lib/puppet/rails/resource.rb b/lib/puppet/rails/resource.rb index 68a784d9d..423b227ad 100644 --- a/lib/puppet/rails/resource.rb +++ b/lib/puppet/rails/resource.rb @@ -1,50 +1,44 @@ require 'puppet' require 'puppet/rails/lib/init' require 'puppet/rails/param_name' class Puppet::Rails::Resource < ActiveRecord::Base has_many :param_values, :through => :param_names has_many :param_names has_many :source_files belongs_to :hosts acts_as_taggable - Puppet::Type.loadall - Puppet::Type.eachtype do |type| - klass = Class.new(Puppet::Rails::Resource) - Object.const_set("Puppet%s" % type.name.to_s.capitalize, klass) - end - def parameters hash = {} self.param_values.find(:all).each do |pvalue| pname = self.param_names.find(:first) hash.store(pname.name, pvalue.value) end return hash end # Convert our object to a resource. Do not retain whether the object # is collectable, though, since that would cause it to get stripped # from the configuration. def to_resource(scope) hash = self.attributes - hash.delete("type") + hash["type"] = hash["restype"] + hash.delete("restype") hash.delete("host_id") hash.delete("source_file_id") hash.delete("id") hash.each do |p, v| hash.delete(p) if v.nil? end - hash[:type] = self.class.to_s.gsub(/Puppet/,'').downcase hash[:scope] = scope hash[:source] = scope.source obj = Puppet::Parser::Resource.new(hash) self.param_names.each do |pname| obj.set(pname.to_resourceparam(scope.source)) end return obj end end diff --git a/test/lib/puppettest/railstesting.rb b/test/lib/puppettest/railstesting.rb index baac6e03a..1d2d94863 100644 --- a/test/lib/puppettest/railstesting.rb +++ b/test/lib/puppettest/railstesting.rb @@ -1,46 +1,46 @@ module PuppetTest::RailsTesting Parser = Puppet::Parser AST = Puppet::Parser::AST include PuppetTest::ParserTesting def railsinit Puppet::Rails.init end def railsteardown if Puppet[:dbadapter] != "sqlite3" Puppet::Rails.teardown end end def railsresource(type = "file", title = "/tmp/testing", params = {}) railsteardown railsinit # We need a host for resources #host = Puppet::Rails::Host.new(:name => Facter.value("hostname")) # Now build a resource resources = [] - resources << mkresource(:type => type, :title => title, :exported => true, + resources << mkresource(:restype => type, :title => title, :exported => true, :params => params) # Now collect our facts facts = Facter.to_hash # Now try storing our crap host = nil assert_nothing_raised { host = Puppet::Rails::Host.store( :resources => resources, :facts => facts, :name => facts["hostname"] ) } # Now save the whole thing host.save end end # $Id$ diff --git a/test/rails/railsresource.rb b/test/rails/railsresource.rb index f16f925a8..36df881ca 100755 --- a/test/rails/railsresource.rb +++ b/test/rails/railsresource.rb @@ -1,68 +1,68 @@ #!/usr/bin/env ruby $:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/ require 'puppet' require 'puppet/rails' require 'puppettest' require 'puppettest/railstesting' require 'puppettest/resourcetesting' # Don't do any tests w/out this class if defined? ActiveRecord::Base class TestRailsResource < Test::Unit::TestCase include PuppetTest::RailsTesting include PuppetTest::ResourceTesting # Create a resource param from a rails parameter def test_to_resource railsinit # We need a host for resources host = Puppet::Rails::Host.new(:name => "myhost") # Now build a resource resource = host.resources.create( :title => "/tmp/to_resource", + :restype => "file", :exported => true) # For some reason the child class doesn't exist until after the resource is created. # Probably an issue with the dynamic class generation. - resource.type = "PuppetFile" resource.save # Now add some params {"owner" => "root", "mode" => "644"}.each do |param, value| pn = resource.param_names.find_or_create_by_name(param) pv = pn.param_values.find_or_create_by_value(value) resource.param_names << pn end # Now save the whole thing host.save # We need a scope interp, scope, source = mkclassframing # Find the new resource and include all it's parameters. resource = Puppet::Rails::Resource.find_by_id(resource.id, :include => [ :param_names, :param_values ]) # Now, try to convert our resource to a real resource res = nil assert_nothing_raised do res = resource.to_resource(scope) end assert_instance_of(Puppet::Parser::Resource, res) assert_equal("root", res[:owner]) assert_equal("644", res[:mode]) assert_equal("/tmp/to_resource", res.title) assert_equal(source, res.source) end end else $stderr.puts "Install Rails for Rails and Caching tests" end # $Id$