Conflicts:

	bin/puppetca
	lib/puppet/type/group.rb
	lib/puppet/type/tidy.rb
	lib/puppet/util/settings.rb

Also edited the following files so tests will pass:

       lib/puppet/type/component.rb
       spec/unit/ssl/certificate_request.rb
       spec/unit/type/computer.rb
       spec/unit/type/mcx.rb
       spec/unit/type/resources.rb
       spec/unit/util/settings.rb
       spec/unit/util/storage.rb
       test/ral/type/zone.rb
This commit is contained in:
Luke Kanies 2008-12-02 16:26:54 -06:00
Родитель f73e13e746 278bfe8301
Коммит 99a9b5a045
747 изменённых файлов: 7358 добавлений и 35782 удалений

Просмотреть файл

@ -8,6 +8,44 @@
set file paths to 'false' to disable the CRL.
0.24.x
Fixed #1695 - Solaris 10 zone provider doesn't properly handle unknown zone attributes in newer releases
Fixed #1776 - Trivial fix for gentoo service provider
Added Rake :ci namespace and CI tasks
Fixed #1767 - Minor fix to emacs mode
Fixed #1711 - fileserver test fails due to incorrect mocking
Fixed #1751 - Mac OS X DirectoryService nameservice provider support for
plist output and password hash fil
Fixed #1752 - Add an optional argument to Puppet::Util.execute to determine
whether stderr and stdout are combined in the output
Added versionable feature to the RPM provider
Fixed #1668 - puppetca can't clean unsigned certs
Moved RRD feature from util/metric.rb to feature/base.rb
Fixed #1735 and #1747 - Fixes to confine system
Fixed #1681 - Add filesystem type check to test for per-file SELinux context support
Fixed #1746 - Sync SELinux file attributes after file contents created/modified
Replaced SELInux calls to binaries with Ruby SELinux bindings
Fixed #1748 - Include spec directory in packages
Fixes #1672 - unsafe crontab handling in Solaris
Fixed #1718 - Added preseed to apt uninstall and purge
Fixed #1739 - Added uninstall functionality to yum provider
Fixed #1710 - Spurious output in test run
Fixed #1667 - Documentation should specify natural language regexs, not Regexp objects

Просмотреть файл

@ -24,6 +24,7 @@ project = Rake::RedLabProject.new("puppet") do |p|
'lib/puppet/**/*.rb',
'lib/puppet/**/*.py',
'test/**/*',
'spec/**/*',
'bin/**/*',
'ext/**/*',
'examples/**/*',
@ -136,7 +137,7 @@ desc "Run the specs under spec/"
task :spec do
require 'spec'
require 'spec/rake/spectask'
require 'rcov'
# require 'rcov'
Spec::Rake::SpecTask.new do |t|
# t.rcov = true
t.spec_opts = ['--format','s', '--loadby','mtime']
@ -149,6 +150,29 @@ task :unit do
sh "cd test; rake"
end
namespace :ci do
desc "Run the CI prep tasks"
task :prep do
require 'rubygems'
gem 'ci_reporter'
require 'ci/reporter/rake/rspec'
require 'ci/reporter/rake/test_unit'
ENV['CI_REPORTS'] = 'results'
end
desc "Run CI Unit tests"
task :unit => [:prep, 'ci:setup:testunit'] do
sh "cd test; rake test; exit 0"
end
desc "Run CI RSpec tests"
task :spec => [:prep, 'ci:setup:rspec'] do
sh "cd spec; rake all; exit 0"
end
end
desc "Send patch information to the puppet-dev list"
task :mail_patches do
if Dir.glob("00*.patch").length > 0

Просмотреть файл

@ -42,19 +42,10 @@ Autotest.add_hook :initialize do |at|
}
end
# a place for overrides when necessary.
class Autotest::PuppetRspec < Autotest::Rspec
# Autotest will look for spec commands in the following
# locations, in this order:
#
# * bin/spec
# * default spec bin/loader installed in Rubygems
# * our local vendor/gems/rspec/bin/spec
def spec_commands
[
File.join('vendor', 'gems', 'rspec', 'bin', 'spec') ,
File.join('bin', 'spec'),
File.join(Config::CONFIG['bindir'], 'spec')
]
end
def spec_commands
ENV["AUTOTEST"] = "true"
ENV["PATH"].split(":").collect { |dir| File.join(dir, "spec") }
end
end

Просмотреть файл

@ -8,25 +8,36 @@
#
# = Usage
#
# puppetdoc [-a|--all] [-h|--help] [-m|--mode <text|pdf|trac> [-r|--reference <[type]|configuration|..>]
# puppetdoc [-a|--all] [-h|--help] [-o|--outputdir <rdoc outputdir>] [-m|--mode <text|pdf|trac|rdoc>]
# [-r|--reference <[type]|configuration|..>] [manifest-file]
#
# = Description
#
# This command generates a restructured-text document describing all installed
# If mode is not 'rdoc', then this command generates a restructured-text document describing all installed
# Puppet types or all allowable arguments to puppet executables. It is largely
# meant for internal use and is used to generate the reference document
# available on the Reductive Labs web site.
#
# In 'rdoc' mode, this command generates an html RDoc hierarchy describing the manifests that
# are in 'manifestdir' and 'modulepath' configuration directives.
# The generated documentation directory is doc by default but can be changed with the 'outputdir' option.
#
# If the command is started with 'manifest-file' command-line arguments, puppetdoc generate a single
# manifest documentation that is output on stdout.
#
# = Options
#
# all::
# Output the docs for all of the reference types.
# Output the docs for all of the reference types. In 'rdoc' modes, this also outputs documentation for all resources
#
# help::
# Print this help message
#
# outputdir::
# Specifies the directory where to output the rdoc documentation in 'rdoc' mode.
#
# mode::
# Determine the output mode. Valid modes are 'text', 'trac', and 'pdf'. Note that 'trac' mode only works on Reductive Labs servers. The default mode is 'text'.
# Determine the output mode. Valid modes are 'text', 'trac', 'pdf' and 'rdoc'. Note that 'trac' mode only works on Reductive Labs servers. The default mode is 'text'. In 'rdoc' mode you must provide 'manifests-path'
#
# reference::
# Build a particular reference. Get a list of references by running +puppetdoc --list+.
@ -34,6 +45,10 @@
# = Example
#
# $ puppetdoc -r type > /tmp/type_reference.rst
# or
# $ puppetdoc --outputdir /tmp/rdoc --mode rdoc /path/to/manifests
# or
# $ puppetdoc /etc/puppet/manifests/site.pp
#
# = Author
#
@ -47,16 +62,24 @@
require 'puppet'
require 'puppet/util/reference'
require 'puppet/network/handler'
require 'puppet/util/rdoc'
require 'getoptlong'
result = GetoptLong.new(
[ "--all", "-a", GetoptLong::NO_ARGUMENT ],
[ "--list", "-l", GetoptLong::NO_ARGUMENT ],
[ "--format", "-f", GetoptLong::REQUIRED_ARGUMENT ],
[ "--mode", "-m", GetoptLong::REQUIRED_ARGUMENT ],
[ "--reference", "-r", GetoptLong::REQUIRED_ARGUMENT ],
[ "--help", "-h", GetoptLong::NO_ARGUMENT ]
)
options = [
[ "--all", "-a", GetoptLong::NO_ARGUMENT ],
[ "--list", "-l", GetoptLong::NO_ARGUMENT ],
[ "--format", "-f", GetoptLong::REQUIRED_ARGUMENT ],
[ "--mode", "-m", GetoptLong::REQUIRED_ARGUMENT ],
[ "--reference", "-r", GetoptLong::REQUIRED_ARGUMENT ],
[ "--help", "-h", GetoptLong::NO_ARGUMENT ],
[ "--outputdir", "-o", GetoptLong::REQUIRED_ARGUMENT ],
[ "--verbose", "-v", GetoptLong::NO_ARGUMENT ],
[ "--debug", "-d", GetoptLong::NO_ARGUMENT ]
]
# Add all of the config parameters as valid options.
Puppet.settings.addargs(options)
result = GetoptLong.new(*options)
debug = false
@ -66,8 +89,11 @@ options = {:references => [], :mode => :text, :format => :to_rest}
Reference = Puppet::Util::Reference
begin
unknown_args = []
result.each { |opt,arg|
case opt
when "--outputdir"
options[:outputdir] = arg
when "--all"
options[:all] = true
when "--format"
@ -78,7 +104,7 @@ begin
raise "Invalid output format %s" % arg
end
when "--mode"
if Reference.modes.include?(arg)
if Reference.modes.include?(arg) or arg.intern==:rdoc
options[:mode] = arg.intern
else
raise "Invalid output mode %s" % arg
@ -88,6 +114,10 @@ begin
exit(0)
when "--reference"
options[:references] << arg.intern
when "--verbose"
options[:verbose] = true
when "--debug"
options[:debug] = true
when "--help"
if Puppet.features.usage?
RDoc::usage && exit
@ -95,14 +125,52 @@ begin
puts "No help available unless you have RDoc::usage installed"
exit
end
else
unknown_args << {:opt => opt, :arg => arg }
end
}
# sole manifest documentation
if ARGV.size > 0
options[:mode] = :rdoc
manifest = true
end
# consume the remaining unknown options
# and feed them as settings, but only for rdoc mode
if options[:mode] == :rdoc and unknown_args.size > 0
unknown_args.each do |option|
# force absolute path for modulepath when passed on commandline
if option[:opt]=="--modulepath" or option[:opt] == "--manifestdir"
option[:arg] = option[:arg].split(':').collect { |p| File.expand_path(p) }.join(':')
end
Puppet.settings.handlearg(option[:opt], option[:arg])
end
end
rescue GetoptLong::InvalidOption => detail
$stderr.puts "Try '#{$0} --help'"
exit(1)
end
if options[:all]
if options[:mode] == :rdoc # rdoc mode
# hack to get access to puppetmasterd modulepath and manifestdir
Puppet[:name] = "puppetmasterd"
# Now parse the config
Puppet.parse_config
# Handle the logging settings.
if options[:debug] or options[:verbose]
if options[:debug]
Puppet::Util::Log.level = :debug
else
Puppet::Util::Log.level = :info
end
Puppet::Util::Log.newdestination(:console)
end
end
if options[:all] and options[:mode] != :rdoc
# Don't add dynamic references to the "all" list.
options[:references] = Reference.references.reject do |ref|
Reference.reference(ref).dynamic?
@ -114,6 +182,33 @@ if options[:references].empty?
end
case options[:mode]
when :rdoc # rdoc or sole manifest mode
exit_code = 0
files = []
unless manifest
files += Puppet[:modulepath].split(':').collect { |p| File.expand_path(p) }
files += Puppet[:manifestdir].split(':').collect { |p| File.expand_path(p) }
end
files += ARGV
Puppet.info "scanning: %s" % files.inspect
Puppet.settings.setdefaults("puppetdoc",
"document_all" => [false, "Document all resources"]
)
Puppet.settings[:document_all] = options[:all] || false
begin
if manifest
Puppet::Util::RDoc.manifestdoc(files)
else
Puppet::Util::RDoc.rdoc(options[:outputdir], files)
end
rescue => detail
if Puppet[:trace]
puts detail.backtrace
end
$stderr.puts "Could not generate documentation: %s" % detail
exit_code = 1
end
exit exit_code
when :trac
options[:references].each do |name|
section = Puppet::Util::Reference.reference(name) or raise "Could not find section %s" % name
@ -159,4 +254,3 @@ else
exit exit_code
end

Просмотреть файл

@ -0,0 +1,4 @@
mcx { '/Groups/mcx_dock':
ensure => 'absent',
content => 'absent'
}

Просмотреть файл

@ -0,0 +1,118 @@
mcx { '/Groups/mcx_dock':
ensure => 'present',
content => '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.dock</key>
<dict>
<key>AppItems-Raw</key>
<dict>
<key>state</key>
<string>always</string>
<key>upk</key>
<dict>
<key>mcx_input_key_names</key>
<array>
<string>AppItems-Raw</string>
</array>
<key>mcx_output_key_name</key>
<string>static-apps</string>
<key>mcx_remove_duplicates</key>
<true/>
</dict>
<key>value</key>
<array>
<dict>
<key>mcx_typehint</key>
<integer>1</integer>
<key>tile-data</key>
<dict>
<key>file-data</key>
<dict>
<key>_CFURLString</key>
<string>/Applications/Mail.app</string>
<key>_CFURLStringType</key>
<integer>0</integer>
</dict>
<key>file-label</key>
<string>Mail</string>
</dict>
<key>tile-type</key>
<string>file-tile</string>
</dict>
<dict>
<key>mcx_typehint</key>
<integer>1</integer>
<key>tile-data</key>
<dict>
<key>file-data</key>
<dict>
<key>_CFURLString</key>
<string>/Applications/Safari.app</string>
<key>_CFURLStringType</key>
<integer>0</integer>
</dict>
<key>file-label</key>
<string>Safari</string>
</dict>
<key>tile-type</key>
<string>file-tile</string>
</dict>
</array>
</dict>
<key>DocItems-Raw</key>
<dict>
<key>state</key>
<string>always</string>
<key>upk</key>
<dict>
<key>mcx_input_key_names</key>
<array>
<string>DocItems-Raw</string>
</array>
<key>mcx_output_key_name</key>
<string>static-others</string>
<key>mcx_remove_duplicates</key>
<true/>
</dict>
<key>value</key>
<array/>
</dict>
<key>MCXDockSpecialFolders-Raw</key>
<dict>
<key>state</key>
<string>always</string>
<key>upk</key>
<dict>
<key>mcx_input_key_names</key>
<array>
<string>MCXDockSpecialFolders-Raw</string>
</array>
<key>mcx_output_key_name</key>
<string>MCXDockSpecialFolders</string>
<key>mcx_remove_duplicates</key>
<true/>
</dict>
<key>value</key>
<array/>
</dict>
<key>contents-immutable</key>
<dict>
<key>state</key>
<string>always</string>
<key>value</key>
<false/>
</dict>
<key>static-only</key>
<dict>
<key>state</key>
<string>always</string>
<key>value</key>
<false/>
</dict>
</dict>
</dict>
</plist>
'
}

125
examples/mcx_dock_full.pp Normal file
Просмотреть файл

@ -0,0 +1,125 @@
# Mac MCX Test
computer { "localhost": }
mcx {
"mcx_dock":
ensure => "present",
ds_type => "group",
ds_name => "mcx_dock",
content => '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.dock</key>
<dict>
<key>AppItems-Raw</key>
<dict>
<key>state</key>
<string>always</string>
<key>upk</key>
<dict>
<key>mcx_input_key_names</key>
<array>
<string>AppItems-Raw</string>
</array>
<key>mcx_output_key_name</key>
<string>static-apps</string>
<key>mcx_remove_duplicates</key>
<true/>
</dict>
<key>value</key>
<array>
<dict>
<key>mcx_typehint</key>
<integer>1</integer>
<key>tile-data</key>
<dict>
<key>file-data</key>
<dict>
<key>_CFURLString</key>
<string>/Applications/Mail.app</string>
<key>_CFURLStringType</key>
<integer>0</integer>
</dict>
<key>file-label</key>
<string>Mail</string>
</dict>
<key>tile-type</key>
<string>file-tile</string>
</dict>
<dict>
<key>mcx_typehint</key>
<integer>1</integer>
<key>tile-data</key>
<dict>
<key>file-data</key>
<dict>
<key>_CFURLString</key>
<string>/Applications/Safari.app</string>
<key>_CFURLStringType</key>
<integer>0</integer>
</dict>
<key>file-label</key>
<string>Safari</string>
</dict>
<key>tile-type</key>
<string>file-tile</string>
</dict>
</array>
</dict>
<key>DocItems-Raw</key>
<dict>
<key>state</key>
<string>always</string>
<key>upk</key>
<dict>
<key>mcx_input_key_names</key>
<array>
<string>DocItems-Raw</string>
</array>
<key>mcx_output_key_name</key>
<string>static-others</string>
<key>mcx_remove_duplicates</key>
<true/>
</dict>
<key>value</key>
<array/>
</dict>
<key>MCXDockSpecialFolders-Raw</key>
<dict>
<key>state</key>
<string>always</string>
<key>upk</key>
<dict>
<key>mcx_input_key_names</key>
<array>
<string>MCXDockSpecialFolders-Raw</string>
</array>
<key>mcx_output_key_name</key>
<string>MCXDockSpecialFolders</string>
<key>mcx_remove_duplicates</key>
<true/>
</dict>
<key>value</key>
<array/>
</dict>
<key>contents-immutable</key>
<dict>
<key>state</key>
<string>always</string>
<key>value</key>
<false/>
</dict>
<key>static-only</key>
<dict>
<key>state</key>
<string>always</string>
<key>value</key>
<false/>
</dict>
</dict>
</dict>
</plist>
'
}

Просмотреть файл

@ -0,0 +1,9 @@
# Mac MCX Test
computer { "localhost": }
mcx {
"/Groups/mcx_dock":
ensure => "present",
content => 'invalid plist'
}

118
examples/mcx_nogroup.pp Normal file
Просмотреть файл

@ -0,0 +1,118 @@
mcx { '/Groups/doesnotexist':
ensure => 'present',
content => '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.dock</key>
<dict>
<key>AppItems-Raw</key>
<dict>
<key>state</key>
<string>always</string>
<key>upk</key>
<dict>
<key>mcx_input_key_names</key>
<array>
<string>AppItems-Raw</string>
</array>
<key>mcx_output_key_name</key>
<string>static-apps</string>
<key>mcx_remove_duplicates</key>
<true/>
</dict>
<key>value</key>
<array>
<dict>
<key>mcx_typehint</key>
<integer>1</integer>
<key>tile-data</key>
<dict>
<key>file-data</key>
<dict>
<key>_CFURLString</key>
<string>/Applications/Mail.app</string>
<key>_CFURLStringType</key>
<integer>0</integer>
</dict>
<key>file-label</key>
<string>Mail</string>
</dict>
<key>tile-type</key>
<string>file-tile</string>
</dict>
<dict>
<key>mcx_typehint</key>
<integer>1</integer>
<key>tile-data</key>
<dict>
<key>file-data</key>
<dict>
<key>_CFURLString</key>
<string>/Applications/Safari.app</string>
<key>_CFURLStringType</key>
<integer>0</integer>
</dict>
<key>file-label</key>
<string>Safari</string>
</dict>
<key>tile-type</key>
<string>file-tile</string>
</dict>
</array>
</dict>
<key>DocItems-Raw</key>
<dict>
<key>state</key>
<string>always</string>
<key>upk</key>
<dict>
<key>mcx_input_key_names</key>
<array>
<string>DocItems-Raw</string>
</array>
<key>mcx_output_key_name</key>
<string>static-others</string>
<key>mcx_remove_duplicates</key>
<true/>
</dict>
<key>value</key>
<array/>
</dict>
<key>MCXDockSpecialFolders-Raw</key>
<dict>
<key>state</key>
<string>always</string>
<key>upk</key>
<dict>
<key>mcx_input_key_names</key>
<array>
<string>MCXDockSpecialFolders-Raw</string>
</array>
<key>mcx_output_key_name</key>
<string>MCXDockSpecialFolders</string>
<key>mcx_remove_duplicates</key>
<true/>
</dict>
<key>value</key>
<array/>
</dict>
<key>contents-immutable</key>
<dict>
<key>state</key>
<string>always</string>
<key>value</key>
<false/>
</dict>
<key>static-only</key>
<dict>
<key>state</key>
<string>always</string>
<key>value</key>
<false/>
</dict>
</dict>
</dict>
</plist>
'
}

Просмотреть файл

@ -0,0 +1,4 @@
mcx { '/Groups/foobar':
ensure => 'absent',
content => 'absent'
}

Просмотреть файл

@ -226,7 +226,7 @@ of the initial include plus puppet-include-indent."
;; Semicolon ends a block for a resource when multiple resources
;; are defined in the same block, but try not to get the case of
;; a complete resource on a single line wrong.
((looking-at "^\\([^'\":\n]\\|\"[^\n\"]*\"\\|'[^\n']'\\)**;\\s-*$")
((looking-at "^\\([^'\":\n]\\|\"[^\n\"]*\"\\|'[^\n']*'\\)*;\\s-*$")
(setq cur-indent (- (current-indentation) puppet-indent-level))
(setq not-indented nil))

Просмотреть файл

@ -320,7 +320,7 @@ module Puppet
a proxy in front of the process or processes, since Mongrel cannot
speak SSL.",
:call_on_define => true, # Call our hook with the default value, so we always get the correct bind address set.
:hook => proc { |value| value == "webrick" ? parent[:bindaddress] = "0.0.0.0" : parent[:bindaddress] = "127.0.0.1" if parent[:bindaddress] == "" }
:hook => proc { |value| value == "webrick" ? Puppet.settings[:bindaddress] = "0.0.0.0" : Puppet.settings[:bindaddress] = "127.0.0.1" if Puppet.settings[:bindaddress] == "" }
}
)

Просмотреть файл

@ -25,3 +25,6 @@ Puppet.features.add :diff, :libs => %w{diff/lcs diff/lcs/hunk}
# We have augeas
Puppet.features.add(:augeas, :libs => ["augeas"])
# We have RRD available
Puppet.features.add(:rrd, :libs => ["RRDtool"])

Просмотреть файл

@ -24,7 +24,7 @@ class Puppet::Module
def self.templatepath(environment = nil)
dirs = Puppet.settings.value(:templatedir, environment).split(":")
dirs.select do |p|
p =~ /^#{File::SEPARATOR}/ && File::directory?(p)
File::directory?(p)
end
end

Просмотреть файл

@ -487,7 +487,7 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
return unless Puppet[:splay]
return if splayed?
time = rand(Integer(Puppet[:splaylimit]))
time = rand(Integer(Puppet[:splaylimit]) + 1)
Puppet.info "Sleeping for %s seconds (splay is enabled)" % time
sleep(time)
@splayed = true

Просмотреть файл

@ -73,7 +73,9 @@ module Puppet::Network
rescue Timeout::Error => detail
Puppet.err "Connection timeout calling %s.%s: %s" %
[namespace, method, detail.to_s]
raise XMLRPCClientError.new("Connection Timeout").set_backtrace(detail.backtrace)
error = XMLRPCClientError.new("Connection Timeout")
error.set_backtrace(detail.backtrace)
raise error
rescue => detail
if detail.message =~ /^Wrong size\. Was \d+, should be \d+$/
Puppet.warning "XMLRPC returned wrong size. Retrying."

Просмотреть файл

@ -12,8 +12,23 @@ class Puppet::Parser::AST
include Puppet::Util::Errors
include Puppet::Util::MethodHelper
include Puppet::Util::Docs
attr_accessor :line, :file, :parent, :scope
# don't fetch lexer comment by default
def use_docs
self.class.use_docs
end
# allow our subclass to specify they want documentation
class << self
attr_accessor :use_docs
def associates_doc
self.use_docs = true
end
end
# Does this ast object set something? If so, it gets evaluated first.
def self.settor?
if defined? @settor

Просмотреть файл

@ -6,6 +6,8 @@ class Puppet::Parser::AST
class CaseStatement < AST::Branch
attr_accessor :test, :options, :default
associates_doc
# Short-curcuit evaluation. Return the value of the statements for
# the first option that matches.
def evaluate(scope)

Просмотреть файл

