forked from nanliu/puppet-solaris
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Solaris 11 has migrated a log of configuration parameters from configuration files into smf manifests. So instead of manipulating a `/etc/resolv.conf` file you manipulate properties with the svccfg utility. The new type `service_config` can now be used to set such properties, e.g.: service_config { 'network/dns/client:config/nameserver': ensure => [ '10.0.0.2', '10.0.0.3' ], type => net_address, } which is the same as service_config { 'config/nameserver': ensure => [ '10.0.0.2', '10.0.0.3' ], fmri => 'svc:/network/dns/client', type => net_address, }
- Loading branch information
Showing
5 changed files
with
544 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
require 'strscan' | ||
Puppet::Type.type(:service_config).provide(:svccfg) do | ||
desc "Manages smf configuration with svccfg" | ||
|
||
commands :svccfg => '/usr/sbin/svccfg' | ||
|
||
defaultfor :operatingsystem => :solaris | ||
|
||
def ensure | ||
result = [:absent] | ||
svccfg('-s', resource[:fmri], :listprop, resource[:prop]).each_line do |line| | ||
next if /^\s*$/.match(line) # ignore empty lines | ||
next if /^\s*#/.match(line) # ignore comments | ||
name, type, value = line.chomp.split(/\s+/,3) | ||
scanner = StringScanner.new(value) | ||
result = [] | ||
while !scanner.eos? | ||
scanner.skip(/\s+/) | ||
# TODO: This will not work if the value itself contains escaped | ||
# characters such as \" | ||
if token = scanner.scan(/".*?"|\S+/) | ||
token.gsub!(/"(.*)"/, '\1') | ||
result << token | ||
else | ||
raise Puppet::Error, "Unable to parse value #{value}" | ||
end | ||
end | ||
break | ||
end | ||
result | ||
end | ||
|
||
def ensure=(new_value) | ||
new_value = [new_value] unless new_value.is_a? Array | ||
if new_value == [:absent] | ||
svccfg('-s', resource[:fmri], :delprop, resource[:prop]) | ||
else | ||
quoted_values = case resource[:type] | ||
when :astring | ||
new_value.map {|s| "\"#{s}\"" } | ||
else | ||
new_value | ||
end | ||
argument = if quoted_values.size == 1 | ||
quoted_values.first | ||
else | ||
"(#{quoted_values.join(' ')})" | ||
end | ||
|
||
svccfg('-s', resource[:fmri], :setprop, resource[:prop], '=', "#{resource[:type]}:", argument) | ||
end | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
module Puppet | ||
newtype(:service_config) do | ||
|
||
@doc = "Manages smf configuration on Solaris 11. | ||
Solaris 11 and OpenSolaris have moved a lot of configuration aspects | ||
from plaintext files to SMF configuration. The `service_config` type | ||
can be used to set these options with puppet. One serive config is | ||
identified by a service identifier (FMRI) and a property name. | ||
The title of a `service_config` resource can either be the combination | ||
of a fmri and a property name in the format `<fmri>:<property>` or can | ||
be just the property name. In the latter case you have to provider | ||
the fmri as a resource parameter. | ||
E.g. to set the nameserver of your DNS client to `10.0.0.1` and | ||
`10.0.0.2` you can either write | ||
service_config { 'network/dns/client:config/nameserver': | ||
ensure => [ '10.0.0.1', '10.0.0.2' ], | ||
type => net_address, | ||
} | ||
or you can write | ||
service_config { 'config/nameserver': | ||
ensure => [ '10.0.0.1', '10.0.0.2' ], | ||
fmri => 'network/dns/client', | ||
type => net_address, | ||
} | ||
Be aware that in both cases the resource title has to be unique. | ||
Valid values to `ensure` are either a single value, an array or the | ||
value `absent` when you want to make sure the specified property is | ||
absent." | ||
|
||
def self.title_patterns | ||
[ | ||
# pattern to parse <fmri>:<prop> | ||
[ | ||
/^(.*):(.*)$/, | ||
[ | ||
[:fmri, lambda{|x| x} ], | ||
[:prop, lambda{|x| x} ] | ||
] | ||
], | ||
# pattern to parse <prop> | ||
[ | ||
/^(.*)$/, | ||
[ | ||
[:prop, lambda{|x| x}] | ||
] | ||
] | ||
] | ||
end | ||
|
||
def name | ||
# I am not sure if puppet relies on the resource having a name. | ||
# In general the name is the value of the namevar of the resource | ||
# Because we use multiple namevars (fmri and prop) this is not going | ||
# to work, so I overwrite the method here. The type may as well work | ||
# without this method but you knows... | ||
"#{self[:fmri]}:#{self[:prop]}" | ||
end | ||
|
||
newparam(:fmri) do | ||
desc "The name of the service you want to configure, e.g. | ||
`svc:/system/keymap:default`" | ||
|
||
isnamevar | ||
end | ||
|
||
newparam(:prop) do | ||
desc "The name of the property you want to configure, e.g. | ||
`keymap/layout`" | ||
|
||
isnamevar | ||
end | ||
|
||
newparam(:type) do | ||
desc "The type of the property. This is important when changing a setting" | ||
|
||
newvalues :astring | ||
newvalues :boolean | ||
newvalues :integer, :count, :time | ||
newvalues :net_address, :net_address_v4, :net_address_v6 | ||
end | ||
|
||
newproperty(:ensure, :array_matching => :all) do | ||
desc "The desired value of the property. You can either specify a | ||
single value, an array, or the special string `absent`, if you want | ||
to remove a property" | ||
|
||
newvalues :absent | ||
newvalues /.*/ | ||
|
||
def insync?(is) | ||
is == @should | ||
end | ||
end | ||
|
||
end | ||
end |
148 changes: 148 additions & 0 deletions
148
spec/integration/provider/service_config/svccfg_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
#! /usr/bin/env ruby | ||
|
||
require 'spec_helper' | ||
|
||
describe Puppet::Type.type(:service_config).provider(:svccfg), '(integration)' do | ||
|
||
before :each do | ||
described_class.stubs(:suitable?).returns true | ||
end | ||
|
||
let :fmri do | ||
'svc:/network/dns/client' | ||
end | ||
|
||
let :prop do | ||
'config/search' | ||
end | ||
|
||
let :default_options do | ||
{ | ||
:title => "#{fmri}:#{prop}", | ||
:fmri => fmri, | ||
:prop => prop, | ||
:type => :astring | ||
} | ||
end | ||
|
||
let :resource_singlevalue do | ||
Puppet::Type.type(:service_config).new(default_options.merge(:ensure => 'example.com')) | ||
end | ||
|
||
let :resource_listone do | ||
Puppet::Type.type(:service_config).new(default_options.merge(:ensure => ['test.com'])) | ||
end | ||
|
||
let :resource_listthree do | ||
Puppet::Type.type(:service_config).new(default_options.merge(:ensure => ['example.com', 'example.de', 'test.com'])) | ||
end | ||
|
||
let :resource_absent do | ||
Puppet::Type.type(:service_config).new(default_options.merge(:ensure => :absent)) | ||
end | ||
|
||
def run_in_catalog(resource) | ||
catalog = Puppet::Resource::Catalog.new | ||
catalog.host_config = false | ||
resource.expects(:err).never | ||
catalog.add_resource resource | ||
catalog.apply | ||
end | ||
|
||
describe "ensure is a single value" do | ||
it "should do nothing if value is in sync" do | ||
resource_singlevalue.provider.expects(:svccfg).with('-s', fmri, :listprop, prop).returns("config/search astring example.com\n") | ||
resource_singlevalue.provider.expects(:svccfg).with('-s', fmri, :setprop, prop, '=', 'astring:', '"example.com"').never | ||
run_in_catalog(resource_singlevalue) | ||
end | ||
|
||
it "should create the property if currently absent" do | ||
resource_singlevalue.provider.expects(:svccfg).with('-s', fmri, :listprop, prop).returns("\n\n") | ||
resource_singlevalue.provider.expects(:svccfg).with('-s', fmri, :setprop, prop, '=', 'astring:', '"example.com"') | ||
run_in_catalog(resource_singlevalue) | ||
end | ||
|
||
it "should replace a single value" do | ||
resource_singlevalue.provider.expects(:svccfg).with('-s', fmri, :listprop, prop).returns("config/search astring wrong.com\n") | ||
resource_singlevalue.provider.expects(:svccfg).with('-s', fmri, :setprop, prop, '=', 'astring:', '"example.com"') | ||
run_in_catalog(resource_singlevalue) | ||
end | ||
|
||
it "should replace a list of values" do | ||
resource_singlevalue.provider.expects(:svccfg).with('-s', fmri, :listprop, prop).returns("config/search astring \"example.com\" \"wrong.com\"\n") | ||
resource_singlevalue.provider.expects(:svccfg).with('-s', fmri, :setprop, prop, '=', 'astring:', '"example.com"') | ||
run_in_catalog(resource_singlevalue) | ||
end | ||
end | ||
|
||
describe "ensure is a list of values with one element" do | ||
it "should do nothing if value is in sync" do | ||
resource_listone.provider.expects(:svccfg).with('-s', fmri, :listprop, prop).returns("config/search astring test.com\n") | ||
resource_listone.provider.expects(:svccfg).with('-s', fmri, :setprop, prop, '=', 'astring:', '"test.com"').never | ||
run_in_catalog(resource_listone) | ||
end | ||
|
||
it "should create the property if currently absent" do | ||
resource_listone.provider.expects(:svccfg).with('-s', fmri, :listprop, prop).returns("\n\n") | ||
resource_listone.provider.expects(:svccfg).with('-s', fmri, :setprop, prop, '=', 'astring:', '"test.com"') | ||
run_in_catalog(resource_listone) | ||
end | ||
|
||
it "should replace a single value" do | ||
resource_listone.provider.expects(:svccfg).with('-s', fmri, :listprop, prop).returns("config/search astring wrong.com\n") | ||
resource_listone.provider.expects(:svccfg).with('-s', fmri, :setprop, prop, '=', 'astring:', '"test.com"') | ||
run_in_catalog(resource_listone) | ||
end | ||
|
||
it "should replace a list of values" do | ||
resource_listone.provider.expects(:svccfg).with('-s', fmri, :listprop, prop).returns("config/search astring \"example.com\" \"wrong.com\"\n") | ||
resource_listone.provider.expects(:svccfg).with('-s', fmri, :setprop, prop, '=', 'astring:', '"test.com"') | ||
run_in_catalog(resource_listone) | ||
end | ||
end | ||
|
||
describe "ensure is a list of values with more than one element" do | ||
it "should do nothing if value is in sync" do | ||
resource_listthree.provider.expects(:svccfg).with('-s', fmri, :listprop, prop).returns("config/search astring \"example.com\" \"example.de\" \"test.com\"\n") | ||
resource_listthree.provider.expects(:svccfg).with('-s', fmri, :setprop, prop, '=', 'astring:', '("example.com" "example.de" "test.com")').never | ||
run_in_catalog(resource_listthree) | ||
end | ||
|
||
it "should create the property if currently absent" do | ||
resource_listthree.provider.expects(:svccfg).with('-s', fmri, :listprop, prop).returns("\n\n") | ||
resource_listthree.provider.expects(:svccfg).with('-s', fmri, :setprop, prop, '=', 'astring:', '("example.com" "example.de" "test.com")') | ||
run_in_catalog(resource_listthree) | ||
end | ||
|
||
it "should replace a single value" do | ||
resource_listthree.provider.expects(:svccfg).with('-s', fmri, :listprop, prop).returns("config/search astring wrong.com\n") | ||
resource_listthree.provider.expects(:svccfg).with('-s', fmri, :setprop, prop, '=', 'astring:', '("example.com" "example.de" "test.com")') | ||
run_in_catalog(resource_listthree) | ||
end | ||
|
||
it "should replace a list of values" do | ||
resource_listthree.provider.expects(:svccfg).with('-s', fmri, :listprop, prop).returns("config/search astring \"example.com\" \"test.com\" \"example.de\"\n") | ||
resource_listthree.provider.expects(:svccfg).with('-s', fmri, :setprop, prop, '=', 'astring:', '("example.com" "example.de" "test.com")') | ||
run_in_catalog(resource_listthree) | ||
end | ||
end | ||
|
||
describe "ensure is absent" do | ||
it "should to nothing if property is already absent" do | ||
resource_absent.provider.expects(:svccfg).with('-s', fmri, :listprop, prop).returns("\n\n") | ||
resource_absent.provider.expects(:svccfg).never | ||
run_in_catalog(resource_absent) | ||
end | ||
it "should remove the property if it has a single value" do | ||
resource_absent.provider.expects(:svccfg).with('-s', fmri, :listprop, prop).returns("config/search astring wrong.com\n") | ||
resource_absent.provider.expects(:svccfg).with('-s', fmri, :delprop, prop) | ||
run_in_catalog(resource_absent) | ||
end | ||
|
||
it "should remove the property if it has a list of values" do | ||
resource_absent.provider.expects(:svccfg).with('-s', fmri, :listprop, prop).returns("config/search astring \"example.com\" \"test.com\" \"example.de\"\n") | ||
resource_absent.provider.expects(:svccfg).with('-s', fmri, :delprop, prop) | ||
run_in_catalog(resource_absent) | ||
end | ||
end | ||
end |
Oops, something went wrong.