# Just quick mess-around to see what a DSL would look like. # # This is what the executable could look like: ##!/usr/bin/ruby # #require 'puppet' #require 'puppet/dsl' # #Puppet::DSL.import(ARGV[0]) # #bucket = Puppet::TransBucket.new #bucket.type = "top" #bucket.keyword = "class" # #Puppet::DSL.find_all do |name, sub| # sub.included #end.each do |name, sub| # bucket.push sub.export #end # #puts bucket.to_manifest # # And here's what an example config could look like: # ##!/usr/bin/ruby # # # require 'puppet' # require 'puppet/dsl' # # include Puppet::DSL # init() # # aspect :webserver do # file "/tmp/testone", :content => "yaytest" # # exec "testing", :command => "/bin/echo this is a test" # end # # aspect :other, :inherits => :webserver do # file "/tmp/testone", :mode => "755" # end # # acquire :other # # apply require 'puppet' # Provide the actual commands for acting like a language. module Puppet::DSL def aspect(name, options = {}, &block) Puppet::DSL::Aspect.new(name, options, &block) end def acquire(*names) names.each do |name| if aspect = Puppet::DSL::Aspect[name] unless aspect.evaluated? aspect.evaluate end else raise "Could not find aspect %s" % name end end end def apply bucket = export() catalog = bucket.to_catalog catalog.apply end def export objects = Puppet::DSL::Aspect.collect do |name, aspect| if aspect.evaluated? aspect.export end end.reject { |a| a.nil? }.flatten.collect do |obj| obj.to_trans end bucket = Puppet::TransBucket.new(objects) bucket.name = "top" bucket.type = "class" return bucket end def init unless Process.uid == 0 Puppet[:confdir] = File.expand_path("~/.puppet") Puppet[:vardir] = File.expand_path("~/.puppet/var") end Puppet[:user] = Process.uid Puppet[:group] = Process.gid Puppet::Util::Log.newdestination(:console) Puppet::Util::Log.level = :info end class Aspect Resource = Puppet::Parser::Resource include Puppet::Util include Puppet::DSL extend Puppet::Util extend Enumerable attr_accessor :parent, :name, :evaluated @aspects = {} @@objects = Hash.new do |hash, key| hash[key] = {} end # Create an instance method for every type Puppet::Type.loadall Puppet::Type.eachtype do |type| define_method(type.name) do |*args| newresource(type, *args) end end def self.[]=(name, aspect) name = symbolize(name) @aspects[name] = aspect end def self.[](name) name = symbolize(name) # Make sure there's always a main. This can get deleted in testing. if name == :main and ! @aspects[name] new(:main) {} end @aspects[name] end def self.clear @aspects.clear @@objects.clear end def self.delete(name) name = symbolize(name) if @aspects.has_key?(name) @aspects.delete(name) end end def self.each @aspects.each do |name, a| yield name, a end end def child_of?(aspect) unless aspect.is_a?(self.class) obj = self.class[aspect] unless obj raise "Could not find aspect %s" % aspect end aspect = obj end if self.parent if self.parent == aspect return true elsif self.parent.child_of?(aspect) return true else return false end else return false end end def evaluate if self.parent and ! self.parent.evaluated? self.parent.evaluate end unless evaluated? if defined? @block instance_eval(&@block) end @evaluated = true end end def evaluated? if self.evaluated true else false end end def export @resources.dup end def initialize(name, options = {}, &block) name = symbolize(name) @name = name if block @block = block end if pname = options[:inherits] if pname.is_a?(self.class) @parent = pname elsif parent = self.class[pname] @parent = parent else raise "Could not find parent aspect %s" % pname end end @resources = [] self.class[name] = self end def newresource(type, name, params = {}) if self.is_a?(Puppet::DSL::Aspect) source = self else source = Puppet::DSL::Aspect[:main] end unless obj = @@objects[type][name] obj = Resource.new :title => name, :type => type.name, :source => source, :scope => scope @@objects[type][name] = obj @resources << obj end params.each do |name, value| param = Resource::Param.new( :name => name, :value => value, :source => source ) obj.send(:set_parameter, param) end obj end def scope unless defined?(@scope) # Set the code to something innocuous; we just need the # scopes, not the interpreter. Hackish, but true. Puppet[:code] = " " @interp = Puppet::Parser::Interpreter.new require 'puppet/node' @node = Puppet::Node.new(Facter.value(:hostname)) if env = Puppet[:environment] and env == "" env = nil end @node.parameters = Facter.to_hash @compile = Puppet::Parser::Compiler.new(@node, @interp.send(:parser, env)) @scope = @compile.topscope end @scope end def type self.name end end end @aspects = {}