@ -8,6 +8,8 @@ class Puppet::Parser::AST
class Collection < AST::Branch
attr_accessor :type, :query, :form
associates_doc
# We return an object that does a late-binding evaluation.
def evaluate(scope)
if self.query

Просмотреть файл

@ -18,6 +18,10 @@ class Puppet::Parser::AST
lval = @lval.safeevaluate(scope)
rval = @rval.safeevaluate(scope)
# convert to number if operands are number
lval = Puppet::Parser::Scope.number?(lval) || lval
rval = Puppet::Parser::Scope.number?(rval) || rval
# return result
unless @operator == '!='
lval.send(@operator,rval)

Просмотреть файл

@ -10,6 +10,8 @@ class Puppet::Parser::AST::Definition < Puppet::Parser::AST::Branch
attr_accessor :name
end
associates_doc
# The class name
@name = :definition

Просмотреть файл

@ -4,6 +4,9 @@ class Puppet::Parser::AST
# A separate ElseIf statement; can function as an 'else' if there's no
# test.
class Else < AST::Branch
associates_doc
attr_accessor :statements
def each

Просмотреть файл

@ -3,25 +3,17 @@ require 'puppet/parser/ast/branch'
class Puppet::Parser::AST
# An AST object to call a function.
class Function < AST::Branch
associates_doc
attr_accessor :name, :arguments
@settor = true
def evaluate(scope)
# We don't need to evaluate the name, because it's plaintext
args = @arguments.safeevaluate(scope)
return scope.send("function_" + @name, args)
end
def initialize(hash)
@ftype = hash[:ftype] || :rvalue
hash.delete(:ftype) if hash.include? :ftype
super(hash)
# Make sure it's a defined function
unless @fname = Puppet::Parser::Functions.function(@name)
unless @fname
raise Puppet::ParseError, "Unknown function %s" % @name
end
@ -42,6 +34,21 @@ class Puppet::Parser::AST
raise Puppet::DevError, "Invalid function type %s" % @ftype.inspect
end
# We don't need to evaluate the name, because it's plaintext
args = @arguments.safeevaluate(scope)
return scope.send("function_" + @name, args)
end
def initialize(hash)
@ftype = hash[:ftype] || :rvalue
hash.delete(:ftype) if hash.include? :ftype
super(hash)
@fname = Puppet::Parser::Functions.function(@name)
# Lastly, check the parity
end
end

Просмотреть файл

@ -4,6 +4,9 @@ require 'puppet/parser/ast/definition'
# in that each class is a singleton -- only one will exist for a given
# node.
class Puppet::Parser::AST::HostClass < Puppet::Parser::AST::Definition
associates_doc
@name = :class
# Are we a child of the passed class? Do a recursive search up our

Просмотреть файл

@ -3,6 +3,9 @@ require 'puppet/parser/ast/branch'
class Puppet::Parser::AST
# A basic 'if/elsif/else' statement.
class IfStatement < AST::Branch
associates_doc
attr_accessor :test, :else, :statements
def each

Просмотреть файл

@ -3,6 +3,9 @@ require 'puppet/parser/ast/hostclass'
# The specific code associated with a host. Nodes are annoyingly unlike
# other objects. That's just the way it is, at least for now.
class Puppet::Parser::AST::Node < Puppet::Parser::AST::HostClass
associates_doc
@name = :node
def initialize(options)

Просмотреть файл

@ -4,6 +4,9 @@ require 'puppet/parser/ast/resource_reference'
# builtin type.
class Puppet::Parser::AST
class Resource < AST::ResourceReference
associates_doc
attr_accessor :title, :type, :exported, :virtual
attr_reader :params

Просмотреть файл

@ -6,6 +6,8 @@ class Puppet::Parser::AST
class ResourceDefaults < AST::Branch
attr_accessor :type, :params
associates_doc
# As opposed to ResourceDef, this stores each default for the given
# object type.
def evaluate(scope)

Просмотреть файл

@ -4,6 +4,9 @@ class Puppet::Parser::AST
# Set a parameter on a resource specification created somewhere else in the
# configuration. The object is responsible for verifying that this is allowed.
class ResourceOverride < Resource
associates_doc
attr_accessor :object
attr_reader :params

Просмотреть файл

@ -3,6 +3,9 @@ require 'puppet/parser/ast/branch'
class Puppet::Parser::AST
# Define a variable. Stores the value in the current scope.
class VarDef < AST::Branch
associates_doc
attr_accessor :name, :value, :append
@settor = true

Просмотреть файл

@ -54,6 +54,20 @@ module Functions
end
end
# Remove a function added by newfunction
def self.rmfunction(name)
name = symbolize(name)
unless @functions.include? name
raise Puppet::DevError, "Function %s is not defined" % name
end
@functions.delete(name)
fname = "function_" + name.to_s
Puppet::Parser::Scope.send(:remove_method, fname)
end
# Determine if a given name is a function
def self.function(name)
name = symbolize(name)

Просмотреть файл

@ -0,0 +1,21 @@
Puppet::Parser::Functions::newfunction(:inline_template, :type => :rvalue, :doc =>
"Evaluate a template string and return its value. See `the templating docs
</trac/puppet/wiki/PuppetTemplating>`_ for more information. Note that
if multiple template strings are specified, their output is all concatenated
and returned as the output of the function.") do |vals|
require 'erb'
vals.collect do |string|
# Use a wrapper, so the template can't get access to the full
# Scope object.
wrapper = Puppet::Parser::TemplateWrapper.new(self)
begin
wrapper.result(string)
rescue => detail
raise Puppet::ParseError,
"Failed to parse inline template: %s" %
[detail]
end
end.join("")
end

Просмотреть файл

@ -9,10 +9,11 @@ Puppet::Parser::Functions::newfunction(:template, :type => :rvalue, :doc =>
# Use a wrapper, so the template can't get access to the full
# Scope object.
debug "Retrieving template %s" % file
wrapper = Puppet::Parser::TemplateWrapper.new(self, file)
wrapper = Puppet::Parser::TemplateWrapper.new(self)
wrapper.file = file
begin
wrapper.result()
wrapper.result
rescue => detail
raise Puppet::ParseError,
"Failed to parse template %s: %s" %

Просмотреть файл

@ -130,6 +130,7 @@ namestring: name
}
resource: classname LBRACE resourceinstances endsemi RBRACE {
@lexer.commentpop
array = val[2]
if array.instance_of?(AST::ResourceInstance)
array = [array]
@ -158,6 +159,7 @@ resource: classname LBRACE resourceinstances endsemi RBRACE {
# Override a value set elsewhere in the configuration.
resourceoverride: resourceref LBRACE anyparams endcomma RBRACE {
@lexer.commentpop
result = ast AST::ResourceOverride, :object => val[0], :params => val[2]
}
@ -198,7 +200,7 @@ collection: classref collectrhand {
Puppet.warning addcontext("Collection names must now be capitalized")
end
type = val[0].downcase
args = {:type => type}
args = {:type => type }
if val[1].is_a?(AST::CollExpr)
args[:query] = val[1]
@ -410,6 +412,7 @@ resourceref: NAME LBRACK rvalues RBRACK {
}
ifstatement: IF expression LBRACE statements RBRACE else {
@lexer.commentpop
args = {
:test => val[1],
:statements => val[3]
@ -422,6 +425,7 @@ ifstatement: IF expression LBRACE statements RBRACE else {
result = ast AST::IfStatement, args
}
| IF expression LBRACE RBRACE else {
@lexer.commentpop
args = {
:test => val[1],
:statements => ast(AST::Nop)
@ -436,9 +440,11 @@ ifstatement: IF expression LBRACE statements RBRACE else {
else: # nothing
| ELSE LBRACE statements RBRACE {
@lexer.commentpop
result = ast AST::Else, :statements => val[2]
}
| ELSE LBRACE RBRACE {
@lexer.commentpop
result = ast AST::Else, :statements => ast(AST::Nop)
}
@ -508,6 +514,7 @@ expression: rvalue
}
casestatement: CASE rvalue LBRACE caseopts RBRACE {
@lexer.commentpop
options = val[3]
unless options.instance_of?(AST::ASTArray)
options = ast AST::ASTArray, :children => [val[3]]
@ -526,8 +533,10 @@ caseopts: caseopt
}
caseopt: casevalues COLON LBRACE statements RBRACE {
@lexer.commentpop
result = ast AST::CaseOpt, :value => val[0], :statements => val[3]
} | casevalues COLON LBRACE RBRACE {
@lexer.commentpop
result = ast(AST::CaseOpt,
:value => val[0],
:statements => ast(AST::ASTArray)
@ -549,7 +558,10 @@ selector: selectlhand QMARK svalues {
}
svalues: selectval
| LBRACE sintvalues endcomma RBRACE { result = val[1] }
| LBRACE sintvalues endcomma RBRACE {
@lexer.commentpop
result = val[1]
}
sintvalues: selectval
| sintvalues comma selectval {
@ -593,12 +605,14 @@ import: IMPORT qtexts {
# Disable definition inheritance for now. 8/27/06, luke
#definition: DEFINE NAME argumentlist parent LBRACE statements RBRACE {
definition: DEFINE classname argumentlist LBRACE statements RBRACE {
@lexer.commentpop
newdefine classname(val[1]), :arguments => val[2], :code => val[4]
@lexer.indefine = false
result = nil
#} | DEFINE NAME argumentlist parent LBRACE RBRACE {
} | DEFINE classname argumentlist LBRACE RBRACE {
@lexer.commentpop
newdefine classname(val[1]), :arguments => val[2]
@lexer.indefine = false
result = nil
@ -606,11 +620,13 @@ definition: DEFINE classname argumentlist LBRACE statements RBRACE {
#hostclass: CLASS NAME argumentlist parent LBRACE statements RBRACE {
hostclass: CLASS classname classparent LBRACE statements RBRACE {
@lexer.commentpop
# Our class gets defined in the parent namespace, not our own.
@lexer.namepop
newclass classname(val[1]), :code => val[4], :parent => val[2]
result = nil
} | CLASS classname classparent LBRACE RBRACE {
@lexer.commentpop
# Our class gets defined in the parent namespace, not our own.
@lexer.namepop
newclass classname(val[1]), :parent => val[2]
@ -618,9 +634,11 @@ hostclass: CLASS classname classparent LBRACE statements RBRACE {
}
nodedef: NODE hostnames nodeparent LBRACE statements RBRACE {
@lexer.commentpop
newnode val[1], :parent => val[2], :code => val[4]
result = nil
} | NODE hostnames nodeparent LBRACE RBRACE {
@lexer.commentpop
newnode val[1], :parent => val[2]
result = nil
}

Просмотреть файл

@ -17,7 +17,7 @@ class Puppet::Parser::Lexer
# Our base token class.
class Token
attr_accessor :regex, :name, :string, :skip, :incr_line, :skip_text
attr_accessor :regex, :name, :string, :skip, :incr_line, :skip_text, :accumulate
def initialize(regex, name)
if regex.is_a?(String)
@ -28,8 +28,10 @@ class Puppet::Parser::Lexer
end
end
def skip?
self.skip
%w{skip accumulate}.each do |method|
define_method(method+"?") do
self.send(method)
end
end
def to_s
@ -132,7 +134,7 @@ class Puppet::Parser::Lexer
'*' => :TIMES,
'<<' => :LSHIFT,
'>>' => :RSHIFT,
%r{([a-z][-\w]*::)+[a-z][-\w]*} => :CLASSNAME,
%r{([a-z][-\w]*)?(::[a-z][-\w]*)+} => :CLASSNAME, # Require '::' in the class name, else we'd compete with NAME
%r{((::){0,1}[A-Z][-\w]*)+} => :CLASSREF
)
@ -155,11 +157,16 @@ class Puppet::Parser::Lexer
[string_token, value]
end
TOKENS.add_token :COMMENT, %r{#.*}, :skip => true
TOKENS.add_token :COMMENT, %r{#.*}, :accumulate => true, :skip => true do |lexer,value|
value.sub!(/# ?/,'')
[self, value]
end
TOKENS.add_token :MLCOMMENT, %r{/\*(.*?)\*/}m do |lexer, value|
TOKENS.add_token :MLCOMMENT, %r{/\*(.*?)\*/}m, :accumulate => true, :skip => true do |lexer, value|
lexer.line += value.count("\n")
[nil,nil]
value.sub!(/^\/\* ?/,'')
value.sub!(/ ?\*\/$/,'')
[self,value]
end
TOKENS.add_token :RETURN, "\n", :skip => true, :incr_line => true, :skip_text => true
@ -325,6 +332,7 @@ class Puppet::Parser::Lexer
@namestack = []
@indefine = false
@expected = []
@commentstack = ['']
end
# Make any necessary changes to the token and/or value.
@ -333,12 +341,18 @@ class Puppet::Parser::Lexer
skip() if token.skip_text
return if token.skip
return if token.skip and not token.accumulate?
token, value = token.convert(self, value) if token.respond_to?(:convert)
return unless token
if token.accumulate?
@commentstack.last << value + "\n"
end
return if token.skip
return token, value
end
@ -389,6 +403,18 @@ class Puppet::Parser::Lexer
raise "Could not match '%s'" % nword
end
if matched_token.name == :RETURN
# this matches a blank line
if @last_return
# eat the previously accumulated comments
getcomment
end
# since :RETURN skips, we won't survive to munge_token
@last_return = true
else
@last_return = false
end
final_token, value = munge_token(matched_token, value)
next unless final_token
@ -399,6 +425,10 @@ class Puppet::Parser::Lexer
@expected.pop
end
if final_token.name == :LBRACE
commentpush
end
yield [final_token.name, value]
if @previous_token
@ -414,7 +444,6 @@ class Puppet::Parser::Lexer
@indefine = value
end
end
@previous_token = final_token
skip()
end
@ -453,4 +482,19 @@ class Puppet::Parser::Lexer
def string=(string)
@scanner = StringScanner.new(string)
end
# returns the content of the currently accumulated content cache
def commentpop
return @commentstack.pop
end
def getcomment
comment = @commentstack.pop
@commentstack.push('')
return comment
end
def commentpush
@commentstack.push('')
end
end

Просмотреть файл

@ -29,7 +29,7 @@ module Puppet
class Parser < Racc::Parser
module_eval <<'..end grammar.ra modeval..id5cb4445525', 'grammar.ra', 741
module_eval <<'..end grammar.ra modeval..id987bcfd032', 'grammar.ra', 759
# It got too annoying having code in a file that needs to be compiled.
require 'puppet/parser/parser_support'
@ -41,7 +41,7 @@ require 'puppet/parser/parser_support'
# $Id$
..end grammar.ra modeval..id5cb4445525
..end grammar.ra modeval..id987bcfd032
##### racc 1.4.5 generates ###
@ -1103,8 +1103,9 @@ module_eval <<'.,.,', 'grammar.ra', 130
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 151
module_eval <<'.,.,', 'grammar.ra', 152
def _reduce_34( val, _values, result )
@lexer.commentpop
array = val[2]
if array.instance_of?(AST::ResourceInstance)
array = [array]
@ -1127,7 +1128,7 @@ module_eval <<'.,.,', 'grammar.ra', 151
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 154
module_eval <<'.,.,', 'grammar.ra', 155
def _reduce_35( val, _values, result )
# This is a deprecated syntax.
error "All resource specifications require names"
@ -1135,7 +1136,7 @@ module_eval <<'.,.,', 'grammar.ra', 154
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 157
module_eval <<'.,.,', 'grammar.ra', 158
def _reduce_36( val, _values, result )
# a defaults setting for a type
result = ast(AST::ResourceDefaults, :type => val[0], :params => val[2])
@ -1143,14 +1144,15 @@ module_eval <<'.,.,', 'grammar.ra', 157
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 162
module_eval <<'.,.,', 'grammar.ra', 164
def _reduce_37( val, _values, result )
@lexer.commentpop
result = ast AST::ResourceOverride, :object => val[0], :params => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 189
module_eval <<'.,.,', 'grammar.ra', 191
def _reduce_38( val, _values, result )
type = val[0]
@ -1178,27 +1180,27 @@ module_eval <<'.,.,', 'grammar.ra', 189
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 190
module_eval <<'.,.,', 'grammar.ra', 192
def _reduce_39( val, _values, result )
result = :virtual
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 191
module_eval <<'.,.,', 'grammar.ra', 193
def _reduce_40( val, _values, result )
result = :exported
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 214
module_eval <<'.,.,', 'grammar.ra', 216
def _reduce_41( val, _values, result )
if val[0] =~ /^[a-z]/
Puppet.warning addcontext("Collection names must now be capitalized")
end
type = val[0].downcase
args = {:type => type}
args = {:type => type }
if val[1].is_a?(AST::CollExpr)
args[:query] = val[1]
@ -1215,7 +1217,7 @@ module_eval <<'.,.,', 'grammar.ra', 214
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 224
module_eval <<'.,.,', 'grammar.ra', 226
def _reduce_42( val, _values, result )
if val[1]
result = val[1]
@ -1227,7 +1229,7 @@ module_eval <<'.,.,', 'grammar.ra', 224
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 232
module_eval <<'.,.,', 'grammar.ra', 234
def _reduce_43( val, _values, result )
if val[1]
result = val[1]
@ -1243,7 +1245,7 @@ module_eval <<'.,.,', 'grammar.ra', 232
# reduce 45 omitted
module_eval <<'.,.,', 'grammar.ra', 240
module_eval <<'.,.,', 'grammar.ra', 242
def _reduce_46( val, _values, result )
result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2]
result
@ -1252,7 +1254,7 @@ module_eval <<'.,.,', 'grammar.ra', 240
# reduce 47 omitted
module_eval <<'.,.,', 'grammar.ra', 246
module_eval <<'.,.,', 'grammar.ra', 248
def _reduce_48( val, _values, result )
result = val[1]
result.parens = true
@ -1264,7 +1266,7 @@ module_eval <<'.,.,', 'grammar.ra', 246
# reduce 50 omitted
module_eval <<'.,.,', 'grammar.ra', 254
module_eval <<'.,.,', 'grammar.ra', 256
def _reduce_51( val, _values, result )
result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2]
#result = ast AST::CollExpr
@ -1273,7 +1275,7 @@ module_eval <<'.,.,', 'grammar.ra', 254
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 259
module_eval <<'.,.,', 'grammar.ra', 261
def _reduce_52( val, _values, result )
result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2]
#result = ast AST::CollExpr
@ -1286,7 +1288,7 @@ module_eval <<'.,.,', 'grammar.ra', 259
# reduce 54 omitted
module_eval <<'.,.,', 'grammar.ra', 266
module_eval <<'.,.,', 'grammar.ra', 268
def _reduce_55( val, _values, result )
result = ast AST::ResourceInstance, :children => [val[0],val[2]]
result
@ -1295,7 +1297,7 @@ module_eval <<'.,.,', 'grammar.ra', 266
# reduce 56 omitted
module_eval <<'.,.,', 'grammar.ra', 276
module_eval <<'.,.,', 'grammar.ra', 278
def _reduce_57( val, _values, result )
if val[0].instance_of?(AST::ResourceInstance)
result = ast AST::ASTArray, :children => [val[0],val[2]]
@ -1311,21 +1313,21 @@ module_eval <<'.,.,', 'grammar.ra', 276
# reduce 59 omitted
module_eval <<'.,.,', 'grammar.ra', 283
module_eval <<'.,.,', 'grammar.ra', 285
def _reduce_60( val, _values, result )
result = ast AST::Undef, :value => :undef
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 287
module_eval <<'.,.,', 'grammar.ra', 289
def _reduce_61( val, _values, result )
result = ast AST::Name, :value => val[0]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 291
module_eval <<'.,.,', 'grammar.ra', 293
def _reduce_62( val, _values, result )
result = ast AST::Type, :value => val[0]
result
@ -1344,7 +1346,7 @@ module_eval <<'.,.,', 'grammar.ra', 291
# reduce 68 omitted
module_eval <<'.,.,', 'grammar.ra', 307
module_eval <<'.,.,', 'grammar.ra', 309
def _reduce_69( val, _values, result )
if val[0] =~ /::/
raise Puppet::ParseError, "Cannot assign to variables in other namespaces"
@ -1356,7 +1358,7 @@ module_eval <<'.,.,', 'grammar.ra', 307
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 312
module_eval <<'.,.,', 'grammar.ra', 314
def _reduce_70( val, _values, result )
variable = ast AST::Name, :value => val[0]
result = ast AST::VarDef, :name => variable, :value => val[2], :append => true
@ -1364,21 +1366,21 @@ module_eval <<'.,.,', 'grammar.ra', 312
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 317
module_eval <<'.,.,', 'grammar.ra', 319
def _reduce_71( val, _values, result )
result = ast AST::ASTArray
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 317
module_eval <<'.,.,', 'grammar.ra', 319
def _reduce_72( val, _values, result )
result = val[0]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 326
module_eval <<'.,.,', 'grammar.ra', 328
def _reduce_73( val, _values, result )
if val[0].instance_of?(AST::ASTArray)
val[0].push(val[2])
@ -1390,14 +1392,14 @@ module_eval <<'.,.,', 'grammar.ra', 326
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 330
module_eval <<'.,.,', 'grammar.ra', 332
def _reduce_74( val, _values, result )
result = ast AST::ResourceParam, :param => val[0], :value => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 335
module_eval <<'.,.,', 'grammar.ra', 337
def _reduce_75( val, _values, result )
result = ast AST::ResourceParam, :param => val[0], :value => val[2],
:add => true
@ -1409,21 +1411,21 @@ module_eval <<'.,.,', 'grammar.ra', 335
# reduce 77 omitted
module_eval <<'.,.,', 'grammar.ra', 343
module_eval <<'.,.,', 'grammar.ra', 345
def _reduce_78( val, _values, result )
result = ast AST::ASTArray
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 343
module_eval <<'.,.,', 'grammar.ra', 345
def _reduce_79( val, _values, result )
result = val[0]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 352
module_eval <<'.,.,', 'grammar.ra', 354
def _reduce_80( val, _values, result )
if val[0].instance_of?(AST::ASTArray)
val[0].push(val[2])
@ -1437,7 +1439,7 @@ module_eval <<'.,.,', 'grammar.ra', 352
# reduce 81 omitted
module_eval <<'.,.,', 'grammar.ra', 361
module_eval <<'.,.,', 'grammar.ra', 363
def _reduce_82( val, _values, result )
if val[0].instance_of?(AST::ASTArray)
result = val[0].push(val[2])
@ -1480,7 +1482,7 @@ module_eval <<'.,.,', 'grammar.ra', 361
# reduce 98 omitted
module_eval <<'.,.,', 'grammar.ra', 388
module_eval <<'.,.,', 'grammar.ra', 390
def _reduce_99( val, _values, result )
args = aryfy(val[2])
result = ast AST::Function,
@ -1491,7 +1493,7 @@ module_eval <<'.,.,', 'grammar.ra', 388
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 393
module_eval <<'.,.,', 'grammar.ra', 395
def _reduce_100( val, _values, result )
result = ast AST::Function,
:name => val[0],
@ -1501,28 +1503,28 @@ module_eval <<'.,.,', 'grammar.ra', 393
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 397
module_eval <<'.,.,', 'grammar.ra', 399
def _reduce_101( val, _values, result )
result = ast AST::String, :value => val[0]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 399
module_eval <<'.,.,', 'grammar.ra', 401
def _reduce_102( val, _values, result )
result = ast AST::FlatString, :value => val[0]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 403
module_eval <<'.,.,', 'grammar.ra', 405
def _reduce_103( val, _values, result )
result = ast AST::Boolean, :value => val[0]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 408
module_eval <<'.,.,', 'grammar.ra', 410
def _reduce_104( val, _values, result )
Puppet.warning addcontext("Deprecation notice: Resource references should now be capitalized")
result = ast AST::ResourceReference, :type => val[0], :title => val[2]
@ -1530,15 +1532,16 @@ module_eval <<'.,.,', 'grammar.ra', 408
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 410
module_eval <<'.,.,', 'grammar.ra', 412
def _reduce_105( val, _values, result )
result = ast AST::ResourceReference, :type => val[0], :title => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 423
module_eval <<'.,.,', 'grammar.ra', 426
def _reduce_106( val, _values, result )
@lexer.commentpop
args = {
:test => val[1],
:statements => val[3]
@ -1553,8 +1556,9 @@ module_eval <<'.,.,', 'grammar.ra', 423
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 435
module_eval <<'.,.,', 'grammar.ra', 439
def _reduce_107( val, _values, result )
@lexer.commentpop
args = {
:test => val[1],
:statements => ast(AST::Nop)
@ -1571,15 +1575,17 @@ module_eval <<'.,.,', 'grammar.ra', 435
# reduce 108 omitted
module_eval <<'.,.,', 'grammar.ra', 440
module_eval <<'.,.,', 'grammar.ra', 445
def _reduce_109( val, _values, result )
@lexer.commentpop
result = ast AST::Else, :statements => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 443
module_eval <<'.,.,', 'grammar.ra', 449
def _reduce_110( val, _values, result )
@lexer.commentpop
result = ast AST::Else, :statements => ast(AST::Nop)
result
end
@ -1587,127 +1593,128 @@ module_eval <<'.,.,', 'grammar.ra', 443
# reduce 111 omitted
module_eval <<'.,.,', 'grammar.ra', 460
module_eval <<'.,.,', 'grammar.ra', 466
def _reduce_112( val, _values, result )
result = ast AST::ArithmeticOperator, :operator => val[1], :lval => val[0], :rval => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 463
module_eval <<'.,.,', 'grammar.ra', 469
def _reduce_113( val, _values, result )
result = ast AST::ArithmeticOperator, :operator => val[1], :lval => val[0], :rval => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 466
module_eval <<'.,.,', 'grammar.ra', 472
def _reduce_114( val, _values, result )
result = ast AST::ArithmeticOperator, :operator => val[1], :lval => val[0], :rval => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 469
module_eval <<'.,.,', 'grammar.ra', 475
def _reduce_115( val, _values, result )
result = ast AST::ArithmeticOperator, :operator => val[1], :lval => val[0], :rval => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 472
module_eval <<'.,.,', 'grammar.ra', 478
def _reduce_116( val, _values, result )
result = ast AST::ArithmeticOperator, :operator => val[1], :lval => val[0], :rval => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 475
module_eval <<'.,.,', 'grammar.ra', 481
def _reduce_117( val, _values, result )
result = ast AST::ArithmeticOperator, :operator => val[1], :lval => val[0], :rval => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 478
module_eval <<'.,.,', 'grammar.ra', 484
def _reduce_118( val, _values, result )
result = ast AST::Minus, :value => val[1]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 481
module_eval <<'.,.,', 'grammar.ra', 487
def _reduce_119( val, _values, result )
result = ast AST::ComparisonOperator, :operator => val[1], :lval => val[0], :rval => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 484
module_eval <<'.,.,', 'grammar.ra', 490
def _reduce_120( val, _values, result )
result = ast AST::ComparisonOperator, :operator => val[1], :lval => val[0], :rval => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 487
module_eval <<'.,.,', 'grammar.ra', 493
def _reduce_121( val, _values, result )
result = ast AST::ComparisonOperator, :operator => val[1], :lval => val[0], :rval => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 490
module_eval <<'.,.,', 'grammar.ra', 496
def _reduce_122( val, _values, result )
result = ast AST::ComparisonOperator, :operator => val[1], :lval => val[0], :rval => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 493
module_eval <<'.,.,', 'grammar.ra', 499
def _reduce_123( val, _values, result )
result = ast AST::ComparisonOperator, :operator => val[1], :lval => val[0], :rval => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 496
module_eval <<'.,.,', 'grammar.ra', 502
def _reduce_124( val, _values, result )
result = ast AST::ComparisonOperator, :operator => val[1], :lval => val[0], :rval => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 499
module_eval <<'.,.,', 'grammar.ra', 505
def _reduce_125( val, _values, result )
result = ast AST::Not, :value => val[1]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 502
module_eval <<'.,.,', 'grammar.ra', 508
def _reduce_126( val, _values, result )
result = ast AST::BooleanOperator, :operator => val[1], :lval => val[0], :rval => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 505
module_eval <<'.,.,', 'grammar.ra', 511
def _reduce_127( val, _values, result )
result = ast AST::BooleanOperator, :operator => val[1], :lval => val[0], :rval => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 508
module_eval <<'.,.,', 'grammar.ra', 514
def _reduce_128( val, _values, result )
result = val[1]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 516
module_eval <<'.,.,', 'grammar.ra', 523
def _reduce_129( val, _values, result )
@lexer.commentpop
options = val[3]
unless options.instance_of?(AST::ASTArray)
options = ast AST::ASTArray, :children => [val[3]]
@ -1719,7 +1726,7 @@ module_eval <<'.,.,', 'grammar.ra', 516
# reduce 130 omitted
module_eval <<'.,.,', 'grammar.ra', 526
module_eval <<'.,.,', 'grammar.ra', 533
def _reduce_131( val, _values, result )
if val[0].instance_of?(AST::ASTArray)
val[0].push val[1]
@ -1731,15 +1738,17 @@ module_eval <<'.,.,', 'grammar.ra', 526
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 530
module_eval <<'.,.,', 'grammar.ra', 538
def _reduce_132( val, _values, result )
@lexer.commentpop
result = ast AST::CaseOpt, :value => val[0], :statements => val[3]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 535
module_eval <<'.,.,', 'grammar.ra', 544
def _reduce_133( val, _values, result )
@lexer.commentpop
result = ast(AST::CaseOpt,
:value => val[0],
:statements => ast(AST::ASTArray)
@ -1750,7 +1759,7 @@ module_eval <<'.,.,', 'grammar.ra', 535
# reduce 134 omitted
module_eval <<'.,.,', 'grammar.ra', 545
module_eval <<'.,.,', 'grammar.ra', 554
def _reduce_135( val, _values, result )
if val[0].instance_of?(AST::ASTArray)
val[0].push(val[2])
@ -1762,7 +1771,7 @@ module_eval <<'.,.,', 'grammar.ra', 545
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 549
module_eval <<'.,.,', 'grammar.ra', 558
def _reduce_136( val, _values, result )
result = ast AST::Selector, :param => val[0], :values => val[2]
result
@ -1771,16 +1780,17 @@ module_eval <<'.,.,', 'grammar.ra', 549
# reduce 137 omitted
module_eval <<'.,.,', 'grammar.ra', 551
module_eval <<'.,.,', 'grammar.ra', 564
def _reduce_138( val, _values, result )
result = val[1]
@lexer.commentpop
result = val[1]
result
end
.,.,
# reduce 139 omitted
module_eval <<'.,.,', 'grammar.ra', 562
module_eval <<'.,.,', 'grammar.ra', 574
def _reduce_140( val, _values, result )
if val[0].instance_of?(AST::ASTArray)
val[0].push(val[2])
@ -1792,7 +1802,7 @@ module_eval <<'.,.,', 'grammar.ra', 562
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 566
module_eval <<'.,.,', 'grammar.ra', 578
def _reduce_141( val, _values, result )
result = ast AST::ResourceParam, :param => val[0], :value => val[2]
result
@ -1813,28 +1823,28 @@ module_eval <<'.,.,', 'grammar.ra', 566
# reduce 148 omitted
module_eval <<'.,.,', 'grammar.ra', 577
module_eval <<'.,.,', 'grammar.ra', 589
def _reduce_149( val, _values, result )
result = ast AST::Default, :value => val[0]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 579
module_eval <<'.,.,', 'grammar.ra', 591
def _reduce_150( val, _values, result )
result = [val[0].value]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 583
module_eval <<'.,.,', 'grammar.ra', 595
def _reduce_151( val, _values, result )
results = val[0] << val[2].value
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 591
module_eval <<'.,.,', 'grammar.ra', 603
def _reduce_152( val, _values, result )
val[1].each do |file|
import(file)
@ -1845,8 +1855,9 @@ module_eval <<'.,.,', 'grammar.ra', 591
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 601
module_eval <<'.,.,', 'grammar.ra', 614
def _reduce_153( val, _values, result )
@lexer.commentpop
newdefine classname(val[1]), :arguments => val[2], :code => val[4]
@lexer.indefine = false
result = nil
@ -1856,8 +1867,9 @@ module_eval <<'.,.,', 'grammar.ra', 601
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 605
module_eval <<'.,.,', 'grammar.ra', 619
def _reduce_154( val, _values, result )
@lexer.commentpop
newdefine classname(val[1]), :arguments => val[2]
@lexer.indefine = false
result = nil
@ -1865,8 +1877,9 @@ module_eval <<'.,.,', 'grammar.ra', 605
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 613
module_eval <<'.,.,', 'grammar.ra', 628
def _reduce_155( val, _values, result )
@lexer.commentpop
# Our class gets defined in the parent namespace, not our own.
@lexer.namepop
newclass classname(val[1]), :code => val[4], :parent => val[2]
@ -1875,8 +1888,9 @@ module_eval <<'.,.,', 'grammar.ra', 613
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 618
module_eval <<'.,.,', 'grammar.ra', 634
def _reduce_156( val, _values, result )
@lexer.commentpop
# Our class gets defined in the parent namespace, not our own.
@lexer.namepop
newclass classname(val[1]), :parent => val[2]
@ -1885,16 +1899,18 @@ module_eval <<'.,.,', 'grammar.ra', 618
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 623
module_eval <<'.,.,', 'grammar.ra', 640
def _reduce_157( val, _values, result )
@lexer.commentpop
newnode val[1], :parent => val[2], :code => val[4]
result = nil
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 626
module_eval <<'.,.,', 'grammar.ra', 644
def _reduce_158( val, _values, result )
@lexer.commentpop
newnode val[1], :parent => val[2]
result = nil
result
@ -1909,7 +1925,7 @@ module_eval <<'.,.,', 'grammar.ra', 626
# reduce 162 omitted
module_eval <<'.,.,', 'grammar.ra', 640
module_eval <<'.,.,', 'grammar.ra', 658
def _reduce_163( val, _values, result )
result = val[0]
result = [result] unless result.is_a?(Array)
@ -1926,14 +1942,14 @@ module_eval <<'.,.,', 'grammar.ra', 640
# reduce 167 omitted
module_eval <<'.,.,', 'grammar.ra', 649
module_eval <<'.,.,', 'grammar.ra', 667
def _reduce_168( val, _values, result )
result = nil
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 653
module_eval <<'.,.,', 'grammar.ra', 671
def _reduce_169( val, _values, result )
result = ast AST::ASTArray, :children => []
result
@ -1942,14 +1958,14 @@ module_eval <<'.,.,', 'grammar.ra', 653
# reduce 170 omitted
module_eval <<'.,.,', 'grammar.ra', 658
module_eval <<'.,.,', 'grammar.ra', 676
def _reduce_171( val, _values, result )
result = nil
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 662
module_eval <<'.,.,', 'grammar.ra', 680
def _reduce_172( val, _values, result )
result = val[1]
result = [result] unless result[0].is_a?(Array)
@ -1959,7 +1975,7 @@ module_eval <<'.,.,', 'grammar.ra', 662
# reduce 173 omitted
module_eval <<'.,.,', 'grammar.ra', 669
module_eval <<'.,.,', 'grammar.ra', 687
def _reduce_174( val, _values, result )
result = val[0]
result = [result] unless result[0].is_a?(Array)
@ -1968,7 +1984,7 @@ module_eval <<'.,.,', 'grammar.ra', 669
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 674
module_eval <<'.,.,', 'grammar.ra', 692
def _reduce_175( val, _values, result )
Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype")
result = [val[0], val[2]]
@ -1976,7 +1992,7 @@ module_eval <<'.,.,', 'grammar.ra', 674
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 678
module_eval <<'.,.,', 'grammar.ra', 696
def _reduce_176( val, _values, result )
Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype")
result = [val[0]]
@ -1984,14 +2000,14 @@ module_eval <<'.,.,', 'grammar.ra', 678
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 680
module_eval <<'.,.,', 'grammar.ra', 698
def _reduce_177( val, _values, result )
result = [val[0], val[2]]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 682
module_eval <<'.,.,', 'grammar.ra', 700
def _reduce_178( val, _values, result )
result = [val[0]]
result
@ -2000,7 +2016,7 @@ module_eval <<'.,.,', 'grammar.ra', 682
# reduce 179 omitted
module_eval <<'.,.,', 'grammar.ra', 687
module_eval <<'.,.,', 'grammar.ra', 705
def _reduce_180( val, _values, result )
result = val[1]
result
@ -2009,7 +2025,7 @@ module_eval <<'.,.,', 'grammar.ra', 687
# reduce 181 omitted
module_eval <<'.,.,', 'grammar.ra', 692
module_eval <<'.,.,', 'grammar.ra', 710
def _reduce_182( val, _values, result )
result = val[1]
result
@ -2020,14 +2036,14 @@ module_eval <<'.,.,', 'grammar.ra', 692
# reduce 184 omitted
module_eval <<'.,.,', 'grammar.ra', 698
module_eval <<'.,.,', 'grammar.ra', 716
def _reduce_185( val, _values, result )
result = ast AST::Variable, :value => val[0]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 706
module_eval <<'.,.,', 'grammar.ra', 724
def _reduce_186( val, _values, result )
if val[1].instance_of?(AST::ASTArray)
result = val[1]
@ -2038,7 +2054,7 @@ module_eval <<'.,.,', 'grammar.ra', 706
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 713
module_eval <<'.,.,', 'grammar.ra', 731
def _reduce_187( val, _values, result )
if val[1].instance_of?(AST::ASTArray)
result = val[1]
@ -2049,7 +2065,7 @@ module_eval <<'.,.,', 'grammar.ra', 713
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 715
module_eval <<'.,.,', 'grammar.ra', 733
def _reduce_188( val, _values, result )
result = ast AST::ASTArray
result
@ -2062,7 +2078,7 @@ module_eval <<'.,.,', 'grammar.ra', 715
# reduce 191 omitted
module_eval <<'.,.,', 'grammar.ra', 720
module_eval <<'.,.,', 'grammar.ra', 738
def _reduce_192( val, _values, result )
result = nil
result

Просмотреть файл

@ -18,6 +18,7 @@ class Puppet::Parser::Parser
attr_reader :version, :environment
attr_accessor :files
attr_accessor :lexer
# Add context to a message; useful for error messages and such.
def addcontext(message, obj = nil)
@ -56,7 +57,9 @@ class Puppet::Parser::Parser
end
end
return klass.new(hash)
k = klass.new(hash)
k.doc = lexer.getcomment if !k.nil? and k.use_docs and k.doc.empty?
return k
end
# The fully qualifed name, with the full namespace.
@ -272,6 +275,7 @@ class Puppet::Parser::Parser
end
code = options[:code]
parent = options[:parent]
doc = options[:doc]
# If the class is already defined, then add code to it.
if other = @astset.classes[name]
@ -304,6 +308,12 @@ class Puppet::Parser::Parser
other.code ||= code
end
end
if other.doc and doc
other.doc += doc
else
other.doc ||= doc
end
else
# Define it anew.
# Note we're doing something somewhat weird here -- we're setting
@ -312,6 +322,8 @@ class Puppet::Parser::Parser
args = {:namespace => name, :classname => name, :parser => self}
args[:code] = code if code
args[:parentclass] = parent if parent
args[:doc] = doc
@astset.classes[name] = ast AST::HostClass, args
end
@ -336,7 +348,8 @@ class Puppet::Parser::Parser
:arguments => options[:arguments],
:code => options[:code],
:parser => self,
:classname => name
:classname => name,
:doc => options[:doc]
}
[:code, :arguments].each do |param|
@ -350,6 +363,7 @@ class Puppet::Parser::Parser
# table, not according to namespaces.
def newnode(names, options = {})
names = [names] unless names.instance_of?(Array)
doc = lexer.getcomment
names.collect do |name|
name = name.to_s.downcase
if other = @astset.nodes[name]
@ -358,7 +372,8 @@ class Puppet::Parser::Parser
name = name.to_s if name.is_a?(Symbol)
args = {
:name => name,
:parser => self
:parser => self,
:doc => doc
}
if options[:code]
args[:code] = options[:code]
@ -399,6 +414,7 @@ class Puppet::Parser::Parser
self.string = string
end
begin
@yydebug = false
main = yyparse(@lexer,:scan)
rescue Racc::ParseError => except
error = Puppet::ParseError.new(except)

Просмотреть файл

@ -37,7 +37,7 @@ class Puppet::Parser::Resource::Reference < Puppet::ResourceReference
if self.title == :main
tmp = @scope.findclass("")
else
unless tmp = @scope.findclass(self.title)
unless tmp = @scope.parser.classes[self.title]
fail Puppet::ParseError, "Could not find class '%s'" % self.title
end
end
@ -46,8 +46,9 @@ class Puppet::Parser::Resource::Reference < Puppet::ResourceReference
fail Puppet::ParseError, "Could not find node '%s'" % self.title
end
else # normal definitions
# We have to swap these variables around so the errors are right.
tmp = @scope.finddefine(self.type)
# The resource type is capitalized, so we have to downcase. Really,
# we should have a better interface for finding these, but eh.
tmp = @scope.parser.definitions[self.type.downcase]
end
if tmp

Просмотреть файл

@ -1,33 +1,18 @@
# A simple wrapper for templates, so they don't have full access to
# the scope objects.
class Puppet::Parser::TemplateWrapper
attr_accessor :scope, :file
attr_accessor :scope, :file, :string
include Puppet::Util
Puppet::Util.logmethods(self)
def initialize(scope, filename)
def initialize(scope)
@__scope__ = scope
@__file__ = Puppet::Module::find_template(filename, scope.compiler.environment)
unless FileTest.exists?(file)
raise Puppet::ParseError,
"Could not find template %s" % file
end
# We'll only ever not have a parser in testing, but, eh.
if scope.parser
scope.parser.watch_file(file)
end
end
def scope
@__scope__
end
def file
@__file__
end
# Should return true if a variable is defined, false if it is not
def has_variable?(name)
if scope.lookupvar(name.to_s, false) != :undefined
@ -77,11 +62,34 @@ class Puppet::Parser::TemplateWrapper
end
end
def result
def file=(filename)
@file = Puppet::Module::find_template(filename, scope.compiler.environment)
unless FileTest.exists?(file)
raise Puppet::ParseError,
"Could not find template %s" % file
end
# We'll only ever not have a parser in testing, but, eh.
if scope.parser
scope.parser.watch_file(file)
end
@string = File.read(file)
end
def result(string = nil)
if string
self.string = string
template_source = "inline template"
else
template_source = file
end
# Expose all the variables in our scope as instance variables of the
# current object, making it possible to access them without conflict
# to the regular methods.
benchmark(:debug, "Bound template variables for #{file}") do
benchmark(:debug, "Bound template variables for #{template_source}") do
scope.to_hash.each { |name, value|
if name.kind_of?(String)
realname = name.gsub(/[^\w]/, "_")
@ -93,8 +101,8 @@ class Puppet::Parser::TemplateWrapper
end
result = nil
benchmark(:debug, "Interpolated template #{file}") do
template = ERB.new(File.read(file), 0, "-")
benchmark(:debug, "Interpolated template #{template_source}") do
template = ERB.new(self.string, 0, "-")
result = template.result(binding)
end
@ -102,7 +110,7 @@ class Puppet::Parser::TemplateWrapper
end
def to_s
"template[%s]" % file
"template[%s]" % (file ? file : "inline")
end
end

Просмотреть файл

@ -445,7 +445,7 @@ class Puppet::Property < Puppet::Parameter
end
# This doc will probably get overridden
@doc ||= "The basic property that the object should be in."
@doc ||= "The basic property that the resource should be in."
end
def self.inherited(sub)

Просмотреть файл

@ -28,6 +28,11 @@ module Puppet
@resource[membership] == :inclusive
end
#dearrayify was motivated because to simplify the implementation of the OrderedList property
def dearrayify(array)
array.sort.join(delimiter)
end
def should
unless defined? @should and @should
return nil
@ -39,7 +44,7 @@ module Puppet
members = add_should_with_current(members, retrieve)
end
members.sort.join(delimiter)
dearrayify(members)
end
def delimiter
@ -57,7 +62,7 @@ module Puppet
def prepare_is_for_comparison(is)
if is.is_a? Array
is = is.sort.join(delimiter)
is = dearrayify(is)
end
is
end

Просмотреть файл

@ -0,0 +1,22 @@
require 'puppet/property/list'
module Puppet
class Property
class OrderedList < List
def add_should_with_current(should, current)
if current.is_a?(Array)
#tricky trick
#Preserve all the current items in the list
#but move them to the back of the line
should = should + (current - should)
end
should
end
def dearrayify(array)
array.join(delimiter)
end
end
end
end

Просмотреть файл

@ -2,16 +2,16 @@
# Copyright (C) 2008 Red Hat Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
# General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# You should have received a copy of the GNU General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#

Просмотреть файл

@ -0,0 +1,22 @@
require 'puppet/provider/nameservice/directoryservice'
Puppet::Type.type(:computer).provide :directoryservice, :parent => Puppet::Provider::NameService::DirectoryService do
desc "Computer object management using DirectoryService on OS X.
Note that these are distinctly different kinds of objects to 'hosts',
as they require a MAC address and can have all sorts of policy attached to
them.
This provider only manages Computer objects in the local directory service
domain, not in remote directories.
If you wish to manage /etc/hosts on Mac OS X, then simply use the host
type as per other platforms.
"
confine :operatingsystem => :darwin
defaultfor :operatingsystem => :darwin
# hurray for abstraction. The nameservice directoryservice provider can
# handle everything we need. super.
end

Просмотреть файл

@ -42,6 +42,9 @@ class Puppet::Provider::Confine
for_binary
end
# Used for logging.
attr_accessor :label
def initialize(values)
values = [values] unless values.is_a?(Array)
@values = values
@ -61,7 +64,7 @@ class Puppet::Provider::Confine
def valid?
values.each do |value|
unless pass?(value)
Puppet.debug message(value)
Puppet.debug(label + ": " + message(value))
return false
end
end

Просмотреть файл

@ -24,8 +24,13 @@ class Puppet::Provider::Confine::Variable < Puppet::Provider::Confine
@facter_value
end
def initialize(values)
super
@values = @values.collect { |v| v.to_s.downcase }
end
def message(value)
"facter value '%s' for '%s' not in required list '%s'" % [value, self.name, values.join(",")]
"facter value '%s' for '%s' not in required list '%s'" % [test_value, self.name, values.join(",")]
end
# Compare the passed-in value to the retrieved value.
@ -35,10 +40,16 @@ class Puppet::Provider::Confine::Variable < Puppet::Provider::Confine
def reset
# Reset the cache. We want to cache it during a given
# run, but across runs.
# run, but not across runs.
@facter_value = nil
end
def valid?
@values.include?(test_value.to_s.downcase)
ensure
reset
end
private
def setting?

Просмотреть файл

@ -19,10 +19,13 @@ class Puppet::Provider::ConfineCollection
confine.name = test
@confines << confine
end
@confines[-1].label = self.label
end
end
def initialize
attr_reader :label
def initialize(label)
@label = label
@confines = []
end

Просмотреть файл

@ -7,7 +7,7 @@ module Puppet::Provider::Confiner
def confine_collection
unless defined?(@confine_collection)
@confine_collection = Puppet::Provider::ConfineCollection.new
@confine_collection = Puppet::Provider::ConfineCollection.new(self.to_s)
end
@confine_collection
end

Просмотреть файл

@ -16,8 +16,9 @@ require 'puppet/provider/nameservice/directoryservice'
Puppet::Type.type(:group).provide :directoryservice, :parent => Puppet::Provider::NameService::DirectoryService do
desc "Group management using DirectoryService on OS X."
commands :dscl => "/usr/bin/dscl"
confine :operatingsystem => :darwin
#defaultfor :operatingsystem => :darwin
defaultfor :operatingsystem => :darwin
has_feature :manages_members
end

Просмотреть файл

@ -0,0 +1,199 @@
#--
# Copyright (C) 2008 Jeffrey J McCune.
# This program and entire repository is free software; you can
# redistribute it and/or modify it under the terms of the GNU
# General Public License as published by the Free Software
# Foundation; either version 2 of the License, or any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
# Author: Jeff McCune <mccune.jeff@gmail.com>
require 'tempfile'
Puppet::Type.type(:mcx).provide :mcxcontent, :parent => Puppet::Provider do
desc "MCX Settings management using DirectoryService on OS X.
This provider manages the entire MCXSettings attribute available
to some directory services nodes. This management is 'all or nothing'
in that discrete application domain key value pairs are not managed
by this provider.
It is recommended to use WorkGroup Manager to configure Users, Groups,
Computers, or ComputerLists, then use 'ralsh mcx' to generate a puppet
manifest from the resulting configuration.
Original Author: Jeff McCune (mccune.jeff@gmail.com)"
# This provides a mapping of puppet types to DirectoryService
# type strings.
TypeMap = {
:user => "Users",
:group => "Groups",
:computer => "Computers",
:computerlist => "ComputerLists",
}
class MCXContentProviderException < Exception
end
commands :dscl => "/usr/bin/dscl"
confine :operatingsystem => :darwin
defaultfor :operatingsystem => :darwin
# self.instances is all important.
# This is the only class method, it returns
# an array of instances of this class.
def self.instances
mcx_list = []
for ds_type in TypeMap.keys
ds_path = "/Local/Default/#{TypeMap[ds_type]}"
output = dscl 'localhost', '-list', ds_path
member_list = output.split
for ds_name in member_list
content = mcxexport(ds_type, ds_name)
if content.empty?
Puppet.debug "/#{TypeMap[ds_type]}/#{ds_name} has no MCX data."
else
# This node has MCX data.
rsrc = self.new(:name => "/#{TypeMap[ds_type]}/#{ds_name}",
:ds_type => ds_type,
:ds_name => ds_name,
:content => content)
mcx_list << rsrc
end
end
end
return mcx_list
end
private
# mcxexport is used by instances, and therefore
# a class method.
def self.mcxexport(ds_type, ds_name)
ds_t = TypeMap[ds_type]
ds_n = ds_name.to_s
ds_path = "/Local/Default/#{ds_t}/#{ds_n}"
dscl 'localhost', '-mcxexport', ds_path
end
def mcximport(ds_type, ds_name, val)
ds_t = TypeMap[ds_type]
ds_n = ds_name.to_s
ds_path = "/Local/Default/#{ds_t}/#{ds_name}"
tmp = Tempfile.new('puppet_mcx')
begin
tmp << val
tmp.flush
dscl 'localhost', '-mcximport', ds_path, tmp.path
ensure
tmp.close
tmp.unlink
end
end
# Given the resource name string, parse ds_type out.
def parse_type(name)
tmp = name.split('/')[1]
if ! tmp.is_a? String
raise MCXContentProviderException,
"Coult not parse ds_type from resource name '#{name}'. Specify with ds_type parameter."
end
# De-pluralize and downcase.
tmp = tmp.chop.downcase.to_sym
if not TypeMap.keys.member? tmp
raise MCXContentProviderException,
"Coult not parse ds_type from resource name '#{name}'. Specify with ds_type parameter."
end
return tmp
end
# Given the resource name string, parse ds_name out.
def parse_name(name)
ds_name = name.split('/')[2]
if ! ds_name.is_a? String
raise MCXContentProviderException,
"Could not parse ds_name from resource name '#{name}'. Specify with ds_name parameter."
end
return ds_name
end
# Gather ds_type and ds_name from resource or
# parse it out of the name.
# This is a private instance method, not a class method.
def get_dsparams
ds_type = resource[:ds_type]
if ds_type.nil?
ds_type = parse_type(resource[:name])
end
raise MCXContentProviderException unless TypeMap.keys.include? ds_type.to_sym
ds_name = resource[:ds_name]
if ds_name.nil?
ds_name = parse_name(resource[:name])
end
rval = {
:ds_type => ds_type.to_sym,
:ds_name => ds_name,
}
return rval
end
public
def create
self.content=(resource[:content])
end
def destroy
ds_parms = get_dsparams
ds_t = TypeMap[ds_parms[:ds_type]]
ds_n = ds_parms[:ds_name].to_s
ds_path = "/Local/Default/#{ds_t}/#{ds_n}"
dscl 'localhost', '-mcxdelete', ds_path
end
def exists?
# JJM Just re-use the content method and see if it's empty.
begin
mcx = content
rescue Puppet::ExecutionFailure => e
return false
end
has_mcx = ! mcx.empty?
return has_mcx
end
def content
ds_parms = get_dsparams
mcx = self.class.mcxexport(ds_parms[:ds_type],
ds_parms[:ds_name])
return mcx
end
def content=(value)
# dscl localhost -mcximport
ds_parms = get_dsparams
mcx = mcximport(ds_parms[:ds_type],
ds_parms[:ds_name],
resource[:content])
return mcx
end
end

Просмотреть файл

@ -14,6 +14,8 @@
require 'puppet'
require 'puppet/provider/nameservice'
require 'facter/util/plist'
class Puppet::Provider::NameService
class DirectoryService < Puppet::Provider::NameService
@ -26,6 +28,7 @@ class DirectoryService < Puppet::Provider::NameService
attr_writer :ds_path
end
# JJM 2007-07-24: Not yet sure what initvars() does. I saw it in netinfo.rb
# I do know, however, that it makes methods "work" =)
# e.g. addcmd isn't available if this method call isn't present.
@ -36,9 +39,9 @@ class DirectoryService < Puppet::Provider::NameService
initvars()
commands :dscl => "/usr/bin/dscl"
commands :dseditgroup => "/usr/sbin/dseditgroup"
confine :operatingsystem => :darwin
# JJM FIXME: This will need to be the default around October 2007.
# defaultfor :operatingsystem => :darwin
defaultfor :operatingsystem => :darwin
# JJM 2007-07-25: This map is used to map NameService attributes to their
@ -55,6 +58,10 @@ class DirectoryService < Puppet::Provider::NameService
'UniqueID' => :uid,
'RealName' => :comment,
'Password' => :password,
'GeneratedUID' => :guid,
'IPAddress' => :ip_address,
'ENetAddress' => :en_address,
'GroupMembership' => :members,
}
# JJM The same table as above, inverted.
@@ns_to_ds_attribute_map = {
@ -65,16 +72,19 @@ class DirectoryService < Puppet::Provider::NameService
:uid => 'UniqueID',
:comment => 'RealName',
:password => 'Password',
:guid => 'GeneratedUID',
:en_address => 'ENetAddress',
:ip_address => 'IPAddress',
:members => 'GroupMembership',
}
@@password_hash_dir = "/var/db/shadow/hash"
def self.instances
# JJM Class method that provides an array of instance objects of this
# type.
# JJM: Properties are dependent on the Puppet::Type we're managine.
type_property_array = [:name] + @resource_type.validproperties
# JJM: No sense reporting the password. It's hashed.
type_property_array.delete(:password) if type_property_array.include? :password
# Create a new instance of this Puppet::Type for each object present
# on the system.
@ -119,7 +129,7 @@ class DirectoryService < Puppet::Provider::NameService
all_present_str_array = list_all_present()
# JJM: Return nil if the named object isn't present.
# NBK: shortcut the process if the resource is missing
return nil unless all_present_str_array.include? resource_name
dscl_vector = get_exec_preamble("-read", resource_name)
@ -132,44 +142,37 @@ class DirectoryService < Puppet::Provider::NameService
# JJM: We need a new hash to return back to our caller.
attribute_hash = Hash.new
# JJM: First, the output string goes into an array.
# Then, the each array element is split
# If you want to figure out what this is doing, I suggest
# ruby-debug, and stepping through it.
dscl_output.split("\n").each do |line|
# JJM: Split the attribute name and the list of values.
ds_attribute, ds_values_string = line.split(':')
# Split sets the values to nil if there's nothing after the :
ds_values_string ||= ""
# JJM: skip this attribute line if the Puppet::Type doesn't care about it.
dscl_plist = Plist.parse_xml(dscl_output)
dscl_plist.keys().each do |key|
ds_attribute = key.sub("dsAttrTypeStandard:", "")
next unless (@@ds_to_ns_attribute_map.keys.include?(ds_attribute) and type_properties.include? @@ds_to_ns_attribute_map[ds_attribute])
# JJM: We asked dscl to output url encoded values so we're able
# to machine parse on whitespace. We need to urldecode:
# " Jeff%20McCune John%20Doe " => ["Jeff McCune", "John Doe"]
ds_value_array = ds_values_string.scan(/[^\s]+/).collect do |v|
url_decoded_value = CGI::unescape v
if url_decoded_value =~ /^[-0-9]+$/
url_decoded_value.to_i
else
url_decoded_value
end
ds_value = dscl_plist[key]
case @@ds_to_ns_attribute_map[ds_attribute]
when :members:
ds_value = ds_value # only members uses arrays so far
when :gid, :uid:
# OS X stores objects like uid/gid as strings.
# Try casting to an integer for these cases to be
# consistent with the other providers and the group type
# validation
begin
ds_value = Integer(ds_value[0])
rescue ArgumentError
ds_value = ds_value[0]
end
else ds_value = ds_value[0]
end
# JJM: Finally, we're able to build up our attribute hash.
# Remember, the hash is keyed by NameService attribute names,
# not DirectoryService attribute names.
# NOTE: We're also sort of cheating here... DirectoryService
# is robust enough to allow multiple values for almost every
# attribute in the system. Traditional NameService things
# really don't handle this case, so we'll always pull thet first
# value returned from DirectoryService.
# THERE MAY BE AN ORDERING ISSUE HERE, but I think it's ok...
attribute_hash[@@ds_to_ns_attribute_map[ds_attribute]] = ds_value_array[0]
attribute_hash[@@ds_to_ns_attribute_map[ds_attribute]] = ds_value
end
return attribute_hash
# NBK: need to read the existing password here as it's not actually
# stored in the user record. It is stored at a path that involves the
# UUID of the user record for non-Mobile local acccounts.
# Mobile Accounts are out of scope for this provider for now
if @resource_type.validproperties.include?(:password)
attribute_hash[:password] = self.get_password(attribute_hash[:guid])
end
return attribute_hash
end
def self.get_exec_preamble(ds_action, resource_name = nil)
@ -181,7 +184,7 @@ class DirectoryService < Puppet::Provider::NameService
# We EXPECT name to be @resource[:name] when called from an instance object.
# There are two ways to specify paths in 10.5. See man dscl.
command_vector = [ command(:dscl), "-url", "." ]
command_vector = [ command(:dscl), "-plist", "." ]
# JJM: The actual action to perform. See "man dscl"
# Common actiosn: -create, -delete, -merge, -append, -passwd
command_vector << ds_action
@ -196,6 +199,52 @@ class DirectoryService < Puppet::Provider::NameService
# e.g. 'dscl / -create /Users/mccune'
return command_vector
end
def self.set_password(resource_name, guid, password_hash)
password_hash_file = "#{@@password_hash_dir}/#{guid}"
begin
File.open(password_hash_file, 'w') { |f| f.write(password_hash)}
rescue Errno::EACCES => detail
raise Puppet::Error, "Could not write to password hash file: #{detail}"
end
# NBK: For shadow hashes, the user AuthenticationAuthority must contain a value of
# ";ShadowHash;". The LKDC in 10.5 makes this more interesting though as it
# will dynamically generate ;Kerberosv5;;username@LKDC:SHA1 attributes if
# missing. Thus we make sure we only set ;ShadowHash; if it is missing, and
# we can do this with the merge command. This allows people to continue to
# use other custom AuthenticationAuthority attributes without stomping on them.
#
# There is a potential problem here in that we're only doing this when setting
# the password, and the attribute could get modified at other times while the
# hash doesn't change and so this doesn't get called at all... but
# without switching all the other attributes to merge instead of create I can't
# see a simple enough solution for this that doesn't modify the user record
# every single time. This should be a rather rare edge case. (famous last words)
dscl_vector = self.get_exec_preamble("-merge", resource_name)
dscl_vector << "AuthenticationAuthority" << ";ShadowHash;"
begin
dscl_output = execute(dscl_vector)
rescue Puppet::ExecutionFailure => detail
raise Puppet::Error, "Could not set AuthenticationAuthority."
end
end
def self.get_password(guid)
password_hash = nil
password_hash_file = "#{@@password_hash_dir}/#{guid}"
# TODO: sort out error conditions?
if File.exists?(password_hash_file)
if not File.readable?(password_hash_file)
raise Puppet::Error("Could not read password hash file at #{password_hash_file} for #{@resource[:name]}")
end
f = File.new(password_hash_file)
password_hash = f.read
f.close
end
password_hash
end
def ensure=(ensure_value)
super
@ -206,7 +255,6 @@ class DirectoryService < Puppet::Provider::NameService
if ensure_value == :present
@resource.class.validproperties.each do |name|
next if name == :ensure
# LAK: We use property.sync here rather than directly calling
# the settor method because the properties might do some kind
# of conversion. In particular, the user gid property might
@ -223,79 +271,130 @@ class DirectoryService < Puppet::Provider::NameService
end
def password=(passphrase)
# JJM: Setting the password is a special case. We don't just
# set the attribute because we need to update the password
# databases.
# FIRST, make sure the AuthenticationAuthority is ;ShadowHash; If
# we don't do this, we don't get a shadow hash account. ("Obviously...")
dscl_vector = self.class.get_exec_preamble("-create", @resource[:name])
dscl_vector << "AuthenticationAuthority" << ";ShadowHash;"
begin
dscl_output = execute(dscl_vector)
rescue Puppet::ExecutionFailure => detail
raise Puppet::Error, "Could not set AuthenticationAuthority."
end
# JJM: Second, we need to actually set the password. dscl does
# some magic, creating the proper hash for us based on the
# AuthenticationAuthority attribute, set above.
dscl_vector = self.class.get_exec_preamble("-passwd", @resource[:name])
dscl_vector << passphrase
# JJM: Should we not log the password string? This may be a security
# risk...
begin
dscl_output = execute(dscl_vector)
rescue Puppet::ExecutionFailure => detail
raise Puppet::Error, "Could not set password using command vector: %{dscl_vector.inspect}"
end
exec_arg_vector = self.class.get_exec_preamble("-read", @resource.name)
exec_arg_vector << @@ns_to_ds_attribute_map[:guid]
begin
guid_output = execute(exec_arg_vector)
guid_plist = Plist.parse_xml(guid_output)
# Although GeneratedUID like all DirectoryService values can be multi-valued
# according to the schema, in practice user accounts cannot have multiple UUIDs
# otherwise Bad Things Happen, so we just deal with the first value.
guid = guid_plist["dsAttrTypeStandard:#{@@ns_to_ds_attribute_map[:guid]}"][0]
self.class.set_password(@resource.name, guid, passphrase)
rescue Puppet::ExecutionFailure => detail
raise Puppet::Error, "Could not set %s on %s[%s]: %s" % [param, @resource.class.name, @resource.name, detail]
end
end
# JJM: nameservice.rb defines methods for each attribute of the type.
# We implement these methods here, by implementing get() and set()
# See the resource_type= method defined in nameservice.rb
# I'm not sure what the implications are of doing things this way.
# It was a bit difficult to sort out what was happening in my head,
# but ruby-debug makes this process much more transparent.
#
def set(property, value)
# JJM: As it turns out, the set method defined in our parent class
# is fine. It just calls the modifycmd() method, which
# I'll implement here.
super
end
# NBK: we override @parent.set as we need to execute a series of commands
# to deal with array values, rather than the single command nameservice.rb
# expects to be returned by modifycmd. Thus we don't bother defining modifycmd.
def get(param)
hash = getinfo(false)
if hash
return hash[param]
def set(param, value)
self.class.validate(param, value)
current_members = @property_value_cache_hash[:members]
if param == :members
# If we are meant to be authoritative for the group membership
# then remove all existing members who haven't been specified
# in the manifest.
if @resource[:auth_membership] and not current_members.nil?
remove_unwanted_members(current_members, value)
end
# if they're not a member, make them one.
add_members(current_members, value)
else
return :absent
exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name])
# JJM: The following line just maps the NS name to the DS name
# e.g. { :uid => 'UniqueID' }
exec_arg_vector << @@ns_to_ds_attribute_map[symbolize(param)]
# JJM: The following line sends the actual value to set the property to
exec_arg_vector << value.to_s
begin
execute(exec_arg_vector)
rescue Puppet::ExecutionFailure => detail
raise Puppet::Error, "Could not set %s on %s[%s]: %s" % [param, @resource.class.name, @resource.name, detail]
end
end
end
def modifycmd(property, value)
# JJM: This method will assemble a exec vector which modifies
# a single property and it's value using dscl.
# JJM: With /usr/bin/dscl, the -create option will destroy an
# existing property record if it exists
# NBK: we override @parent.create as we need to execute a series of commands
# to create objects with dscl, rather than the single command nameservice.rb
# expects to be returned by addcmd. Thus we don't bother defining addcmd.
def create
if exists?
info "already exists"
# The object already exists
return nil
end
# NBK: First we create the object with a known guid so we can set the contents
# of the password hash if required
# Shelling out sucks, but for a single use case it doesn't seem worth
# requiring people install a UUID library that doesn't come with the system.
# This should be revisited if Puppet starts managing UUIDs for other platform
# user records.
guid = %x{/usr/bin/uuidgen}.chomp
exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name])
# JJM: The following line just maps the NS name to the DS name
# e.g. { :uid => 'UniqueID' }
exec_arg_vector << @@ns_to_ds_attribute_map[symbolize(property)]
# JJM: The following line sends the actual value to set the property to
exec_arg_vector << value.to_s
return exec_arg_vector
exec_arg_vector << @@ns_to_ds_attribute_map[:guid] << guid
begin
execute(exec_arg_vector)
rescue Puppet::ExecutionFailure => detail
raise Puppet::Error, "Could not set GeneratedUID for %s %s: %s" %
[@resource.class.name, @resource.name, detail]
end
if value = @resource.should(:password) and value != ""
self.class.set_password(@resource[:name], guid, value)
end
# Now we create all the standard properties
Puppet::Type.type(@resource.class.name).validproperties.each do |property|
next if property == :ensure
if value = @resource.should(property) and value != ""
if property == :members
add_members(nil, value)
else
exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name])
exec_arg_vector << @@ns_to_ds_attribute_map[symbolize(property)]
next if property == :password # skip setting the password here
exec_arg_vector << value.to_s
begin
execute(exec_arg_vector)
rescue Puppet::ExecutionFailure => detail
raise Puppet::Error, "Could not create %s %s: %s" %
[@resource.class.name, @resource.name, detail]
end
end
end
end
end
def addcmd
# JJM 2007-07-24:
# - addcmd returns an array to be executed to create a new object.
# - This method is probably being called from the
# ensure= method in nameservice.rb, or here...
# - This should only be called if the object doesn't exist.
# JJM: Blame nameservice.rb for the terse method name. =)
#
self.class.get_exec_preamble("-create", @resource[:name])
def remove_unwanted_members(current_members, new_members)
current_members.each do |member|
if not value.include?(member)
cmd = [:dseditgroup, "-o", "edit", "-n", ".", "-d", member, @resource[:name]]
begin
execute(cmd)
rescue Puppet::ExecutionFailure => detail
raise Puppet::Error, "Could not set %s on %s[%s]: %s" % [param, @resource.class.name, @resource.name, detail]
end
end
end
end
def add_members(current_members, new_members)
new_members.each do |user|
if current_members.nil? or not current_members.include?(user)
cmd = [:dseditgroup, "-o", "edit", "-n", ".", "-a", user, @resource[:name]]
begin
execute(cmd)
rescue Puppet::ExecutionFailure => detail
raise Puppet::Error, "Could not set %s on %s[%s]: %s" % [param, @resource.class.name, @resource.name, detail]
end
end
end
end
def deletecmd
@ -341,9 +440,13 @@ class DirectoryService < Puppet::Provider::NameService
# list, then report on the remaining list. Pretty whacky, ehh?
type_properties = [:name] + self.class.resource_type.validproperties
type_properties.delete(:ensure) if type_properties.include? :ensure
type_properties << :guid # append GeneratedUID so we just get the report here
@property_value_cache_hash = self.class.single_report(@resource[:name], *type_properties)
[:uid, :gid].each do |param|
@property_value_cache_hash[param] = @property_value_cache_hash[param].to_i if @property_value_cache_hash and @property_value_cache_hash.include?(param)
end
end
return @property_value_cache_hash
end
end
end
end

Просмотреть файл

@ -12,9 +12,6 @@
# As a result, we store installed .app.dmg file names
# in /var/db/.puppet_appdmg_installed_<name>
# require 'ruby-debug'
# Debugger.start
require 'puppet/provider/package'
Puppet::Type.type(:package).provide(:appdmg, :parent => Puppet::Provider::Package) do
desc "Package management which copies application bundles to a target."

Просмотреть файл

@ -99,10 +99,16 @@ Puppet::Type.type(:package).provide :apt, :parent => :dpkg, :source => :dpkg do
end
def uninstall
if @resource[:responsefile]
self.run_preseed
end
aptget "-y", "-q", :remove, @resource[:name]
end
def purge
if @resource[:responsefile]
self.run_preseed
end
aptget '-y', '-q', :remove, '--purge', @resource[:name]
# workaround a "bug" in apt, that already removed packages are not purged
super

Просмотреть файл

@ -4,6 +4,8 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr
desc "RPM packaging support; should work anywhere with a working ``rpm``
binary."
has_feature :versionable
# The query format by which we identify installed packages
NEVRAFORMAT = "%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}"
NEVRA_FIELDS = [:name, :epoch, :version, :release, :arch]

Просмотреть файл

@ -100,5 +100,9 @@ Puppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do
# Install in yum can be used for update, too
self.install
end
end
def purge
yum "-y", :erase, @resource[:name]
end
end

Просмотреть файл

@ -39,6 +39,15 @@ def pkg_lists(my):
my.doTsSetup()
my.doRpmDBSetup()
# Yum 2.2/2.3 python libraries require a couple of extra function calls to setup package sacks.
# They also don't have a __version__ attribute
try:
yumver = yum.__version__
except AttributeError:
my.doRepoSetup()
my.doSackSetup()
return my.doPackageLists('updates')
def shell_out():

Просмотреть файл

@ -2,27 +2,37 @@
#
# author Brice Figureau <brice-puppet@daysofwonder.com>
Puppet::Type.type(:service).provide :daemontools, :parent => :base do
desc "Daemontools service management.
This provider manages daemons running supervised by D.J.Bernstein daemontools.
It tries to detect the service directory, with by order of preference:
* /service
* /etc/service
* /var/lib/svscan
The daemon directory should be placed in a directory that can be
by default in:
* /var/lib/service
* /etc
or this can be overriden in the service resource parameters:
desc """
Daemontools service management.
This provider manages daemons running supervised by D.J.Bernstein daemontools.
It tries to detect the service directory, with by order of preference::
* /service
* /etc/service
* /var/lib/svscan
The daemon directory should be placed in a directory that can be
by default in::
* /var/lib/service
* /etc
or this can be overriden in the service resource parameters::
service {
\"myservice\":
provider => \"daemontools\", path => \"/path/to/daemons\";
}
This provider supports out of the box:
* start/stop (mapped to enable/disable)
* enable/disable
* restart
* status"
This provider supports out of the box::
* start/stop (mapped to enable/disable)
* enable/disable
* restart
* status
"""
commands :svc => "/usr/bin/svc"
commands :svstat => "/usr/bin/svstat"

Просмотреть файл

@ -35,7 +35,7 @@ Puppet::Type.type(:service).provide :gentoo, :parent => :init do
return :false unless line
# If it's enabled then it will print output showing service | runlevel
if output =~ /#{@resource[:name]}\s*|\s*default/
if output =~ /#{@resource[:name]}\s*\|\s*default/
return :true
else
return :false

Просмотреть файл

@ -24,24 +24,30 @@ Puppet::Type.type(:service).provide :init, :parent => :base do
# List all services of this type.
def self.instances
path = self.defpath
unless FileTest.directory?(path)
Puppet.notice "Service path %s does not exist" % path
next
end
check = [:ensure]
if public_method_defined? :enabled?
check << :enable
end
Dir.entries(path).reject { |e|
fullpath = File.join(path, e)
e =~ /^\./ or ! FileTest.executable?(fullpath)
}.collect do |name|
new(:name => name, :path => path)
self.defpath = [self.defpath] unless self.defpath.is_a? Array
instances = []
self.defpath.each do |path|
unless FileTest.directory?(path)
Puppet.debug "Service path %s does not exist" % path
next
end
check = [:ensure]
if public_method_defined? :enabled?
check << :enable
end
Dir.entries(path).each do |name|
fullpath = File.join(path, name)
next if name =~ /^\./
next if not FileTest.executable?(fullpath)
instances << new(:name => name, :path => path)
end
end
instances
end
# Mark that our init script supports 'status' commands.

Просмотреть файл

@ -0,0 +1,194 @@
require 'facter/util/plist'
Puppet::Type.type(:service).provide :launchd, :parent => :base do
desc "launchd service management framework.
This provider manages launchd jobs, the default service framework for
Mac OS X, that has also been open sourced by Apple for possible use on
other platforms.
See:
* http://developer.apple.com/macosx/launchd.html
* http://launchd.macosforge.org/
This provider reads plists out of the following directories:
* /System/Library/LaunchDaemons
* /System/Library/LaunchAgents
* /Library/LaunchDaemons
* /Library/LaunchAgents
and builds up a list of services based upon each plists \"Label\" entry.
This provider supports:
* ensure => running/stopped,
* enable => true/false
* status
* restart
Here is how the Puppet states correspond to launchd states:
* stopped => job unloaded
* started => job loaded
* enabled => 'Disable' removed from job plist file
* disabled => 'Disable' added to job plist file
Note that this allows you to do something launchctl can't do, which is to
be in a state of \"stopped/enabled\ or \"running/disabled\".
"
commands :launchctl => "/bin/launchctl"
defaultfor :operatingsystem => :darwin
confine :operatingsystem => :darwin
has_feature :enableable
Launchd_Paths = ["/Library/LaunchAgents",
"/Library/LaunchDaemons",
"/System/Library/LaunchAgents",
"/System/Library/LaunchDaemons",]
# returns a label => path map for either all jobs, or just a single
# job if the label is specified
def self.jobsearch(label=nil)
label_to_path_map = {}
Launchd_Paths.each do |path|
if FileTest.exists?(path)
Dir.entries(path).each do |f|
next if f =~ /^\..*$/
next if FileTest.directory?(f)
fullpath = File.join(path, f)
job = Plist::parse_xml(fullpath)
if job and job.has_key?("Label")
if job["Label"] == label
return { label => fullpath }
else
label_to_path_map[job["Label"]] = fullpath
end
end
end
end
end
# if we didn't find the job above and we should have, error.
if label
raise Puppet::Error.new("Unable to find launchd plist for job: #{label}")
end
# if returning all jobs
label_to_path_map
end
def self.instances
jobs = self.jobsearch
jobs.keys.collect do |job|
new(:name => job, :provider => :launchd, :path => jobs[job])
end
end
# finds the path for a given label and returns the path and parsed plist
# as an array of [path, plist]. Note plist is really a Hash here.
def plist_from_label(label)
job = self.class.jobsearch(label)
job_path = job[label]
job_plist = Plist::parse_xml(job_path)
if not job_plist
raise Puppet::Error.new("Unable to parse launchd plist at path: #{job_path}")
end
[job_path, job_plist]
end
def status
# launchctl list <jobname> exits zero if the job is loaded
# and non-zero if it isn't. Simple way to check...
begin
launchctl :list, resource[:name]
return :running
rescue Puppet::ExecutionFailure
return :stopped
end
end
# start the service. To get to a state of running/enabled, we need to
# conditionally enable at load, then disable by modifying the plist file
# directly.
def start
job_path, job_plist = plist_from_label(resource[:name])
did_enable_job = false
cmds = []
cmds << :launchctl << :load
if self.enabled? == :false # launchctl won't load disabled jobs
cmds << "-w"
did_enable_job = true
end
cmds << job_path
begin
execute(cmds)
rescue Puppet::ExecutionFailure
raise Puppet::Error.new("Unable to start service: %s at path: %s" % [resource[:name], job_path])
end
# As load -w clears the Disabled flag, we need to add it in after
if did_enable_job and resource[:enable] == :false
self.disable
end
end
def stop
job_path, job_plist = plist_from_label(resource[:name])
did_disable_job = false
cmds = []
cmds << :launchctl << :unload
if self.enabled? == :true # keepalive jobs can't be stopped without disabling
cmds << "-w"
did_disable_job = true
end
cmds << job_path
begin
execute(cmds)
rescue Puppet::ExecutionFailure
raise Puppet::Error.new("Unable to stop service: %s at path: %s" % [resource[:name], job_path])
end
# As unload -w sets the Disabled flag, we need to add it in after
if did_disable_job and resource[:enable] == :true
self.enable
end
end
# launchd jobs are enabled by default. They are only disabled if the key
# "Disabled" is set to true, but it can also be set to false to enable it.
def enabled?
job_path, job_plist = plist_from_label(resource[:name])
if job_plist.has_key?("Disabled")
if job_plist["Disabled"] # inverse of disabled is enabled
return :false
end
end
return :true
end
# enable and disable are a bit hacky. We write out the plist with the appropriate value
# rather than dealing with launchctl as it is unable to change the Disabled flag
# without actually loading/unloading the job.
def enable
job_path, job_plist = plist_from_label(resource[:name])
if self.enabled? == :false
job_plist.delete("Disabled")
Plist::Emit.save_plist(job_plist, job_path)
end
end
def disable
job_path, job_plist = plist_from_label(resource[:name])
job_plist["Disabled"] = true
Plist::Emit.save_plist(job_plist, job_path)
end
end

Просмотреть файл

@ -2,26 +2,36 @@
#
# author Brice Figureau <brice-puppet@daysofwonder.com>
Puppet::Type.type(:service).provide :runit, :parent => :daemontools do
desc "Runit service management.
This provider manages daemons running supervised by Runit.
It tries to detect the service directory, with by order of preference:
* /service
* /var/service
* /etc/service
The daemon directory should be placed in a directory that can be
by default in:
* /etc/sv
or this can be overriden in the service resource parameters:
desc """
Runit service management.
This provider manages daemons running supervised by Runit.
It tries to detect the service directory, with by order of preference::
* /service
* /var/service
* /etc/service
The daemon directory should be placed in a directory that can be
by default in::
* /etc/sv
or this can be overriden in the service resource parameters::
service {
\"myservice\":
provider => \"runit\", path => \"/path/to/daemons\";
}
This provider supports out of the box:
* start/stop
* enable/disable
* restart
* status"
This provider supports out of the box::
* start/stop
* enable/disable
* restart
* status
"""
commands :sv => "/usr/bin/sv"

Просмотреть файл

@ -19,7 +19,7 @@ Puppet::Type.type(:ssh_authorized_key).provide(:parsed,
if record[:options].nil?
record[:options] = [:absent]
else
record[:options] = record[:options].split(',')
record[:options] = Puppet::Type::Ssh_authorized_key::ProviderParsed.parse_options(record[:options])
end
},
:pre_gen => proc { |record|
@ -71,5 +71,25 @@ Puppet::Type.type(:ssh_authorized_key).provide(:parsed,
File.chown(Puppet::Util.uid(user), nil, @property_hash[:target])
end
end
# parse sshv2 option strings, wich is a comma separated list of
# either key="values" elements or bare-word elements
def self.parse_options(options)
result = []
scanner = StringScanner.new(options)
while !scanner.eos?
scanner.skip(/[ \t]*/)
# scan a long option
if out = scanner.scan(/[-a-z0-9A-Z_]+=\".*?\"/) or out = scanner.scan(/[-a-z0-9A-Z_]+/)
result << out
else
# found an unscannable token, let's abort
break
end
# eat a comma
scanner.skip(/[ \t]*,[ \t]*/)
end
result
end
end

Просмотреть файл

@ -22,17 +22,15 @@ Puppet::Type.type(:user).provide :user_role_add, :parent => :useradd do
value !~ /\s/
end
has_features :manages_homedir, :allows_duplicates, :manages_solaris_rbac
if Puppet.features.libshadow?
has_feature :manages_passwords
end
has_features :manages_homedir, :allows_duplicates, :manages_solaris_rbac, :manages_passwords
#must override this to hand the keyvalue pairs
def add_properties
cmd = []
Puppet::Type.type(:user).validproperties.each do |property|
next if property == :ensure
#skip the password because we can't create it with the solaris useradd
next if [:ensure, :password].include?(property)
# 1680 Now you can set the hashed passwords on solaris:lib/puppet/provider/user/user_role_add.rb
# the value needs to be quoted, mostly because -c might
# have spaces in it
if value = @resource.should(property) and value != ""
@ -87,6 +85,10 @@ Puppet::Type.type(:user).provide :user_role_add, :parent => :useradd do
else
run(addcmd, "create")
end
# added to handle case when password is specified
if @resource[:password]
self.password = @resource[:password]
end
end
def destroy
@ -152,5 +154,40 @@ Puppet::Type.type(:user).provide :user_role_add, :parent => :useradd do
def keys=(keys_hash)
run([command(:modify)] + build_keys_cmd(keys_hash) << @resource[:name], "modify attribute key pairs")
end
#Read in /etc/shadow, find the line for this user (skipping comments, because who knows) and return the hashed pw (the second entry)
#No abstraction, all esoteric knowledge of file formats, yay
def password
#got perl?
if ary = File.readlines("/etc/shadow").reject { |r| r =~ /^[^\w]/}.collect { |l| l.split(':')[0..1] }.find { |user, passwd| user == @resource[:name] }
pass = ary[1]
end
pass
end
#Read in /etc/shadow, find the line for our used and rewrite it with the new pw
#Smooth like 80 grit
def password=(cryptopw)
begin
File.open("/etc/shadow", "r") do |shadow|
File.open("/etc/shadow_tmp", "w", 0600) do |shadow_tmp|
while line = shadow.gets do
line_arr = line.split(':')
if line_arr[0] == @resource[:name]
line_arr[1] = cryptopw
line = line_arr.join(':')
end
shadow_tmp.print line
end
end
end
File.rename("/etc/shadow_tmp", "/etc/shadow")
rescue => detail
fail "Could not write temporary shadow file: %s" % detail
ensure
# Make sure this *always* gets deleted
File.unlink("/etc/shadow_tmp") if File.exist?("/etc/shadow_tmp")
end
end
end

Просмотреть файл

@ -0,0 +1,56 @@
Puppet::Type.type(:zfs).provide(:solaris) do
desc "Provider for Solaris zfs."
commands :zfs => "/usr/sbin/zfs"
defaultfor :operatingsystem => :solaris
def add_properties
properties = []
Puppet::Type.type(:zfs).validproperties.each do |property|
next if property == :ensure
if value = @resource[property] and value != ""
properties << "-o" << "#{property}=#{value}"
end
end
properties
end
def arrayify_second_line_on_whitespace(text)
if second_line = text.split("\n")[1]
second_line.split("\s")
else
[]
end
end
def create
zfs *([:create] + add_properties + [@resource[:name]])
end
def delete
zfs(:destroy, @resource[:name])
end
def exists?
if zfs(:list).split("\n").detect { |line| line.split("\s")[0] == @resource[:name] }
true
else
false
end
end
[:mountpoint, :compression, :copies, :quota, :reservation, :sharenfs, :snapdir].each do |field|
define_method(field) do
#special knowledge of format
#the command returns values in this format with the header
#NAME PROPERTY VALUE SOURCE
arrayify_second_line_on_whitespace(zfs(:get, field, @resource[:name]))[2]
end
define_method(field.to_s + "=") do |should|
zfs(:set, "#{field}=#{should}", @resource[:name])
end
end
end

Просмотреть файл

@ -12,6 +12,7 @@ Puppet::Type.type(:zone).provide(:solaris) do
properties = {}
line.split(":").each_with_index { |value, index|
next unless fields[index]
properties[fields[index]] = value
}
@ -35,9 +36,8 @@ Puppet::Type.type(:zone).provide(:solaris) do
# Perform all of our configuration steps.
def configure
# If the thing is entirely absent, then we need to create the config.
str = %{create -b
set zonepath=%s
} % @resource[:path]
# Is there someway to get this on one line?
str = "create -b #{@resource[:create_args]}\nset zonepath=%s\n" % @resource[:path]
# Then perform all of our configuration steps. It's annoying
# that we need this much internal info on the resource.
@ -65,7 +65,11 @@ set zonepath=%s
end
def install
zoneadm :install
if @resource[:install_args]
zoneadm :install, @resource[:install_args].split(" ")
else
zoneadm :install
end
end
# Look up the current status.

Просмотреть файл

@ -0,0 +1,112 @@
Puppet::Type.type(:zpool).provide(:solaris) do
desc "Provider for Solaris zpool."
commands :zpool => "/usr/sbin/zpool"
defaultfor :operatingsystem => :solaris
def process_zpool_data(pool_array)
if pool_array == []
return Hash.new(:absent)
end
#get the name and get rid of it
pool = Hash.new([])
pool[:pool] = pool_array[0]
pool_array.shift
#order matters here :(
tmp = []
pool_array.reverse.each_with_index do |value, i|
case value
when "spares": pool[:spare] = tmp.reverse and tmp.clear
when "logs": pool[:log] = tmp.reverse and tmp.clear
when "mirror", "raidz1", "raidz2":
sym = value == "mirror" ? :mirror : :raidz
pool[sym].unshift(tmp.reverse.join(' '))
pool[:raid_parity] = "raidz2" if value == "raidz2"
tmp.clear
else
tmp << value
pool[:disk] = tmp.reverse if i == 0
end
end
pool
end
def get_pool_data
#this is all voodoo dependent on the output from zpool
zpool_data = %x{ zpool status #{@resource[:pool]}}.split("\n").select { |line| line.index("\t") == 0 }.collect { |l| l.strip.split("\s")[0] }
zpool_data.shift
zpool_data
end
def current_pool
unless (defined?(@current_pool) and @current_pool)
@current_pool = process_zpool_data(get_pool_data)
end
@current_pool
end
def flush
@current_pool= nil
end
#Adds log and spare
def build_named(name)
if prop = @resource[name.intern]
[name] + prop.collect { |p| p.split(' ') }.flatten
else
[]
end
end
#query for parity and set the right string
def raidzarity
@resource[:raid_parity] ? @resource[:raid_parity] : "raidz1"
end
#handle mirror or raid
def handle_multi_arrays(prefix, array)
array.collect{ |a| [prefix] + a.split(' ') }.flatten
end
#builds up the vdevs for create command
def build_vdevs
if disk = @resource[:disk]
disk.collect { |d| d.split(' ') }.flatten
elsif mirror = @resource[:mirror]
handle_multi_arrays("mirror", mirror)
elsif raidz = @resource[:raidz]
handle_multi_arrays(raidzarity, raidz)
end
end
def create
zpool(*([:create, @resource[:pool]] + build_vdevs + build_named("spare") + build_named("log")))
end
def delete
zpool :destroy, @resource[:pool]
end
def exists?
if current_pool[:pool] == :absent
false
else
true
end
end
[:disk, :mirror, :raidz, :log, :spare].each do |field|
define_method(field) do
current_pool[field]
end
define_method(field.to_s + "=") do |should|
Puppet.warning "NO CHANGES BEING MADE: zpool %s does not match, should be '%s' currently is '%s'" % [field, should, current_pool[field]]
end
end
end

Просмотреть файл

@ -0,0 +1,9 @@
class AddEnvironmentToHost < ActiveRecord::Migration
def self.up
add_column :hosts, :environment, :string
end
def self.down
remove_column :hosts, :environment
end
end

Просмотреть файл

@ -54,6 +54,7 @@ class Puppet::Rails::Schema
create_table :hosts do |t|
t.column :name, :string, :null => false
t.column :ip, :string
t.column :environment, :string
t.column :last_compile, :datetime
t.column :last_freshcheck, :datetime
t.column :last_report, :datetime

Просмотреть файл

@ -43,6 +43,10 @@ class Puppet::Rails::Host < ActiveRecord::Base
host.ip = ip
end
if env = node.environment
host.environment = env
end
# Store the facts into the database.
host.setfacts node.parameters

Просмотреть файл

@ -133,7 +133,7 @@ Signals
-------
The ``puppetd`` and ``puppetmasterd`` executables catch some signals for special
handling. Both daemons catch (``SIGHUP``), which forces the server to restart
tself. Predictably, interrupt and terminate (``SIGINT`` and ``SIGHUP``) will shut
tself. Predictably, interrupt and terminate (``SIGINT`` and ``SIGTERM``) will shut
down the server, whether it be an instance of ``puppetd`` or ``puppetmasterd``.
Sending the ``SIGUSR1`` signal to an instance of ``puppetd`` will cause it to

Просмотреть файл

@ -151,12 +151,14 @@ Puppet::Reports.register_report(:tagmail) do
reports.each do |emails, messages|
Puppet.info "Sending report to %s" % emails.join(", ")
# We need to open a separate process for every set of email addresses
IO.popen(Puppet[:sendmail] + " " + emails.join(" "), "w") do |p|
p.puts "From: #{Puppet[:reportfrom]}"
p.puts "Subject: Puppet Report for %s" % self.host
p.puts "To: " + emails.join(", ")
sync.synchronize do
IO.popen(Puppet[:sendmail] + " " + emails.join(" "), "w") do |p|
p.puts "From: #{Puppet[:reportfrom]}"
p.puts "Subject: Puppet Report for %s" % self.host
p.puts "To: " + emails.join(", ")
p.puts messages
p.puts messages
end
end
end
rescue => detail
@ -174,5 +176,11 @@ Puppet::Reports.register_report(:tagmail) do
# Don't bother waiting for the pid to return.
Process.detach(pid)
end
def sync
unless defined?(@sync)
@sync = Sync.new
end
end
end

Просмотреть файл

@ -53,12 +53,10 @@ class Puppet::Transaction::Change
# The transaction catches any exceptions here.
events = @property.sync
if events.nil?
return nil
end
if events.is_a?(Array)
events = [(@property.name.to_s + "_changed").to_sym]
elsif events.is_a?(Array)
if events.empty?
return nil
events = [(@property.name.to_s + "_changed").to_sym]
end
else
events = [events]

Просмотреть файл

@ -53,6 +53,7 @@ module Puppet
Puppet.debug "Defining %s on %s" % [param, ref]
trans[param] = value
}
trans.catalog = self.catalog
Puppet::Type::Component.create(trans)
end

Просмотреть файл

@ -2,16 +2,16 @@
# Copyright (C) 2008 Red Hat Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
# General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Publicretu
# You should have received a copy of the GNU General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#

Просмотреть файл

@ -87,7 +87,7 @@ Puppet::Type.newtype(:component) do
@reference = Puppet::ResourceReference.new(:component, @title)
if catalog and ! catalog.resource[@reference.to_s]
if catalog and ! catalog.resource(@reference.to_s)
catalog.alias(self, @reference.to_s)
end
end

Просмотреть файл

@ -0,0 +1,61 @@
Puppet::Type.newtype(:computer) do
@doc = "Computer object management using DirectoryService on OS X.
Note that these are distinctly different kinds of objects to 'hosts',
as they require a MAC address and can have all sorts of policy attached to
them.
This provider only manages Computer objects in the local directory service
domain, not in remote directories.
If you wish to manage /etc/hosts on Mac OS X, then simply use the host
type as per other platforms.
This type primarily exists to create localhost Computer objects that MCX
policy can then be attached to."
# ensurable
# We autorequire the computer object in case it is being managed at the
# file level by Puppet.
autorequire(:file) do
if self[:name]
"/var/db/dslocal/nodes/Default/computers/#{self[:name]}.plist"
else
nil
end
end
newproperty(:ensure, :parent => Puppet::Property::Ensure) do
desc "Control the existences of this computer record. Set this attribute to
``present`` to ensure the computer record exists. Set it to ``absent``
to delete any computer records with this name"
newvalue(:present) do
provider.create
end
newvalue(:absent) do
provider.delete
end
end
newparam(:name) do
desc "The authoritative 'short' name of the computer record."
isnamevar
end
newparam(:realname) do
desc "The 'long' name of the computer record."
end
newproperty(:en_address) do
desc "The MAC address of the primary network interface. Must match en0."
end
newproperty(:ip_address) do
desc "The IP Address of the Computer object."
end
end

Просмотреть файл

@ -854,7 +854,7 @@ module Puppet
# file creation/modification, so we have to do some extra checking.
def property_fix
properties.each do |thing|
next unless [:mode, :owner, :group].include?(thing.name)
next unless [:mode, :owner, :group, :seluser, :selrole, :seltype, :selrange].include?(thing.name)
# Make sure we get a new stat objct
self.stat(true)

Просмотреть файл

@ -2,11 +2,10 @@ module Puppet
Puppet.type(:file).ensurable do
require 'etc'
desc "Whether to create files that don't currently exist.
Possible values are *absent*, *present* (will match any form of
file existence, and if the file is missing will create an empty
file), *file*, and *directory*. Specifying ``absent`` will delete
the file, although currently this will not recursively delete
directories.
Possible values are *absent*, *present*, *file*, and *directory*.
Specifying ``present`` will match any form of file existence, and
if the file is missing will create an empty file. Specifying
``absent`` will delete the file (and directory if recurse => true).
Anything other than those values will be considered to be a symlink.
For instance, the following text creates a link::

Просмотреть файл

@ -103,22 +103,8 @@ module Puppet
end
def sync
unless @resource.stat(false)
stat = @resource.stat(true)
unless stat
self.debug "File does not exist; cannot set mode"
return nil
end
end
mode = self.should
if mode == :absent
# This is really only valid for create states...
return nil
end
begin
File.chmod(mode, @resource[:path])
rescue => detail

Просмотреть файл

@ -1,5 +1,8 @@
module Puppet
Puppet.type(:file).newproperty(:owner) do
include Puppet::Util::POSIX
include Puppet::Util::Warnings
require 'etc'
desc "To whom the file should belong. Argument can be user name or
user ID."
@ -24,40 +27,32 @@ module Puppet
end
end
def name2id(value)
if value.is_a?(Symbol)
return value.to_s
def insync?(current)
unless Puppet::Util::SUIDManager.uid == 0
warning "Cannot manage ownership unless running as root"
return true
end
begin
user = Etc.getpwnam(value)
if user.uid == ""
return nil
@should.each do |value|
if value =~ /^\d+$/
uid = Integer(value)
elsif value.is_a?(String)
fail "Could not find user %s" % value unless uid = uid(value)
else
uid = value
end
return user.uid
rescue ArgumentError => detail
return nil
return true if uid == current
end
return false
end
# Determine if the user is valid, and if so, return the UID
def validuser?(value)
if value =~ /^\d+$/
value = value.to_i
end
if value.is_a?(Integer)
# verify the user is a valid user
if tmp = id2name(value)
return value
else
return false
end
if number = uid(value)
return number
else
if tmp = name2id(value)
return tmp
else
return false
end
return false
end
end
@ -99,13 +94,6 @@ module Puppet
return :absent
end
# Set our method appropriately, depending on links.
if stat.ftype == "link" and @resource[:links] != :follow
@method = :lchown
else
@method = :chown
end
currentvalue = stat.uid
# On OS X, files that are owned by -2 get returned as really
@ -120,44 +108,24 @@ module Puppet
end
def sync
unless Puppet::Util::SUIDManager.uid == 0
unless defined? @@notifieduid
self.notice "Cannot manage ownership unless running as root"
#@resource.delete(self.name)
@@notifieduid = true
end
return nil
# Set our method appropriately, depending on links.
if resource[:links] == :manage
method = :lchown
else
method = :chown
end
user = nil
unless user = self.validuser?(self.should)
tmp = self.should
unless defined? @@usermissing
@@usermissing = {}
end
if @@usermissing.include?(tmp)
@@usermissing[tmp] += 1
else
self.notice "user %s does not exist" % tmp
@@usermissing[tmp] = 1
end
return nil
uid = nil
@should.each do |user|
break if uid = validuser?(user)
end
unless @resource.stat(false)
unless @resource.stat(true)
self.debug "File does not exist; cannot set owner"
return nil
end
#self.debug "%s: after refresh, is '%s'" % [self.class.name,@is]
end
raise Puppet::Error, "Could not find user(s) %s" % @should.join(",") unless uid
begin
File.send(@method, user, nil, @resource[:path])
File.send(method, uid, nil, @resource[:path])
rescue => detail
raise Puppet::Error, "Failed to set owner to '%s': %s" %
[user, detail]
raise Puppet::Error, "Failed to set owner to '%s': %s" % [uid, detail]
end
return :file_changed

Просмотреть файл

@ -38,18 +38,13 @@ module Puppet
return nil
end
property_default = self.parse_selinux_context(property, context)
self.debug "Found #{property} default '#{property_default}' for #{@resource[:path]}"
if not property_default.nil?
self.debug "Found #{property} default '#{property_default}' for #{@resource[:path]}"
end
return property_default
end
def sync
unless @resource.stat(false)
stat = @resource.stat(true)
unless stat
return nil
end
end
self.set_selinux_context(@resource[:path], @should, name)
return :file_changed
end

Просмотреть файл

@ -1,28 +1,22 @@
# Manage Unix groups. This class is annoyingly complicated; There
# is some variety in whether systems use 'groupadd' or 'addgroup', but OS X
# significantly complicates the picture by using NetInfo. Eventually we
# will also need to deal with systems that have their groups hosted elsewhere
# (e.g., in LDAP). That will likely only be a problem for OS X, since it
# currently does not use the POSIX interfaces, since lookupd's cache screws
# things up.
require 'etc'
require 'facter'
module Puppet
newtype(:group) do
@doc = "Manage groups. This type can only create groups. Group
membership must be managed on individual users. This resource type
uses the prescribed native tools for creating groups and generally
uses POSIX APIs for retrieving information about them. It does
not directly modify ``/etc/group`` or anything.
@doc = "Manage groups. On most platforms this can only create groups.
Group membership must be managed on individual users.
For most platforms, the tools used are ``groupadd`` and its ilk;
for Mac OS X, NetInfo is used. This is currently unconfigurable,
but if you desperately need it to be so, please contact us."
On some platforms such as OS X, group membership is managed as an
attribute of the group, not the user record. Providers must have
the feature 'manages_members' to manage the 'members' property of
a group record."
feature :manages_members,
"For directories where membership is an attribute of groups not users."
newproperty(:ensure) do
desc "The basic state that the object should be in."
ensurable do
desc "Create or remove the group."
newvalue(:present) do
provider.create
@ -35,20 +29,6 @@ module Puppet
:group_removed
end
# If they're talking about the thing at all, they generally want to
# say it should exist.
defaultto do
if @resource.managed?
:present
else
nil
end
end
def retrieve
return provider.exists? ? :present : :absent
end
end
newproperty(:gid) do
@ -87,13 +67,28 @@ module Puppet
return gid
end
end
newproperty(:members, :array_matching => :all, :required_features => :manages_members) do
desc "The members of the group. For directory services where group
membership is stored in the group objects, not the users."
def change_to_s(currentvalue, newvalue)
currentvalue = currentvalue.join(",") if currentvalue != :absent
newvalue = newvalue.join(",")
super(currentvalue, newvalue)
end
end
newparam(:auth_membership) do
desc "whether the provider is authoritative for group membership."
defaultto true
end
newparam(:name) do
desc "The group name. While naming limitations vary by
system, it is advisable to keep the name to the degenerate
limitations, which is a maximum of 8 characters beginning with
a letter."
isnamevar
end

114
lib/puppet/type/mcx.rb Normal file
Просмотреть файл

@ -0,0 +1,114 @@
#--
# Copyright (C) 2008 Jeffrey J McCune.
# This program and entire repository is free software; you can
# redistribute it and/or modify it under the terms of the GNU
# General Public License as published by the Free Software
# Foundation; either version 2 of the License, or any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
# Author: Jeff McCune <mccune.jeff@gmail.com>
Puppet::Type.newtype(:mcx) do
@doc = "MCX object management using DirectoryService on OS X.
Original Author: Jeff McCune <mccune.jeff@gmail.com>
The default provider of this type merely manages the XML plist as
reported by the dscl -mcxexport command. This is similar to the
content property of the file type in Puppet.
The recommended method of using this type is to use Work Group Manager
to manage users and groups on the local computer, record the resulting
puppet manifest using the command 'ralsh mcx' then deploying this
to other machines.
"
feature :manages_content, \
"The provider can manage MCXSettings as a string.",
:methods => [:content, :content=]
ensurable do
desc "Create or remove the MCX setting."
newvalue(:present) do
provider.create
end
newvalue(:absent) do
provider.destroy
end
end
newparam(:name) do
desc "The name of the resource being managed.
The default naming convention follows Directory Service paths:
'/Computers/localhost'
'/Groups/admin'
'/Users/localadmin'
The ds_type and ds_name type parameters are not necessary if the
default naming convention is followed."
isnamevar
end
newparam(:ds_type) do
desc "The DirectoryService type this MCX setting attaches to."
newvalues(:user, :group, :computer, :computerlist)
end
newparam(:ds_name) do
desc "The name to attach the MCX Setting to.
e.g. 'localhost' when ds_type => computer. This setting is not
required, as it may be parsed so long as the resource name is
parseable. e.g. /Groups/admin where 'group' is the dstype."
end
newproperty(:content, :required_features => :manages_content) do
desc "The XML Plist. The value of MCXSettings in DirectoryService.
This is the standard output from the system command:
dscl localhost -mcxexport /Local/Default/<ds_type>/<ds_name>
Note that ds_type is capitalized and plural in the dscl command."
end
# JJM Yes, this is not DRY at all. Because of the code blocks
# autorequire must be done this way. I think.
def setup_autorequire(type)
# value returns a Symbol
name = value(:name)
ds_type = value(:ds_type)
ds_name = value(:ds_name)
if ds_type == type
rval = [ ds_name.to_s ]
else
rval = [ ]
end
rval
end
autorequire(:user) do
setup_autorequire(:user)
end
autorequire(:group) do
setup_autorequire(:group)
end
autorequire(:computer) do
setup_autorequire(:computer)
end
end

Просмотреть файл

@ -38,6 +38,22 @@ module Puppet
should be specified as an array."
defaultto do :absent end
def is_to_s(value)
if value == :absent or value.include?(:absent)
super
else
value.join(",")
end
end
def should_to_s(value)
if value == :absent or value.include?(:absent)
super
else
value.join(",")
end
end
end
autorequire(:user) do

Просмотреть файл

@ -1,6 +1,7 @@
require 'etc'
require 'facter'
require 'puppet/property/list'
require 'puppet/property/ordered_list'
require 'puppet/property/keyvalue'
module Puppet
@ -98,6 +99,16 @@ module Puppet
end
end
def insync?(is)
# We know the 'is' is a number, so we need to convert the 'should' to a number,
# too.
@should.each do |value|
return true if number = Puppet::Util.gid(value) and is == number
end
return false
end
def sync
found = false
@should.each do |value|
@ -109,6 +120,8 @@ module Puppet
end
fail "Could not find group(s) %s" % @should.join(",") unless found
# Use the default event.
end
end
@ -263,6 +276,17 @@ module Puppet
end
end
#autorequire the roles that the user has
autorequire(:user) do
reqs = []
if roles_property = @parameters[:roles] and roles = roles_property.should
reqs += roles.split(',')
end
reqs
end
newparam(:role_membership) do
desc "Whether specified roles should be treated as the only roles
of which the user is a member or whether they should merely
@ -301,7 +325,7 @@ module Puppet
defaultto :minimum
end
newproperty(:profiles, :parent => Puppet::Property::List, :required_features => :manages_solaris_rbac) do
newproperty(:profiles, :parent => Puppet::Property::OrderedList, :required_features => :manages_solaris_rbac) do
desc "The profiles the user has. Multiple profiles should be
specified as an array."

45
lib/puppet/type/zfs.rb Executable file
Просмотреть файл

@ -0,0 +1,45 @@
module Puppet
newtype(:zfs) do
@doc = "Manage zfs. Create destroy and set properties on zfs instances."
ensurable
newparam(:name) do
desc "The full name for this filesystem. (including the zpool)"
end
newproperty(:mountpoint) do
desc "The mountpoint property."
end
newproperty(:compression) do
desc "The compression property."
end
newproperty(:copies) do
desc "The copies property."
end
newproperty(:quota) do
desc "The quota property."
end
newproperty(:reservation) do
desc "The reservation property."
end
newproperty(:sharenfs) do
desc "The sharenfs property."
end
newproperty(:snapdir) do
desc "The sharenfs property."
end
autorequire(:zpool) do
#strip the zpool off the zfs name and autorequire it
[@parameters[:name].value.split('/')[0]]
end
end
end

Просмотреть файл

@ -358,6 +358,14 @@ end
end
end
newparam(:create_args) do
desc "Arguments to the zonecfg create command. This can be used to create branded zones."
end
newparam(:install_args) do
desc "Arguments to the zoneadm install command. This can be used to create branded zones."
end
newparam(:realhostname) do
desc "The actual hostname of the zone."
end

60
lib/puppet/type/zpool.rb Executable file
Просмотреть файл

@ -0,0 +1,60 @@
module Puppet
newtype(:zpool) do
@doc = "Manage zpools. Create and delete zpools. The provider WILL NOT SYNC, only report differences.
Supports vdevs with mirrors, raidz, logs and spares."
ensurable
newproperty(:disk, :array_matching => :all) do
desc "The disk(s) for this pool. Can be an array or space separated string"
end
newproperty(:mirror, :array_matching => :all) do
desc "List of all the devices to mirror for this pool. Each mirror should be a space separated string.
mirror => [\"disk1 disk2\", \"disk3 disk4\"]"
validate do |value|
if value.include?(",")
raise ArgumentError, "mirror names must be provided as string separated, not a comma-separated list"
end
end
end
newproperty(:raidz, :array_matching => :all) do
desc "List of all the devices to raid for this pool. Should be an array of space separated strings.
raidz => [\"disk1 disk2\", \"disk3 disk4\"]"
validate do |value|
if value.include?(",")
raise ArgumentError, "raid names must be provided as string separated, not a comma-separated list"
end
end
end
newproperty(:spare, :array_matching => :all) do
desc "Spare disk(s) for this pool."
end
newproperty(:log, :array_matching => :all) do
desc "Log disks for this pool. (doesn't support mirroring yet)"
end
newparam(:pool) do
desc "The name for this pool."
isnamevar
end
newparam(:raid_parity) do
desc "Determines parity when using raidz property."
end
validate do
has_should = [:disk, :mirror, :raidz].select { |prop| self.should(prop) }
if has_should.length > 1
self.fail "You cannot specify %s on this type (only one)" % has_should.join(" and ")
end
end
end
end

Просмотреть файл

@ -268,7 +268,7 @@ module Util
# Execute the desired command, and return the status and output.
# def execute(command, failonfail = true, uid = nil, gid = nil)
def execute(command, arguments = {:failonfail => true})
def execute(command, arguments = {:failonfail => true, :combine => true})
if command.is_a?(Array)
command = command.flatten.collect { |i| i.to_s }
str = command.join(" ")
@ -301,9 +301,13 @@ module Util
# The idea here is to avoid IO#read whenever possible.
output_file="/dev/null"
error_file="/dev/null"
if ! arguments[:squelch]
require "tempfile"
output_file = Tempfile.new("puppet")
if arguments[:combine]
error_file=output_file
end
end
oldverb = $VERBOSE
@ -319,7 +323,8 @@ module Util
begin
$stdin.reopen("/dev/null")
$stdout.reopen(output_file)
$stderr.reopen(output_file)
$stderr.reopen(error_file)
3.upto(256){|fd| IO::new(fd).close rescue nil}
if arguments[:gid]
Process.egid = arguments[:gid]

Просмотреть файл

@ -218,10 +218,10 @@ class Puppet::Util::FileType
begin
output = Puppet::Util.execute(%w{crontab -l}, :uid => @path)
return "" if output.include?("can't open your crontab")
raise Puppet::Error, "User %s not authorized to use cron" % @path if output.include?("you are not authorized to use cron")
return output
rescue
# If there's a failure, treat it like an empty file.
return ""
rescue => detail
raise Puppet::Error, "Could not read crontab for %s: %s" % [@path, detail]
end
end

Просмотреть файл

@ -4,11 +4,6 @@ require 'puppet'
# A class for handling metrics. This is currently ridiculously hackish.
class Puppet::Util::Metric
# Load the library as a feature, so we can test its presence.
# It's only used by this class, so there's no reason to move it
# to the main feature list.
Puppet.features.add :rrd, :libs => 'RRDtool'
attr_accessor :type, :name, :value, :label
attr_writer :values

85
lib/puppet/util/rdoc.rb Normal file
Просмотреть файл

@ -0,0 +1,85 @@
module Puppet::Util::RDoc
module_function
# launch a rdoc documenation process
# with the files/dir passed in +files+
def rdoc(outputdir, files)
begin
Puppet[:ignoreimport] = true
# then rdoc
require 'rdoc/rdoc'
# load our parser
require 'puppet/util/rdoc/parser'
r = RDoc::RDoc.new
RDoc::RDoc::GENERATORS["puppet"] = RDoc::RDoc::Generator.new("puppet/util/rdoc/generators/puppet_generator.rb",
"PuppetGenerator".intern,
"puppet")
# specify our own format & where to output
options = [ "--fmt", "puppet",
"--quiet",
"--op", outputdir ]
options += files
# launch the documentation process
r.document(options)
rescue RDoc::RDocError => e
raise Puppet::ParseError.new("RDoc error %s" % e)
end
end
# launch a output to console manifest doc
def manifestdoc(files)
Puppet[:ignoreimport] = true
files.select { |f| FileTest.file?(f) }.each do |f|
parser = Puppet::Parser::Parser.new(:environment => Puppet[:environment])
parser.file = f
ast = parser.parse
output(f, ast)
end
end
# Ouputs to the console the documentation
# of a manifest
def output(file, ast)
astobj = []
ast[:nodes].each do |name, k|
astobj << k if k.file == file
end
ast[:classes].each do |name, k|
astobj << k if k.file == file
end
ast[:definitions].each do |name, k|
astobj << k if k.file == file
end
astobj.sort! {|a,b| a.line <=> b.line }.each do |k|
output_astnode_doc(k)
end
end
def output_astnode_doc(ast)
puts ast.doc if !ast.doc.nil? and !ast.doc.empty?
if Puppet.settings[:document_all]
# scan each underlying resources to produce documentation
code = ast.code.children if ast.code.is_a?(Puppet::Parser::AST::ASTArray)
code ||= ast.code
output_resource_doc(code) unless code.nil?
end
end
def output_resource_doc(code)
code.sort { |a,b| a.line <=> b.line }.each do |stmt|
output_resource_doc(stmt.children) if stmt.is_a?(Puppet::Parser::AST::ASTArray)
if stmt.is_a?(Puppet::Parser::AST::Resource)
puts stmt.doc if !stmt.doc.nil? and !stmt.doc.empty?
end
end
end
end

Просмотреть файл

@ -0,0 +1,219 @@
require 'rdoc/code_objects'
module RDoc
# This modules contains various class that are used to hold information
# about the various Puppet language structures we found while parsing.
#
# Those will be mapped to their html counterparts which are defined in
# PuppetGenerator.
# PuppetTopLevel is a top level (usually a .pp/.rb file)
class PuppetTopLevel < TopLevel
attr_accessor :module_name, :global
# will contain all plugins
@@all_plugins = {}
# contains all cutoms facts
@@all_facts = {}
def initialize(toplevel)
super(toplevel.file_relative_name)
end
def self.all_plugins
@@all_plugins.values
end
def self.all_facts
@@all_facts.values
end
end
# PuppetModule holds a Puppet Module
# This is mapped to an HTMLPuppetModule
# it leverage the RDoc (ruby) module infrastructure
class PuppetModule < NormalModule
attr_accessor :facts, :plugins
def initialize(name,superclass=nil)
@facts = []
@plugins = []
super(name,superclass)
end
def initialize_classes_and_modules
super
@nodes = {}
end
def add_plugin(plugin)
add_to(@plugins, plugin)
end
def add_fact(fact)
add_to(@facts, fact)
end
def add_node(name,superclass)
cls = @nodes[name]
unless cls
cls = PuppetNode.new(name, superclass)
@nodes[name] = cls if !@done_documenting
cls.parent = self
cls.section = @current_section
end
cls
end
def each_fact
@facts.each {|c| yield c}
end
def each_plugin
@plugins.each {|c| yield c}
end
def each_node
@nodes.each {|c| yield c}
end
def nodes
@nodes.values
end
end
# PuppetClass holds a puppet class
# It is mapped to a HTMLPuppetClass for display
# It leverages RDoc (ruby) Class
class PuppetClass < ClassModule
attr_accessor :resource_list
def initialize(name, superclass)
super(name,superclass)
@resource_list = []
end
def add_resource(resource)
add_to(@resource_list, resource)
end
def is_module?
false
end
end
# PuppetNode holds a puppet node
# It is mapped to a HTMLPuppetNode for display
# A node is just a variation of a class
class PuppetNode < PuppetClass
def initialize(name, superclass)
super(name,superclass)
end
def is_module?
false
end
end
# Plugin holds a native puppet plugin (function,type...)
# It is mapped to a HTMLPuppetPlugin for display
class Plugin < Context
attr_accessor :name, :type
def initialize(name, type)
super()
@name = name
@type = type
@comment = ""
end
def <=>(other)
@name <=> other.name
end
def full_name
@name
end
def http_url(prefix)
path = full_name.split("::")
File.join(prefix, *path) + ".html"
end
def is_fact?
false
end
def to_s
res = self.class.name + ": " + @name + " (" + @type + ")\n"
res << @comment.to_s
res
end
end
# Fact holds a custom fact
# It is mapped to a HTMLPuppetPlugin for display
class Fact < Context
attr_accessor :name, :confine
def initialize(name, confine)
super()
@name = name
@confine = confine
@comment = ""
end
def <=>(other)
@name <=> other.name
end
def is_fact?
true
end
def full_name
@name
end
def to_s
res = self.class.name + ": " + @name + "\n"
res << @comment.to_s
res
end
end
# PuppetResource holds a puppet resource
# It is mapped to a HTMLPuppetResource for display
# A resource is defined by its "normal" form Type[title]
class PuppetResource < CodeObject
attr_accessor :type, :title, :params
def initialize(type, title, comment, params)
super()
@type = type
@title = title
@comment = comment
@params = params
end
def <=>(other)
full_name <=> other.full_name
end
def full_name
@type + "[" + @title + "]"
end
def name
full_name
end
def to_s
res = @type + "[" + @title + "]\n"
res << @comment.to_s
res
end
end
end

Просмотреть файл

@ -0,0 +1,829 @@
require 'rdoc/generators/html_generator'
require 'puppet/util/rdoc/code_objects'
module Generators
# This module holds all the classes needed to generate the HTML documentation
# of a bunch of puppet manifests.
#
# It works by traversing all the code objects defined by the Puppet RDoc::Parser
# and produces HTML counterparts objects that in turns are used by RDoc template engine
# to produce the final HTML.
#
# It is also responsible of creating the whole directory hierarchy, and various index
# files.
#
# It is to be noted that the whole system is built on top of ruby RDoc. As such there
# is an implicit mapping of puppet entities to ruby entitites:
#
# Puppet => Ruby
# ------------------------
# Module Module
# Class Class
# Definition Method
# Resource
# Node
# Plugin
# Fact
MODULE_DIR = "modules"
NODE_DIR = "nodes"
PLUGIN_DIR = "plugins"
# This is a specialized HTMLGenerator tailored to Puppet manifests
class PuppetGenerator < HTMLGenerator
def PuppetGenerator.for(options)
AllReferences::reset
HtmlMethod::reset
if options.all_one_file
PuppetGeneratorInOne.new(options)
else
PuppetGenerator.new(options)
end
end
def initialize(options) #:not-new:
@options = options
load_html_template
end
# loads our own html template file
def load_html_template
begin
require 'puppet/util/rdoc/generators/template/puppet/puppet'
extend RDoc::Page
rescue LoadError
$stderr.puts "Could not find Puppet template '#{template}'"
exit 99
end
end
def gen_method_index
# we don't generate an all define index
# as the presentation is per module/per class
end
# This is the central method, it generates the whole structures
# along with all the indices.
def generate_html
super
gen_into(@nodes)
gen_into(@plugins)
end
##
# Generate:
# the list of modules
# the list of classes and definitions of a specific module
# the list of all classes
# the list of nodes
# the list of resources
def build_indices
@allfiles = []
@nodes = []
@plugins = []
# contains all the seen modules
@modules = {}
@allclasses = {}
# build the modules, classes and per modules classes and define list
@toplevels.each do |toplevel|
next unless toplevel.document_self
file = HtmlFile.new(toplevel, @options, FILE_DIR)
classes = []
methods = []
modules = []
nodes = []
# find all classes of this toplevel
# store modules if we find one
toplevel.each_classmodule do |k|
generate_class_list(classes, modules, k, toplevel, CLASS_DIR)
end
# find all defines belonging to this toplevel
HtmlMethod.all_methods.each do |m|
# find parent module, check this method is not already
# defined.
if m.context.parent.toplevel === toplevel
methods << m
end
end
classes.each do |k|
@allclasses[k.index_name] = k if !@allclasses.has_key?(k.index_name)
end
# generate nodes and plugins found
classes.each do |k|
if k.context.is_module?
k.context.each_node do |name,node|
nodes << HTMLPuppetNode.new(node, toplevel, NODE_DIR, @options)
@nodes << nodes.last
end
k.context.each_plugin do |plugin|
@plugins << HTMLPuppetPlugin.new(plugin, toplevel, PLUGIN_DIR, @options)
end
k.context.each_fact do |fact|
@plugins << HTMLPuppetPlugin.new(fact, toplevel, PLUGIN_DIR, @options)
end
end
end
@files << file
@allfiles << { "file" => file, "modules" => modules, "classes" => classes, "methods" => methods, "nodes" => nodes }
end
@classes = @allclasses.values
end
# produce a class/module list of HTMLPuppetModule/HTMLPuppetClass
# based on the code object traversal.
def generate_class_list(classes, modules, from, html_file, class_dir)
if from.is_module? and !@modules.has_key?(from.name)
k = HTMLPuppetModule.new(from, html_file, class_dir, @options)
classes << k
@modules[from.name] = k
modules << @modules[from.name]
elsif from.is_module?
modules << @modules[from.name]
elsif !from.is_module?
k = HTMLPuppetClass.new(from, html_file, class_dir, @options)
classes << k
end
from.each_classmodule do |mod|
generate_class_list(classes, modules, mod, html_file, class_dir)
end
end
# generate all the subdirectories, modules, classes and files
def gen_sub_directories
begin
super
File.makedirs(MODULE_DIR)
File.makedirs(NODE_DIR)
File.makedirs(PLUGIN_DIR)
rescue
$stderr.puts $!.message
exit 1
end
end
# generate the index of modules
def gen_file_index
gen_top_index(@modules.values, 'All Modules', RDoc::Page::TOP_INDEX, "fr_modules_index.html")
end
# generate a top index
def gen_top_index(collection, title, template, filename)
template = TemplatePage.new(RDoc::Page::FR_INDEX_BODY, template)
res = []
collection.sort.each do |f|
if f.document_self
res << { "classlist" => CGI.escapeHTML("#{MODULE_DIR}/fr_#{f.index_name}.html"), "module" => CGI.escapeHTML("#{CLASS_DIR}/#{f.index_name}.html"),"name" => CGI.escapeHTML(f.index_name) }
end
end
values = {
"entries" => res,
'list_title' => CGI.escapeHTML(title),
'index_url' => main_url,
'charset' => @options.charset,
'style_url' => style_url('', @options.css),
}
File.open(filename, "w") do |f|
template.write_html_on(f, values)
end
end
# generate the all classes index file and the combo index
def gen_class_index
gen_an_index(@classes, 'All Classes', RDoc::Page::CLASS_INDEX, "fr_class_index.html")
@allfiles.each do |file|
unless file['file'].context.file_relative_name =~ /\.rb$/
gen_composite_index(file,
RDoc::Page::COMBO_INDEX,
"#{MODULE_DIR}/fr_#{file["file"].context.module_name}.html")
end
end
end
def gen_composite_index(collection, template, filename)\
return if FileTest.exists?(filename)
template = TemplatePage.new(RDoc::Page::FR_INDEX_BODY, template)
res1 = []
collection['classes'].sort.each do |f|
if f.document_self
unless f.context.is_module?
res1 << { "href" => "../"+CGI.escapeHTML(f.path), "name" => CGI.escapeHTML(f.index_name) }
end
end
end
res2 = []
collection['methods'].sort.each do |f|
if f.document_self
res2 << { "href" => "../"+f.path, "name" => f.index_name.sub(/\(.*\)$/,'') }
end
end
module_name = []
res3 = []
res4 = []
collection['modules'].sort.each do |f|
module_name << { "href" => "../"+CGI.escapeHTML(f.path), "name" => CGI.escapeHTML(f.index_name) }
unless f.facts.nil?
f.facts.each do |fact|
res3 << {"href" => "../"+CGI.escapeHTML(AllReferences["PLUGIN(#{fact.name})"].path), "name" => CGI.escapeHTML(fact.name)}
end
end
unless f.plugins.nil?
f.plugins.each do |plugin|
res4 << {"href" => "../"+CGI.escapeHTML(AllReferences["PLUGIN(#{plugin.name})"].path), "name" => CGI.escapeHTML(plugin.name)}
end
end
end
res5 = []
collection['nodes'].sort.each do |f|
if f.document_self
res5 << { "href" => "../"+CGI.escapeHTML(f.path), "name" => CGI.escapeHTML(f.name) }
end
end
values = {
"module" => module_name,
"classes" => res1,
'classes_title' => CGI.escapeHTML("Classes"),
'defines_title' => CGI.escapeHTML("Defines"),
'facts_title' => CGI.escapeHTML("Custom Facts"),
'plugins_title' => CGI.escapeHTML("Plugins"),
'nodes_title' => CGI.escapeHTML("Nodes"),
'index_url' => main_url,
'charset' => @options.charset,
'style_url' => style_url('', @options.css),
}
values["defines"] = res2 if res2.size>0
values["facts"] = res3 if res3.size>0
values["plugins"] = res4 if res4.size>0
values["nodes"] = res5 if res5.size>0
File.open(filename, "w") do |f|
template.write_html_on(f, values)
end
end
# returns the initial_page url
def main_url
main_page = @options.main_page
ref = nil
if main_page
ref = AllReferences[main_page]
if ref
ref = ref.path
else
$stderr.puts "Could not find main page #{main_page}"
end
end
unless ref
for file in @files
if file.document_self and file.context.global
ref = CGI.escapeHTML("#{CLASS_DIR}/#{file.context.module_name}.html")
break
end
end
end
unless ref
for file in @files
if file.document_self and !file.context.global
ref = CGI.escapeHTML("#{CLASS_DIR}/#{file.context.module_name}.html")
break
end
end
end
unless ref
$stderr.puts "Couldn't find anything to document"
$stderr.puts "Perhaps you've used :stopdoc: in all classes"
exit(1)
end
ref
end
end
# This module is used to hold/generate a list of puppet resources
# this is used in HTMLPuppetClass and HTMLPuppetNode
module ResourceContainer
def collect_resources
list = @context.resource_list
@resources = list.collect {|m| HTMLPuppetResource.new(m, self, @options) }
end
def build_resource_summary_list(path_prefix='')
collect_resources unless @resources
resources = @resources.sort
res = []
resources.each do |r|
res << {
"name" => CGI.escapeHTML(r.name),
"aref" => "#{path_prefix}\##{r.aref}"
}
end
res
end
def build_resource_detail_list(section)
outer = []
resources = @resources.sort
resources.each do |r|
row = {}
if r.section == section and r.document_self
row["name"] = CGI.escapeHTML(r.name)
desc = r.description.strip
row["m_desc"] = desc unless desc.empty?
row["aref"] = r.aref
row["params"] = r.params
outer << row
end
end
outer
end
end
class HTMLPuppetClass < HtmlClass
include ResourceContainer
def value_hash
super
rl = build_resource_summary_list
@values["resources"] = rl unless rl.empty?
@context.sections.each do |section|
secdata = @values["sections"].select { |secdata| secdata["secsequence"] == section.sequence }
if secdata.size == 1
secdata = secdata[0]
rdl = build_resource_detail_list(section)
secdata["resource_list"] = rdl unless rdl.empty?
end
end
@values
end
end
class HTMLPuppetNode < ContextUser
include ResourceContainer
attr_reader :path
def initialize(context, html_file, prefix, options)
super(context, options)
@html_file = html_file
@is_module = context.is_module?
@values = {}
context.viewer = self
if options.all_one_file
@path = context.full_name
else
@path = http_url(context.full_name, prefix)
end
AllReferences.add("NODE(#{@context.full_name})", self)
end
def name
@context.name
end
# return the relative file name to store this class in,
# which is also its url
def http_url(full_name, prefix)
path = full_name.dup
if path['<<']
path.gsub!(/<<\s*(\w*)/) { "from-#$1" }
end
File.join(prefix, path.split("::")) + ".html"
end
def parent_name
@context.parent.full_name
end
def index_name
name
end
def write_on(f)
value_hash
template = TemplatePage.new(RDoc::Page::BODYINC,
RDoc::Page::NODE_PAGE,
RDoc::Page::METHOD_LIST)
template.write_html_on(f, @values)
end
def value_hash
class_attribute_values
add_table_of_sections
@values["charset"] = @options.charset
@values["style_url"] = style_url(path, @options.css)
d = markup(@context.comment)
@values["description"] = d unless d.empty?
ml = build_method_summary_list
@values["methods"] = ml unless ml.empty?
rl = build_resource_summary_list
@values["resources"] = rl unless rl.empty?
il = build_include_list(@context)
@values["includes"] = il unless il.empty?
@values["sections"] = @context.sections.map do |section|
secdata = {
"sectitle" => section.title,
"secsequence" => section.sequence,
"seccomment" => markup(section.comment)
}
al = build_alias_summary_list(section)
secdata["aliases"] = al unless al.empty?
co = build_constants_summary_list(section)
secdata["constants"] = co unless co.empty?
al = build_attribute_list(section)
secdata["attributes"] = al unless al.empty?
cl = build_class_list(0, @context, section)
secdata["classlist"] = cl unless cl.empty?
mdl = build_method_detail_list(section)
secdata["method_list"] = mdl unless mdl.empty?
rdl = build_resource_detail_list(section)
secdata["resource_list"] = rdl unless rdl.empty?
secdata
end
@values
end
def build_attribute_list(section)
atts = @context.attributes.sort
res = []
atts.each do |att|
next unless att.section == section
if att.visibility == :public || att.visibility == :protected || @options.show_all
entry = {
"name" => CGI.escapeHTML(att.name),
"rw" => att.rw,
"a_desc" => markup(att.comment, true)
}
unless att.visibility == :public || att.visibility == :protected
entry["rw"] << "-"
end
res << entry
end
end
res
end
def class_attribute_values
h_name = CGI.escapeHTML(name)
@values["classmod"] = "Node"
@values["title"] = "#{@values['classmod']}: #{h_name}"
c = @context
c = c.parent while c and !c.diagram
if c && c.diagram
@values["diagram"] = diagram_reference(c.diagram)
end
@values["full_name"] = h_name
parent_class = @context.superclass
if parent_class
@values["parent"] = CGI.escapeHTML(parent_class)
if parent_name
lookup = parent_name + "::" + parent_class
else
lookup = parent_class
end
lookup = "NODE(#{lookup})"
parent_url = AllReferences[lookup] || AllReferences[parent_class]
if parent_url and parent_url.document_self
@values["par_url"] = aref_to(parent_url.path)
end
end
files = []
@context.in_files.each do |f|
res = {}
full_path = CGI.escapeHTML(f.file_absolute_name)
res["full_path"] = full_path
res["full_path_url"] = aref_to(f.viewer.path) if f.document_self
if @options.webcvs
res["cvsurl"] = cvs_url( @options.webcvs, full_path )
end
files << res
end
@values['infiles'] = files
end
def <=>(other)
self.name <=> other.name
end
end
class HTMLPuppetModule < HtmlClass
def initialize(context, html_file, prefix, options)
super(context, html_file, prefix, options)
end
def value_hash
@values = super
fl = build_facts_summary_list
@values["facts"] = fl unless fl.empty?
pl = build_plugins_summary_list
@values["plugins"] = pl unless pl.empty?
nl = build_nodes_list(0, @context)
@values["nodelist"] = nl unless nl.empty?
@values
end
def build_nodes_list(level, context)
res = ""
prefix = "&nbsp;&nbsp;::" * level;
context.nodes.sort.each do |node|
if node.document_self
res <<
prefix <<
"Node " <<
href(url(node.viewer.path), "link", node.full_name) <<
"<br />\n"
end
end
res
end
def build_facts_summary_list
potentially_referenced_list(context.facts) {|fn| ["PLUGIN(#{fn})"] }
end
def build_plugins_summary_list
potentially_referenced_list(context.plugins) {|fn| ["PLUGIN(#{fn})"] }
end
def facts
@context.facts
end
def plugins
@context.plugins
end
end
class HTMLPuppetPlugin < ContextUser
attr_reader :path
def initialize(context, html_file, prefix, options)
super(context, options)
@html_file = html_file
@is_module = false
@values = {}
context.viewer = self
if options.all_one_file
@path = context.full_name
else
@path = http_url(context.full_name, prefix)
end
AllReferences.add("PLUGIN(#{@context.full_name})", self)
end
def name
@context.name
end
# return the relative file name to store this class in,
# which is also its url
def http_url(full_name, prefix)
path = full_name.dup
if path['<<']
path.gsub!(/<<\s*(\w*)/) { "from-#$1" }
end
File.join(prefix, path.split("::")) + ".html"
end
def parent_name
@context.parent.full_name
end
def index_name
name
end
def write_on(f)
value_hash
template = TemplatePage.new(RDoc::Page::BODYINC,
RDoc::Page::PLUGIN_PAGE,
RDoc::Page::PLUGIN_LIST)
template.write_html_on(f, @values)
end
def value_hash
attribute_values
add_table_of_sections
@values["charset"] = @options.charset
@values["style_url"] = style_url(path, @options.css)
d = markup(@context.comment)
@values["description"] = d unless d.empty?
if context.is_fact?
unless context.confine.empty?
res = {}
res["type"] = context.confine[:type]
res["value"] = context.confine[:value]
@values["confine"] = [res]
end
else
@values["type"] = context.type
end
@values["sections"] = @context.sections.map do |section|
secdata = {
"sectitle" => section.title,
"secsequence" => section.sequence,
"seccomment" => markup(section.comment)
}
secdata
end
@values
end
def attribute_values
h_name = CGI.escapeHTML(name)
if @context.is_fact?
@values["classmod"] = "Fact"
else
@values["classmod"] = "Plugin"
end
@values["title"] = "#{@values['classmod']}: #{h_name}"
c = @context
@values["full_name"] = h_name
files = []
@context.in_files.each do |f|
res = {}
full_path = CGI.escapeHTML(f.file_absolute_name)
res["full_path"] = full_path
res["full_path_url"] = aref_to(f.viewer.path) if f.document_self
if @options.webcvs
res["cvsurl"] = cvs_url( @options.webcvs, full_path )
end
files << res
end
@values['infiles'] = files
end
def <=>(other)
self.name <=> other.name
end
end
class HTMLPuppetResource
include MarkUp
attr_reader :context
@@seq = "R000000"
def initialize(context, html_class, options)
@context = context
@html_class = html_class
@options = options
@@seq = @@seq.succ
@seq = @@seq
context.viewer = self
AllReferences.add(name, self)
end
def as_href(from_path)
if @options.all_one_file
"#" + path
else
HTMLGenerator.gen_url(from_path, path)
end
end
def name
@context.name
end
def section
@context.section
end
def index_name
"#{@context.name}"
end
def params
@context.params
end
def parent_name
if @context.parent.parent
@context.parent.parent.full_name
else
nil
end
end
def aref
@seq
end
def path
if @options.all_one_file
aref
else
@html_class.path + "#" + aref
end
end
def description
markup(@context.comment)
end
def <=>(other)
@context <=> other.context
end
def document_self
@context.document_self
end
def find_symbol(symbol, method=nil)
res = @context.parent.find_symbol(symbol, method)
if res
res = res.viewer
end
res
end
end
class PuppetGeneratorInOne < HTMLGeneratorInOne
def gen_method_index
gen_an_index(HtmlMethod.all_methods, 'Defines')
end
end
end

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,437 @@
# Puppet "parser" for the rdoc system
# The parser uses puppet parser and traverse the AST to instruct RDoc about
# our current structures. It also parses ruby files that could contain
# either custom facts or puppet plugins (functions, types...)
# rdoc mandatory includes
require "rdoc/code_objects"
require "puppet/util/rdoc/code_objects"
require "rdoc/tokenstream"
require "rdoc/markup/simple_markup/preprocess"
require "rdoc/parsers/parserfactory"
module RDoc
class Parser
extend ParserFactory
# parser registration into RDoc
parse_files_matching(/\.(rb|pp)$/)
# called with the top level file
def initialize(top_level, file_name, content, options, stats)
@options = options
@stats = stats
@input_file_name = file_name
@top_level = PuppetTopLevel.new(top_level)
@progress = $stderr unless options.quiet
end
# main entry point
def scan
Puppet.info "rdoc: scanning %s" % @input_file_name
if @input_file_name =~ /\.pp$/
@parser = Puppet::Parser::Parser.new(:environment => Puppet[:environment])
@parser.file = @input_file_name
@ast = @parser.parse
end
scan_top_level(@top_level)
@top_level
end
private
# walk down the namespace and lookup/create container as needed
def get_class_or_module(container, name)
# class ::A -> A is in the top level
if name =~ /^::/
container = @top_level
end
names = name.split('::')
final_name = names.pop
names.each do |name|
prev_container = container
container = container.find_module_named(name)
unless container
container = prev_container.add_module(PuppetClass, name)
end
end
return [container, final_name]
end
# split_module tries to find if +path+ belongs to the module path
# if it does, it returns the module name, otherwise if we are sure
# it is part of the global manifest path, "<site>" is returned.
# And finally if this path couldn't be mapped anywhere, nil is returned.
def split_module(path)
# find a module
fullpath = File.expand_path(path)
Puppet.debug "rdoc: testing %s" % fullpath
if fullpath =~ /(.*)\/([^\/]+)\/(?:manifests|plugins)\/.+\.(pp|rb)$/
modpath = $1
name = $2
Puppet.debug "rdoc: module %s into %s ?" % [name, modpath]
Puppet::Module.modulepath().each do |mp|
if File.identical?(modpath,mp)
Puppet.debug "rdoc: found module %s" % name
return name
end
end
end
if fullpath =~ /\.(pp|rb)$/
# there can be paths we don't want to scan under modules
# imagine a ruby or manifest that would be distributed as part as a module
# but we don't want those to be hosted under <site>
Puppet::Module.modulepath().each do |mp|
# check that fullpath is a descendant of mp
dirname = fullpath
while (dirname = File.dirname(dirname)) != '/'
return nil if File.identical?(dirname,mp)
end
end
end
# we are under a global manifests
Puppet.debug "rdoc: global manifests"
return "<site>"
end
# create documentation for the top level +container+
def scan_top_level(container)
# use the module README as documentation for the module
comment = ""
readme = File.join(File.dirname(File.dirname(@input_file_name)), "README")
comment = File.open(readme,"r") { |f| f.read } if FileTest.readable?(readme)
look_for_directives_in(container, comment) unless comment.empty?
# infer module name from directory
name = split_module(@input_file_name)
if name.nil?
# skip .pp files that are not in manifests directories as we can't guarantee they're part
# of a module or the global configuration.
container.document_self = false
return
end
Puppet.debug "rdoc: scanning for %s" % name
container.module_name = name
container.global=true if name == "<site>"
@stats.num_modules += 1
container, name = get_class_or_module(container,name)
mod = container.add_module(PuppetModule, name)
mod.record_location(@top_level)
mod.comment = comment
if @input_file_name =~ /\.pp$/
parse_elements(mod)
elsif @input_file_name =~ /\.rb$/
parse_plugins(mod)
end
end
# create documentation for include statements we can find in +code+
# and associate it with +container+
def scan_for_include(container, code)
code.each do |stmt|
scan_for_include(container,stmt.children) if stmt.is_a?(Puppet::Parser::AST::ASTArray)
if stmt.is_a?(Puppet::Parser::AST::Function) and stmt.name == "include"
stmt.arguments.each do |included|
Puppet.debug "found include: %s" % included.value
container.add_include(Include.new(included.value, stmt.doc))
end
end
end
end
# create documentation for global variables assignements we can find in +code+
# and associate it with +container+
def scan_for_vardef(container, code)
code.each do |stmt|
scan_for_vardef(container,stmt.children) if stmt.is_a?(Puppet::Parser::AST::ASTArray)
if stmt.is_a?(Puppet::Parser::AST::VarDef)
Puppet.debug "rdoc: found constant: %s = %s" % [stmt.name.to_s, value_to_s(stmt.value)]
container.add_constant(Constant.new(stmt.name.to_s, value_to_s(stmt.value), stmt.doc))
end
end
end
# create documentation for resources we can find in +code+
# and associate it with +container+
def scan_for_resource(container, code)
code.each do |stmt|
scan_for_resource(container,stmt.children) if stmt.is_a?(Puppet::Parser::AST::ASTArray)
if stmt.is_a?(Puppet::Parser::AST::Resource)
type = stmt.type.split("::").collect { |s| s.capitalize }.join("::")
title = value_to_s(stmt.title)
Puppet.debug "rdoc: found resource: %s[%s]" % [type,title]
param = []
stmt.params.children.each do |p|
res = {}
res["name"] = p.param
if !p.value.nil?
if !p.value.is_a?(Puppet::Parser::AST::ASTArray)
res["value"] = "'#{p.value}'"
else
res["value"] = "[%s]" % p.value.children.collect { |v| "'#{v}'" }.join(", ")
end
end
param << res
end
container.add_resource(PuppetResource.new(type, title, stmt.doc, param))
end
end
end
# create documentation for a class named +name+
def document_class(name, klass, container)
Puppet.debug "rdoc: found new class %s" % name
container, name = get_class_or_module(container, name)
superclass = klass.parentclass
superclass = "" if superclass.nil? or superclass.empty?
@stats.num_classes += 1
comment = klass.doc
look_for_directives_in(container, comment) unless comment.empty?
cls = container.add_class(PuppetClass, name, superclass)
cls.record_location(@top_level)
# scan class code for include
code = klass.code.children if klass.code.is_a?(Puppet::Parser::AST::ASTArray)
code ||= klass.code
unless code.nil?
scan_for_include(cls, code)
scan_for_resource(cls, code) if Puppet.settings[:document_all]
end
cls.comment = comment
end
# create documentation for a node
def document_node(name, node, container)
Puppet.debug "rdoc: found new node %s" % name
superclass = node.parentclass
superclass = "" if superclass.nil? or superclass.empty?
comment = node.doc
look_for_directives_in(container, comment) unless comment.empty?
n = container.add_node(name, superclass)
n.record_location(@top_level)
code = node.code.children if node.code.is_a?(Puppet::Parser::AST::ASTArray)
code ||= node.code
unless code.nil?
scan_for_include(n, code)
scan_for_vardef(n, code)
scan_for_resource(n, code) if Puppet.settings[:document_all]
end
n.comment = comment
end
# create documentation for a define
def document_define(name, define, container)
Puppet.debug "rdoc: found new definition %s" % name
# find superclas if any
@stats.num_methods += 1
# find the parentclass
# split define name by :: to find the complete module hierarchy
container, name = get_class_or_module(container,name)
return if container.find_local_symbol(name)
# build up declaration
declaration = ""
define.arguments.each do |arg,value|
declaration << "\$#{arg}"
if !value.nil?
declaration << " => "
if !value.is_a?(Puppet::Parser::AST::ASTArray)
declaration << "'#{value.value}'"
else
declaration << "[%s]" % value.children.collect { |v| "'#{v}'" }.join(", ")
end
end
declaration << ", "
end
declaration.chop!.chop! if declaration.size > 1
# register method into the container
meth = AnyMethod.new(declaration, name)
container.add_method(meth)
meth.comment = define.doc
look_for_directives_in(container, meth.comment) unless meth.comment.empty?
meth.params = "( " + declaration + " )"
meth.visibility = :public
meth.document_self = true
meth.singleton = false
end
# Traverse the AST tree and produce code-objects node
# that contains the documentation
def parse_elements(container)
Puppet.debug "rdoc: scanning manifest"
@ast[:classes].each do |name, klass|
if klass.file == @input_file_name
unless name.empty?
document_class(name,klass,container)
else # on main class document vardefs
code = klass.code.children unless klass.code.is_a?(Puppet::Parser::AST::ASTArray)
code ||= klass.code
scan_for_vardef(container, code) unless code.nil?
end
end
end
@ast[:definitions].each do |name, define|
if define.file == @input_file_name
document_define(name,define,container)
end
end
@ast[:nodes].each do |name, node|
if node.file == @input_file_name
document_node(name,node,container)
end
end
end
# create documentation for plugins
def parse_plugins(container)
Puppet.debug "rdoc: scanning plugin or fact"
if @input_file_name =~ /\/facter\/[^\/]+\.rb$/
parse_fact(container)
else
parse_puppet_plugin(container)
end
end
# this is a poor man custom fact parser :-)
def parse_fact(container)
comments = ""
current_fact = nil
File.open(@input_file_name) do |of|
of.each do |line|
# fetch comments
if line =~ /^[ \t]*# ?(.*)$/
comments += $1 + "\n"
elsif line =~ /^[ \t]*Facter.add\(['"](.*?)['"]\)/
current_fact = Fact.new($1,{})
container.add_fact(current_fact)
look_for_directives_in(container, comments) unless comments.empty?
current_fact.comment = comments
current_fact.record_location(@top_level)
comments = ""
Puppet.debug "rdoc: found custom fact %s" % current_fact.name
elsif line =~ /^[ \t]*confine[ \t]*:(.*?)[ \t]*=>[ \t]*(.*)$/
current_fact.confine = { :type => $1, :value => $2 } unless current_fact.nil?
else # unknown line type
comments =""
end
end
end
end
# this is a poor man puppet plugin parser :-)
# it doesn't extract doc nor desc :-(
def parse_puppet_plugin(container)
comments = ""
current_plugin = nil
File.open(@input_file_name) do |of|
of.each do |line|
# fetch comments
if line =~ /^[ \t]*# ?(.*)$/
comments += $1 + "\n"
elsif line =~ /^[ \t]*newfunction[ \t]*\([ \t]*:(.*?)[ \t]*,[ \t]*:type[ \t]*=>[ \t]*(:rvalue|:lvalue)\)/
current_plugin = Plugin.new($1, "function")
container.add_plugin(current_plugin)
look_for_directives_in(container, comments) unless comments.empty?
current_plugin.comment = comments
current_plugin.record_location(@top_level)
comments = ""
Puppet.debug "rdoc: found new function plugins %s" % current_plugin.name
elsif line =~ /^[ \t]*Puppet::Type.newtype[ \t]*\([ \t]*:(.*?)\)/
current_plugin = Plugin.new($1, "type")
container.add_plugin(current_plugin)
look_for_directives_in(container, comments) unless comments.empty?
current_plugin.comment = comments
current_plugin.record_location(@top_level)
comments = ""
Puppet.debug "rdoc: found new type plugins %s" % current_plugin.name
elsif line =~ /module Puppet::Parser::Functions/
# skip
else # unknown line type
comments =""
end
end
end
end
# look_for_directives_in scans the current +comment+ for RDoc directives
def look_for_directives_in(context, comment)
preprocess = SM::PreProcess.new(@input_file_name, @options.rdoc_include)
preprocess.handle(comment) do |directive, param|
case directive
when "stopdoc"
context.stop_doc
""
when "startdoc"
context.start_doc
context.force_documentation = true
""
when "enddoc"
#context.done_documenting = true
#""
throw :enddoc
when "main"
options = Options.instance
options.main_page = param
""
when "title"
options = Options.instance
options.title = param
""
when "section"
context.set_current_section(param, comment)
comment.replace("") # 1.8 doesn't support #clear
break
else
warn "Unrecognized directive '#{directive}'"
break
end
end
remove_private_comments(comment)
end
def remove_private_comments(comment)
comment.gsub!(/^#--.*?^#\+\+/m, '')
comment.sub!(/^#--.*/m, '')
end
# convert an AST value to a string
def value_to_s(value)
value = value.children if value.is_a?(Puppet::Parser::AST::ASTArray)
if value.is_a?(Array)
"['#{value.join(", ")}']"
elsif [:true, true, "true"].include?(value)
"true"
elsif [:false, false, "false"].include?(value)
"false"
else
value.to_s
end
end
end
end

Просмотреть файл

@ -1,74 +1,67 @@
# Provides utility functions to help interfaces Puppet to SELinux.
#
# Currently this is implemented via the command line tools. At some
# point support should be added to use the new SELinux ruby bindings
# as that will be faster and more reliable then shelling out when they
# are available. At this time (2008-09-26) these bindings aren't bundled on
# any SELinux-using distribution I know of.
# This requires the very new SELinux Ruby bindings. These bindings closely
# mirror the SELinux C library interface.
#
# Support for the command line tools is not provided because the performance
# was abysmal. At this time (2008-11-02) the only distribution providing
# these Ruby SELinux bindings which I am aware of is Fedora (in libselinux-ruby).
require 'puppet/util'
begin
require 'selinux'
rescue LoadError
# Nothing
end
module Puppet::Util::SELinux
include Puppet::Util
def selinux_support?
FileTest.exists?("/selinux/enforce")
unless defined? Selinux
return false
end
if Selinux.is_selinux_enabled == 1
return true
end
return false
end
# Retrieve and return the full context of the file. If we don't have
# SELinux support or if the stat call fails then return nil.
# SELinux support or if the SELinux call fails then return nil.
def get_selinux_current_context(file)
unless selinux_support?
return nil
end
context = ""
begin
execpipe("/usr/bin/stat -c %C #{file}") do |out|
out.each do |line|
context << line
end
end
rescue Puppet::ExecutionFailure
retval = Selinux.lgetfilecon(file)
if retval == -1
return nil
end
context.chomp!
# Handle the case that the system seems to have SELinux support but
# stat finds unlabled files.
if context == "(null)"
return nil
end
return context
return retval[1]
end
# Use the matchpathcon command, if present, to return the SELinux context
# which the SELinux policy on the system expects the file to have. We can
# use this to obtain a good default context. If the command does not
# exist or the call fails return nil.
#
# Note: For this command to work a full, non-relative, filesystem path
# should be given.
# Retrieve and return the default context of the file. If we don't have
# SELinux support or if the SELinux call fails to file a default then return nil.
def get_selinux_default_context(file)
unless selinux_support?
return nil
end
unless FileTest.executable?("/usr/sbin/matchpathcon")
# If the filesystem has no support for SELinux labels, return a default of nil
# instead of what matchpathcon would return
unless selinux_label_support?(file)
return nil
end
context = ""
# If the file exists we should pass the mode to matchpathcon for the most specific
# matching. If not, we can pass a mode of 0.
begin
execpipe("/usr/sbin/matchpathcon #{file}") do |out|
out.each do |line|
context << line
end
end
rescue Puppet::ExecutionFailure
filestat = File.lstat(file)
mode = filestat.mode
rescue Errno::ENOENT
mode = 0
end
retval = Selinux.matchpathcon(file, mode)
if retval == -1
return nil
end
# For a successful match, matchpathcon returns two fields separated by
# a variable amount of whitespace. The second field is the full context.
context = context.split(/\s/)[1]
return context
return retval[1]
end
# Take the full SELinux context returned from the tools and parse it
@ -91,32 +84,52 @@ module Puppet::Util::SELinux
end
# This updates the actual SELinux label on the file. You can update
# only a single component or update the entire context. It is just a
# wrapper around the chcon command.
# only a single component or update the entire context.
# The caveat is that since setting a partial context makes no sense the
# file has to already exist. Puppet (via the File resource) will always
# just try to set components, even if all values are specified by the manifest.
# I believe that the OS should always provide at least a fall-through context
# though on any well-running system.
def set_selinux_context(file, value, component = false)
unless selinux_support?
return nil
end
case component
when :seluser
flag = "-u"
when :selrole
flag = "-r"
when :seltype
flag = "-t"
when :selrange
flag = "-l"
else
flag = nil
end
if flag.nil?
cmd = ["/usr/bin/chcon","-h",value,file]
if component
# Must first get existing context to replace a single component
context = Selinux.lgetfilecon(file)[1]
if context == -1
# We can't set partial context components when no context exists
# unless/until we can find a way to make Puppet call this method
# once for all selinux file label attributes.
Puppet.warning "Can't set SELinux context on file unless the file already has some kind of context"
return nil
end
context = context.split(':')
case component
when :seluser
context[0] = value
when :selrole
context[1] = value
when :seltype
context[2] = value
when :selrange
context[3] = value
else
raise ArguementError, "set_selinux_context component must be one of :seluser, :selrole, :seltype, or :selrange"
end
context = context.join(':')
else
cmd = ["/usr/bin/chcon","-h",flag,value,file]
context = value
end
retval = Selinux.lsetfilecon(file, context)
if retval == 0
return true
else
Puppet.warning "Failed to set SELinux context %s on %s" % [context, file]
return false
end
execute(cmd)
return true
end
# Since this call relies on get_selinux_default_context it also needs a
@ -136,4 +149,63 @@ module Puppet::Util::SELinux
end
return nil
end
# Internal helper function to read and parse /proc/mounts
def read_mounts
begin
mounts = File.read("/proc/mounts")
rescue
return nil
end
mntpoint = {}
# Read all entries in /proc/mounts. The second column is the
# mountpoint and the third column is the filesystem type.
# We skip rootfs because it is always mounted at /
mounts.collect do |line|
params = line.split(' ')
next if params[2] == 'rootfs'
mntpoint[params[1]] = params[2]
end
return mntpoint
end
# Internal helper function to return which type of filesystem a
# given file path resides on
def find_fs(file)
unless mnts = read_mounts()
return nil
end
# For a given file:
# Check if the filename is in the data structure;
# return the fstype if it is.
# Just in case: return something if you're down to "/" or ""
# Remove the last slash and everything after it,
# and repeat with that as the file for the next loop through.
ary = file.split('/')
while not ary.empty? do
path = ary.join('/')
if mnts.has_key?(path)
return mnts[path]
end
ary.pop
end
return mnts['/']
end
# Check filesystem a path resides on for SELinux support against
# whitelist of known-good filesystems.
# Returns true if the filesystem can support SELinux labels and
# false if not.
def selinux_label_support?(file)
fstype = find_fs(file)
if fstype.nil?
return false
end
filesystems = ['ext2', 'ext3', 'ext4', 'gfs', 'gfs2', 'xfs', 'jfs']
return filesystems.include?(fstype)
end
end

Просмотреть файл

@ -9,8 +9,6 @@ class Puppet::Util::Settings
include Enumerable
include Puppet::Util
@@sync = Sync.new
attr_accessor :file
attr_reader :timer
@ -21,22 +19,22 @@ class Puppet::Util::Settings
# Set a config value. This doesn't set the defaults, it sets the value itself.
def []=(param, value)
@@sync.synchronize do # yay, thread-safe
param = symbolize(param)
unless element = @config[param]
raise ArgumentError,
"Attempt to assign a value to unknown configuration parameter %s" % param.inspect
end
if element.respond_to?(:munge)
value = element.munge(value)
end
if element.respond_to?(:handle)
element.handle(value)
end
# Reset the name, so it's looked up again.
if param == :name
@name = nil
end
param = symbolize(param)
unless element = @config[param]
raise ArgumentError,
"Attempt to assign a value to unknown configuration parameter %s" % param.inspect
end
if element.respond_to?(:munge)
value = element.munge(value)
end
if element.respond_to?(:handle)
element.handle(value)
end
# Reset the name, so it's looked up again.
if param == :name
@name = nil
end
@sync.synchronize do # yay, thread-safe
@values[:memory][param] = value
@cache.clear
@ -60,7 +58,6 @@ class Puppet::Util::Settings
return options
end
# Turn the config into a Puppet configuration and apply it
def apply
trans = self.to_transportable
begin
@ -87,25 +84,21 @@ class Puppet::Util::Settings
# Remove all set values, potentially skipping cli values.
def clear(exceptcli = false)
@config.each { |name, obj|
unless exceptcli and obj.setbycli
obj.clear
@sync.synchronize do
@values.each do |name, values|
@values.delete(name) unless exceptcli and name == :cli
end
}
@values.each do |name, values|
next if name == :cli and exceptcli
@values.delete(name)
# Don't clear the 'used' in this case, since it's a config file reparse,
# and we want to retain this info.
unless exceptcli
@used = []
end
@cache.clear
@name = nil
end
# Don't clear the 'used' in this case, since it's a config file reparse,
# and we want to retain this info.
unless exceptcli
@used = []
end
@cache.clear
@name = nil
end
# This is mostly just used for testing.
@ -178,10 +171,12 @@ class Puppet::Util::Settings
end
str = str.intern
if self.valid?(str)
if self.boolean?(str)
@values[:cli][str] = bool
else
@values[:cli][str] = value
@sync.synchronize do
if self.boolean?(str)
@values[:cli][str] = bool
else
@values[:cli][str] = value
end
end
else
raise ArgumentError, "Invalid argument %s" % opt
@ -199,14 +194,17 @@ class Puppet::Util::Settings
@shortnames.include?(short)
end
# Create a new config object
# Create a new collection of config settings.
def initialize
@config = {}
@shortnames = {}
@created = []
@searchpath = nil
# Mutex-like thing to protect @values
@sync = Sync.new
# Keep track of set values.
@values = Hash.new { |hash, key| hash[key] = {} }
@ -310,7 +308,10 @@ class Puppet::Util::Settings
end
searchpath.each do |source|
next if source == :name
break if @name = @values[source][:name]
@sync.synchronize do
@name = @values[source][:name]
end
break if @name
end
unless @name
@name = convert(@config[:name].default).intern
@ -333,14 +334,24 @@ class Puppet::Util::Settings
end
end
# Parse the configuration file.
# Parse the configuration file. Just provides
# thread safety.
def parse(file)
# We have to clear outside of the sync, because it's
# also using synchronize().
clear(true)
@sync.synchronize do
unsafe_parse(file)
end
end
# Unsafely parse the file -- this isn't thread-safe and causes plenty of problems if used directly.
def unsafe_parse(file)
parse_file(file).each do |area, values|
@values[area] = values
end
# Determine our environment, if we have one.
if @config[:environment]
env = self.value(:environment).to_sym
@ -377,12 +388,11 @@ class Puppet::Util::Settings
# what kind of element we're creating, but the value itself might be either
# a default or a value, so we can't actually assign it.
def newelement(hash)
value = hash[:value] || hash[:default]
klass = nil
if hash[:section]
hash[:section] = symbolize(hash[:section])
end
case value
case hash[:default]
when true, false, "true", "false":
klass = CBoolean
when /^\$\w+\//, /^\//:
@ -392,7 +402,7 @@ class Puppet::Util::Settings
else
raise Puppet::Error, "Invalid value '%s' for %s" % [value.inspect, hash[:name]]
end
hash[:parent] = self
hash[:settings] = self
element = klass.new(hash)
return element
@ -415,7 +425,7 @@ class Puppet::Util::Settings
def reparse
if defined? @file and @file.changed?
Puppet.notice "Reparsing %s" % @file.file
@@sync.synchronize do
@sync.synchronize do
parse(@file)
end
reuse()
@ -424,7 +434,7 @@ class Puppet::Util::Settings
def reuse
return unless defined? @used
@@sync.synchronize do # yay, thread-safe
@sync.synchronize do # yay, thread-safe
@used.each do |section|
@used.delete(section)
self.use(section)
@ -508,7 +518,6 @@ class Puppet::Util::Settings
name = symbolize(name)
hash[:name] = name
hash[:section] = section
name = hash[:name]
if @config.include?(name)
raise ArgumentError, "Parameter %s is already defined" % name
end
@ -621,7 +630,7 @@ Generated on #{Time.now}.
# Create the necessary objects to use a section. This is idempotent;
# you can 'use' a section as many times as you want.
def use(*sections)
@@sync.synchronize do # yay, thread-safe
@sync.synchronize do # yay, thread-safe
sections = sections.reject { |s| @used.include?(s.to_sym) }
return if sections.empty?
@ -680,16 +689,19 @@ Generated on #{Time.now}.
end
# See if we can find it within our searchable list of values
val = nil
each_source(environment) do |source|
# Look for the value. We have to test the hash for whether
# it exists, because the value might be false.
if @values[source].include?(param)
val = @values[source][param]
break
val = catch :foundval do
each_source(environment) do |source|
# Look for the value. We have to test the hash for whether
# it exists, because the value might be false.
@sync.synchronize do
if @values[source].include?(param)
throw :foundval, @values[source][param]
end
end
end
throw :foundval, nil
end
# If we didn't get a value, use the default
val = @config[param].default if val.nil?
@ -975,14 +987,9 @@ Generated on #{Time.now}.
# The base element type.
class CElement
attr_accessor :name, :section, :default, :parent, :setbycli, :call_on_define
attr_accessor :name, :section, :default, :setbycli, :call_on_define
attr_reader :desc, :short
# Unset any set value.
def clear
@value = nil
end
def desc=(value)
@desc = value.gsub(/^\s*/, '')
end
@ -1002,10 +1009,9 @@ Generated on #{Time.now}.
# Create the new element. Pretty much just sets the name.
def initialize(args = {})
if args.include?(:parent)
self.parent = args[:parent]
args.delete(:parent)
end
@settings = args.delete(:settings)
raise ArgumentError.new("You must refer to a settings object") if @settings.nil? or !@settings.is_a?(Puppet::Util::Settings)
args.each do |param, value|
method = param.to_s + "="
unless self.respond_to? method
@ -1060,7 +1066,7 @@ Generated on #{Time.now}.
# If the value has not been overridden, then print it out commented
# and unconverted, so it's clear that that's the default and how it
# works.
value = @parent.value(self.name)
value = @settings.value(self.name)
if value != @default
line = "%s = %s" % [@name, value]
@ -1075,7 +1081,7 @@ Generated on #{Time.now}.
# Retrieves the value, or if it's not set, retrieves the default.
def value
@parent.value(self.name)
@settings.value(self.name)
end
end
@ -1086,7 +1092,7 @@ Generated on #{Time.now}.
def group
if defined? @group
return @parent.convert(@group)
return @settings.convert(@group)
else
return nil
end
@ -1094,7 +1100,7 @@ Generated on #{Time.now}.
def owner
if defined? @owner
return @parent.convert(@owner)
return @settings.convert(@owner)
else
return nil
end
@ -1117,7 +1123,7 @@ Generated on #{Time.now}.
# Return the appropriate type.
def type
value = @parent.value(self.name)
value = @settings.value(self.name)
if @name.to_s =~ /dir/
return :directory
elsif value.to_s =~ /\/$/
@ -1187,7 +1193,7 @@ Generated on #{Time.now}.
return true unless value.is_a? String
value.scan(/\$(\w+)/) { |name|
name = $1
unless @parent.include?(name)
unless @settings.include?(name)
raise ArgumentError,
"Settings parameter '%s' is undefined" %
name

Просмотреть файл

@ -7,6 +7,49 @@ puppetlibdir = File.join(basedir, "../lib")
puppettestlibdir = File.join(basedir, "../test/lib")
speclibdir = File.join(basedir, "lib")
require 'find'
include Find
include FileTest
$exclusions = %W(lib)
filemap = Hash.new { |hash, key| hash[key] = [] }
allfiles = []
# First collect the entire file list.
find(".") do |f|
# Get rid of the leading ./
f = f.sub(/^\.\//, '')
file = File.basename(f)
dir = File.dirname(f)
# Prune . directories and excluded dirs
if (file =~ /^\./ and f != ".") or $exclusions.include?(File.basename(file))
prune
next
end
next if f == "."
next if dir == "."
# If we're a ruby script, then add it to the list of files for that dir
if file =~ /\.rb$/
allfiles << f
# Add it to all of the parent dirs, not just our own
parts = File.split(dir)
if parts[0] == "."
parts.shift
end
parts.each_with_index { |part, i|
path = File.join(parts[0..i])
filemap[path] << f
}
end
end
libs = [puppetlibdir, puppettestlibdir, speclibdir]
desc "Run all specs"
Spec::Rake::SpecTask.new('all') do |t|
@ -16,3 +59,33 @@ Spec::Rake::SpecTask.new('all') do |t|
end
task :default => [:all]
# Now create a task for every directory
filemap.each do |dir, files|
ns = dir.gsub "/", ":"
# First create a separate task for each file in the namespace.
namespace ns do
files.each do |file|
Spec::Rake::SpecTask.new(file) do |t|
t.spec_files = File.basename(file, '.rb')
t.libs = libs
t.spec_opts = ['--options', 'spec.opts']
end
end
end
# Then create a task that matches the directory itself.
Spec::Rake::SpecTask.new(dir) do |t|
if ENV["TESTFILES"]
t.spec_files = ENV["TESTFILES"].split(/\s+/)
else
t.spec_files = files.sort
end
t.libs = libs
t.spec_opts = ['--options', 'spec.opts']
end
# And alias it with a slash on the end
task(dir + "/" => dir)
end

Просмотреть файл

@ -114,6 +114,12 @@ describe Puppet::Module, " when searching for templates" do
Puppet::Module.find_template("mytemplate").should == "/my/templates/mytemplate"
end
it "should accept relative templatedirs" do
Puppet[:templatedir] = "my/templates"
File.expects(:directory?).with(File.join(Dir.getwd,"my/templates")).returns(true)
Puppet::Module.find_template("mytemplate").should == File.join(Dir.getwd,"my/templates/mytemplate")
end
it "should use the environment templatedir if no module is found and an environment is specified" do
Puppet::Module.stubs(:templatepath).with("myenv").returns(["/myenv/templates"])
Puppet::Module.expects(:find).with("mymod", "myenv").returns(nil)

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше