puppet 5.5.6 → 5.5.7
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puppet might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile +3 -1
- data/Gemfile.lock +12 -12
- data/Rakefile +9 -0
- data/lib/puppet/application.rb +5 -0
- data/lib/puppet/application/apply.rb +1 -0
- data/lib/puppet/application/master.rb +9 -7
- data/lib/puppet/application/script.rb +1 -1
- data/lib/puppet/defaults.rb +51 -31
- data/lib/puppet/etc.rb +20 -0
- data/lib/puppet/file_serving/fileset.rb +1 -1
- data/lib/puppet/functions.rb +123 -0
- data/lib/puppet/functions/new.rb +37 -53
- data/lib/puppet/functions/warning.rb +1 -1
- data/lib/puppet/loaders.rb +1 -0
- data/lib/puppet/parser/functions.rb +3 -1
- data/lib/puppet/parser/functions/sprintf.rb +12 -1
- data/lib/puppet/pops/evaluator/runtime3_converter.rb +16 -0
- data/lib/puppet/pops/evaluator/runtime3_support.rb +3 -4
- data/lib/puppet/pops/issues.rb +8 -0
- data/lib/puppet/pops/loader/loader.rb +2 -2
- data/lib/puppet/pops/loader/loader_paths.rb +3 -1
- data/lib/puppet/pops/loader/module_loaders.rb +1 -1
- data/lib/puppet/pops/loader/ruby_legacy_function_instantiator.rb +62 -0
- data/lib/puppet/pops/loaders.rb +5 -21
- data/lib/puppet/pops/parser/heredoc_support.rb +1 -2
- data/lib/puppet/pops/parser/lexer2.rb +1 -1
- data/lib/puppet/pops/validation/checker4_0.rb +31 -6
- data/lib/puppet/pops/validation/validator_factory_4_0.rb +1 -0
- data/lib/puppet/property/keyvalue.rb +70 -8
- data/lib/puppet/provider/aix_object.rb +483 -0
- data/lib/puppet/provider/exec.rb +54 -57
- data/lib/puppet/provider/group/aix.rb +40 -115
- data/lib/puppet/provider/group/pw.rb +4 -8
- data/lib/puppet/provider/group/windows_adsi.rb +7 -4
- data/lib/puppet/provider/nameservice.rb +1 -25
- data/lib/puppet/provider/nameservice/directoryservice.rb +5 -3
- data/lib/puppet/provider/package/portage.rb +2 -2
- data/lib/puppet/provider/package/windows.rb +2 -2
- data/lib/puppet/provider/package/windows/exe_package.rb +3 -10
- data/lib/puppet/provider/package/zypper.rb +1 -1
- data/lib/puppet/provider/service/launchd.rb +19 -3
- data/lib/puppet/provider/service/windows.rb +49 -40
- data/lib/puppet/provider/user/aix.rb +180 -246
- data/lib/puppet/provider/user/windows_adsi.rb +9 -1
- data/lib/puppet/resource/catalog.rb +1 -5
- data/lib/puppet/type/augeas.rb +1 -1
- data/lib/puppet/type/exec.rb +16 -14
- data/lib/puppet/type/file.rb +2 -2
- data/lib/puppet/type/file/source.rb +9 -5
- data/lib/puppet/type/group.rb +65 -23
- data/lib/puppet/type/k5login.rb +2 -2
- data/lib/puppet/type/notify.rb +1 -1
- data/lib/puppet/type/package.rb +3 -6
- data/lib/puppet/type/resources.rb +12 -2
- data/lib/puppet/type/schedule.rb +8 -1
- data/lib/puppet/type/selboolean.rb +2 -2
- data/lib/puppet/type/selmodule.rb +3 -4
- data/lib/puppet/type/service.rb +2 -5
- data/lib/puppet/type/tidy.rb +1 -1
- data/lib/puppet/type/user.rb +15 -20
- data/lib/puppet/type/yumrepo.rb +2 -2
- data/lib/puppet/type/zone.rb +2 -2
- data/lib/puppet/util.rb +7 -3
- data/lib/puppet/util/execution.rb +15 -1
- data/lib/puppet/util/posix.rb +15 -0
- data/lib/puppet/util/storage.rb +12 -0
- data/lib/puppet/util/windows.rb +4 -2
- data/lib/puppet/util/windows/adsi.rb +235 -205
- data/lib/puppet/util/windows/process.rb +23 -3
- data/lib/puppet/util/windows/security.rb +14 -0
- data/lib/puppet/util/windows/service.rb +977 -0
- data/lib/puppet/util/windows/user.rb +3 -5
- data/lib/puppet/version.rb +1 -1
- data/locales/ja/puppet.po +705 -374
- data/locales/puppet.pot +485 -261
- data/man/man5/puppet.conf.5 +36 -15
- data/man/man8/puppet-agent.8 +1 -1
- data/man/man8/puppet-apply.8 +1 -1
- data/man/man8/puppet-ca.8 +1 -1
- data/man/man8/puppet-catalog.8 +1 -1
- data/man/man8/puppet-cert.8 +1 -1
- data/man/man8/puppet-certificate.8 +1 -1
- data/man/man8/puppet-certificate_request.8 +1 -1
- data/man/man8/puppet-certificate_revocation_list.8 +1 -1
- data/man/man8/puppet-config.8 +1 -1
- data/man/man8/puppet-describe.8 +1 -1
- data/man/man8/puppet-device.8 +1 -1
- data/man/man8/puppet-doc.8 +1 -1
- data/man/man8/puppet-epp.8 +1 -1
- data/man/man8/puppet-facts.8 +1 -1
- data/man/man8/puppet-filebucket.8 +1 -1
- data/man/man8/puppet-generate.8 +1 -1
- data/man/man8/puppet-help.8 +1 -1
- data/man/man8/puppet-key.8 +1 -1
- data/man/man8/puppet-lookup.8 +1 -1
- data/man/man8/puppet-man.8 +1 -1
- data/man/man8/puppet-master.8 +1 -1
- data/man/man8/puppet-module.8 +1 -1
- data/man/man8/puppet-node.8 +1 -1
- data/man/man8/puppet-parser.8 +1 -1
- data/man/man8/puppet-plugin.8 +1 -1
- data/man/man8/puppet-report.8 +1 -1
- data/man/man8/puppet-resource.8 +1 -1
- data/man/man8/puppet-script.8 +1 -1
- data/man/man8/puppet-status.8 +1 -1
- data/man/man8/puppet.8 +2 -2
- data/spec/fixtures/unit/provider/aix_object/aix_colon_list_real_world_input.out +1 -0
- data/spec/fixtures/unit/provider/aix_object/aix_colon_list_real_world_output.out +1 -0
- data/spec/fixtures/unit/provider/user/aix/aix_passwd_file.out +32 -0
- data/spec/integration/parser/collection_spec.rb +4 -8
- data/spec/integration/provider/service/windows_spec.rb +5 -5
- data/spec/integration/type/file_spec.rb +6 -6
- data/spec/integration/util/windows/adsi_spec.rb +6 -5
- data/spec/integration/util/windows/security_spec.rb +10 -7
- data/spec/integration/util/windows/user_spec.rb +37 -17
- data/spec/spec_helper.rb +0 -1
- data/spec/unit/application/apply_spec.rb +41 -2
- data/spec/unit/application/master_spec.rb +7 -0
- data/spec/unit/application_spec.rb +21 -3
- data/spec/unit/defaults_spec.rb +20 -0
- data/spec/unit/etc_spec.rb +25 -0
- data/spec/unit/file_serving/fileset_spec.rb +11 -11
- data/spec/unit/gettext/config_spec.rb +1 -1
- data/spec/unit/pops/evaluator/evaluating_parser_spec.rb +6 -6
- data/spec/unit/pops/loaders/loaders_spec.rb +40 -7
- data/spec/unit/pops/parser/parse_heredoc_spec.rb +16 -0
- data/spec/unit/pops/validator/validator_spec.rb +129 -10
- data/spec/unit/property/keyvalue_spec.rb +97 -6
- data/spec/unit/provider/aix_object_spec.rb +805 -0
- data/spec/unit/provider/group/aix_spec.rb +57 -0
- data/spec/unit/provider/group/pw_spec.rb +0 -6
- data/spec/unit/provider/group/windows_adsi_spec.rb +34 -35
- data/spec/unit/provider/nameservice/directoryservice_spec.rb +2 -2
- data/spec/unit/provider/package/windows/exe_package_spec.rb +3 -3
- data/spec/unit/provider/package/windows_spec.rb +4 -4
- data/spec/unit/provider/service/launchd_spec.rb +19 -0
- data/spec/unit/provider/service/windows_spec.rb +71 -78
- data/spec/unit/provider/user/aix_spec.rb +162 -116
- data/spec/unit/provider/user/windows_adsi_spec.rb +4 -4
- data/spec/unit/resource/catalog_spec.rb +2 -2
- data/spec/unit/ssl/certificate_authority_spec.rb +0 -1
- data/spec/unit/type/group_spec.rb +111 -13
- data/spec/unit/type/resources_spec.rb +18 -0
- data/spec/unit/util/execution_spec.rb +77 -0
- data/spec/unit/util/posix_spec.rb +28 -0
- data/spec/unit/util/storage_spec.rb +107 -0
- data/spec/unit/util/windows/adsi_spec.rb +108 -13
- data/spec/unit/util/windows/service_spec.rb +669 -0
- metadata +17 -5
- data/lib/puppet/provider/aixobject.rb +0 -392
- data/spec/unit/provider/aixobject_spec.rb +0 -101
@@ -42,17 +42,10 @@ class Puppet::Provider::Package::Windows
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def self.install_command(resource)
|
45
|
-
|
45
|
+
munge(resource[:source])
|
46
46
|
end
|
47
47
|
|
48
48
|
def uninstall_command
|
49
|
-
# 1. Launch using cmd /c start because if the executable is a console
|
50
|
-
# application Windows will automatically display its console window
|
51
|
-
# 2. Specify a quoted title, otherwise if uninstall_string is quoted,
|
52
|
-
# start will interpret that to be the title, and get confused
|
53
|
-
# 3. Specify /w (wait) to wait for uninstall to finish
|
54
|
-
command = ['cmd.exe', '/c', 'start', '"puppet-uninstall"', '/w']
|
55
|
-
|
56
49
|
# Only quote bare uninstall strings, e.g.
|
57
50
|
# C:\Program Files (x86)\Notepad++\uninstall.exe
|
58
51
|
# Don't quote uninstall strings that are already quoted, e.g.
|
@@ -60,9 +53,9 @@ class Puppet::Provider::Package::Windows
|
|
60
53
|
# Don't quote uninstall strings that contain arguments:
|
61
54
|
# "C:\Program Files (x86)\Git\unins000.exe" /SILENT
|
62
55
|
if uninstall_string =~ /\A[^"]*.exe\Z/i
|
63
|
-
command
|
56
|
+
command = "\"#{uninstall_string}\""
|
64
57
|
else
|
65
|
-
command
|
58
|
+
command = uninstall_string
|
66
59
|
end
|
67
60
|
|
68
61
|
command
|
@@ -1,4 +1,4 @@
|
|
1
|
-
Puppet::Type.type(:package).provide :zypper, :parent => :rpm do
|
1
|
+
Puppet::Type.type(:package).provide :zypper, :parent => :rpm, :source => :rpm do
|
2
2
|
desc "Support for SuSE `zypper` package manager. Found in SLES10sp2+ and SLES11.
|
3
3
|
|
4
4
|
This provider supports the `install_options` attribute, which allows command-line flags to be passed to zypper.
|
@@ -196,6 +196,22 @@ Puppet::Type.type(:service).provide :launchd, :parent => :base do
|
|
196
196
|
Puppet::Util::Plist.read_plist_file(path)
|
197
197
|
end
|
198
198
|
|
199
|
+
# Read overrides plist, retrying if necessary
|
200
|
+
def self.read_overrides
|
201
|
+
i = 1
|
202
|
+
overrides = nil
|
203
|
+
loop do
|
204
|
+
Puppet.debug(_("Reading overrides plist, attempt %{i}") % {i: i}) if i > 1
|
205
|
+
overrides = read_plist(launchd_overrides)
|
206
|
+
break unless overrides.nil?
|
207
|
+
raise Puppet::Error.new(_('Unable to read overrides plist, too many attempts')) if i == 20
|
208
|
+
Puppet.info(_('Overrides file could not be read, trying again.'))
|
209
|
+
Kernel.sleep(0.1)
|
210
|
+
i += 1
|
211
|
+
end
|
212
|
+
overrides
|
213
|
+
end
|
214
|
+
|
199
215
|
# Clean out the @property_hash variable containing the cached list of services
|
200
216
|
def flush
|
201
217
|
@property_hash.clear
|
@@ -300,7 +316,7 @@ Puppet::Type.type(:service).provide :launchd, :parent => :base do
|
|
300
316
|
_, job_plist = plist_from_label(resource[:name])
|
301
317
|
job_plist_disabled = job_plist["Disabled"] if job_plist.has_key?("Disabled")
|
302
318
|
|
303
|
-
if FileTest.file?(self.class.launchd_overrides) and overrides = self.class.
|
319
|
+
if FileTest.file?(self.class.launchd_overrides) and overrides = self.class.read_overrides
|
304
320
|
if overrides.has_key?(resource[:name])
|
305
321
|
if self.class.get_os_version < 14
|
306
322
|
overrides_disabled = overrides[resource[:name]]["Disabled"] if overrides[resource[:name]].has_key?("Disabled")
|
@@ -324,7 +340,7 @@ Puppet::Type.type(:service).provide :launchd, :parent => :base do
|
|
324
340
|
# rather than dealing with launchctl as it is unable to change the Disabled flag
|
325
341
|
# without actually loading/unloading the job.
|
326
342
|
def enable
|
327
|
-
overrides = self.class.
|
343
|
+
overrides = self.class.read_overrides
|
328
344
|
if self.class.get_os_version < 14
|
329
345
|
overrides[resource[:name]] = { "Disabled" => false }
|
330
346
|
else
|
@@ -334,7 +350,7 @@ Puppet::Type.type(:service).provide :launchd, :parent => :base do
|
|
334
350
|
end
|
335
351
|
|
336
352
|
def disable
|
337
|
-
overrides = self.class.
|
353
|
+
overrides = self.class.read_overrides
|
338
354
|
if self.class.get_os_version < 14
|
339
355
|
overrides[resource[:name]] = { "Disabled" => true }
|
340
356
|
else
|
@@ -16,54 +16,57 @@ Puppet::Type.type(:service).provide :windows, :parent => :service do
|
|
16
16
|
|
17
17
|
has_feature :refreshable
|
18
18
|
|
19
|
-
commands :net => 'net.exe'
|
20
|
-
|
21
19
|
def enable
|
22
|
-
|
23
|
-
raise Puppet::Error.new("Win32 service enable of #{@resource[:name]} failed" ) if( w32ss.nil? )
|
20
|
+
Puppet::Util::Windows::Service.set_startup_mode( @resource[:name], :SERVICE_AUTO_START )
|
24
21
|
rescue => detail
|
25
|
-
raise Puppet::Error.new("Cannot enable
|
22
|
+
raise Puppet::Error.new(_("Cannot enable %{resource_name}, error was: %{detail}") % { resource_name: @resource[:name], detail: detail }, detail )
|
26
23
|
end
|
27
24
|
|
28
25
|
def disable
|
29
|
-
|
30
|
-
raise Puppet::Error.new("Win32 service disable of #{@resource[:name]} failed" ) if( w32ss.nil? )
|
26
|
+
Puppet::Util::Windows::Service.set_startup_mode( @resource[:name], :SERVICE_DISABLED )
|
31
27
|
rescue => detail
|
32
|
-
raise Puppet::Error.new("Cannot disable
|
28
|
+
raise Puppet::Error.new(_("Cannot disable %{resource_name}, error was: %{detail}") % { resource_name: @resource[:name], detail: detail }, detail )
|
33
29
|
end
|
34
30
|
|
35
31
|
def manual_start
|
36
|
-
|
37
|
-
raise Puppet::Error.new("Win32 service manual enable of #{@resource[:name]} failed" ) if( w32ss.nil? )
|
32
|
+
Puppet::Util::Windows::Service.set_startup_mode( @resource[:name], :SERVICE_DEMAND_START )
|
38
33
|
rescue => detail
|
39
|
-
raise Puppet::Error.new("Cannot enable
|
34
|
+
raise Puppet::Error.new(_("Cannot enable %{resource_name} for manual start, error was: %{detail}") % { resource_name: @resource[:name], detail: detail }, detail )
|
40
35
|
end
|
41
36
|
|
42
37
|
def enabled?
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
38
|
+
return :false unless Puppet::Util::Windows::Service.exists?(@resource[:name])
|
39
|
+
|
40
|
+
start_type = Puppet::Util::Windows::Service.service_start_type(@resource[:name])
|
41
|
+
debug("Service #{@resource[:name]} start type is #{start_type}")
|
42
|
+
case start_type
|
43
|
+
when :SERVICE_AUTO_START,
|
44
|
+
:SERVICE_BOOT_START,
|
45
|
+
:SERVICE_SYSTEM_START
|
50
46
|
:true
|
51
|
-
when
|
47
|
+
when :SERVICE_DEMAND_START
|
52
48
|
:manual
|
53
|
-
when
|
49
|
+
when :SERVICE_DISABLED
|
54
50
|
:false
|
55
51
|
else
|
56
|
-
raise Puppet::Error.new("Unknown start type:
|
52
|
+
raise Puppet::Error.new(_("Unknown start type: %{start_type}") % { start_type: start_type })
|
57
53
|
end
|
58
54
|
rescue => detail
|
59
|
-
raise Puppet::Error.new("Cannot get start type
|
55
|
+
raise Puppet::Error.new(_("Cannot get start type %{resource_name}, error was: %{detail}") % { resource_name: @resource[:name], detail: detail }, detail )
|
60
56
|
end
|
61
57
|
|
62
58
|
def start
|
59
|
+
if status == :paused
|
60
|
+
Puppet::Util::Windows::Service.resume(@resource[:name])
|
61
|
+
return
|
62
|
+
end
|
63
|
+
|
64
|
+
# status == :stopped here
|
65
|
+
|
63
66
|
if enabled? == :false
|
64
67
|
# If disabled and not managing enable, respect disabled and fail.
|
65
68
|
if @resource[:enable].nil?
|
66
|
-
raise Puppet::Error
|
69
|
+
raise Puppet::Error.new(_("Will not start disabled service %{resource_name} without managing enable. Specify 'enable => false' to override.") % { resource_name: @resource[:name] })
|
67
70
|
# Otherwise start. If enable => false, we will later sync enable and
|
68
71
|
# disable the service again.
|
69
72
|
elsif @resource[:enable] == :true
|
@@ -72,35 +75,41 @@ Puppet::Type.type(:service).provide :windows, :parent => :service do
|
|
72
75
|
manual_start
|
73
76
|
end
|
74
77
|
end
|
75
|
-
|
76
|
-
net(:start, @resource[:name])
|
77
|
-
rescue Puppet::ExecutionFailure => detail
|
78
|
-
raise Puppet::Error.new("Cannot start #{@resource[:name]}, error was: #{detail}", detail )
|
78
|
+
Puppet::Util::Windows::Service.start(@resource[:name])
|
79
79
|
end
|
80
80
|
|
81
81
|
def stop
|
82
|
-
|
83
|
-
rescue Puppet::ExecutionFailure => detail
|
84
|
-
raise Puppet::Error.new("Cannot stop #{@resource[:name]}, error was: #{detail}", detail )
|
82
|
+
Puppet::Util::Windows::Service.stop(@resource[:name])
|
85
83
|
end
|
86
84
|
|
87
85
|
def status
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
when
|
86
|
+
return :stopped unless Puppet::Util::Windows::Service.exists?(@resource[:name])
|
87
|
+
|
88
|
+
current_state = Puppet::Util::Windows::Service.service_state(@resource[:name])
|
89
|
+
state = case current_state
|
90
|
+
when :SERVICE_STOPPED,
|
91
|
+
:SERVICE_STOP_PENDING
|
92
|
+
:stopped
|
93
|
+
when :SERVICE_PAUSED,
|
94
|
+
:SERVICE_PAUSE_PENDING
|
95
|
+
:paused
|
96
|
+
when :SERVICE_RUNNING,
|
97
|
+
:SERVICE_CONTINUE_PENDING,
|
98
|
+
:SERVICE_START_PENDING
|
99
|
+
:running
|
93
100
|
else
|
94
|
-
raise Puppet::Error.new("Unknown service state '
|
101
|
+
raise Puppet::Error.new(_("Unknown service state '%{current_state}' for service '%{resource_name}'") % { current_state: current_state, resource_name: @resource[:name] })
|
95
102
|
end
|
96
|
-
debug("Service #{@resource[:name]} is #{
|
103
|
+
debug("Service #{@resource[:name]} is #{current_state}")
|
97
104
|
return state
|
98
|
-
rescue => detail
|
99
|
-
raise Puppet::Error.new("Cannot get status of #{@resource[:name]}, error was: #{detail}", detail )
|
100
105
|
end
|
101
106
|
|
102
107
|
# returns all providers for all existing services and startup state
|
103
108
|
def self.instances
|
104
|
-
|
109
|
+
services = []
|
110
|
+
Puppet::Util::Windows::Service.services.each do |service_name, _|
|
111
|
+
services.push(new(:name => service_name))
|
112
|
+
end
|
113
|
+
services
|
105
114
|
end
|
106
115
|
end
|
@@ -1,25 +1,22 @@
|
|
1
|
-
#
|
2
1
|
# User Puppet provider for AIX. It uses standard commands to manage users:
|
3
2
|
# mkuser, rmuser, lsuser, chuser
|
4
3
|
#
|
5
4
|
# Notes:
|
6
5
|
# - AIX users can have expiry date defined with minute granularity,
|
7
|
-
# but
|
6
|
+
# but Puppet does not allow it. There is a ticket open for that (#5431)
|
7
|
+
#
|
8
8
|
# - AIX maximum password age is in WEEKs, not days
|
9
9
|
#
|
10
10
|
# See https://puppet.com/docs/puppet/latest/provider_development.html
|
11
11
|
# for more information
|
12
|
-
|
13
|
-
|
14
|
-
#
|
15
|
-
require 'puppet/provider/aixobject'
|
12
|
+
require 'puppet/provider/aix_object'
|
13
|
+
require 'puppet/util/posix'
|
16
14
|
require 'tempfile'
|
17
15
|
require 'date'
|
18
16
|
|
19
17
|
Puppet::Type.type(:user).provide :aix, :parent => Puppet::Provider::AixObject do
|
20
18
|
desc "User management for AIX."
|
21
19
|
|
22
|
-
# This will the default provider for this platform
|
23
20
|
defaultfor :operatingsystem => :aix
|
24
21
|
confine :operatingsystem => :aix
|
25
22
|
|
@@ -29,7 +26,6 @@ Puppet::Type.type(:user).provide :aix, :parent => Puppet::Provider::AixObject do
|
|
29
26
|
commands :delete => "/usr/sbin/rmuser"
|
30
27
|
commands :modify => "/usr/bin/chuser"
|
31
28
|
|
32
|
-
commands :lsgroup => "/usr/sbin/lsgroup"
|
33
29
|
commands :chpasswd => "/bin/chpasswd"
|
34
30
|
|
35
31
|
# Provider features
|
@@ -37,296 +33,234 @@ Puppet::Type.type(:user).provide :aix, :parent => Puppet::Provider::AixObject do
|
|
37
33
|
has_features :manages_homedir, :manages_passwords, :manages_shell
|
38
34
|
has_features :manages_expiry, :manages_password_age
|
39
35
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
#
|
45
|
-
#verify :groups, "Groups must be comma-separated" do |value|
|
46
|
-
# value !~ /\s/
|
47
|
-
#end
|
36
|
+
class << self
|
37
|
+
def group_provider
|
38
|
+
@group_provider ||= Puppet::Type.type(:group).provider(:aix)
|
39
|
+
end
|
48
40
|
|
49
|
-
|
50
|
-
|
51
|
-
["name"]
|
52
|
-
end
|
41
|
+
# Define some Puppet Property => AIX Attribute (and vice versa)
|
42
|
+
# conversion functions here.
|
53
43
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
# :aix_attr AIX command attribute name
|
59
|
-
# :puppet_prop Puppet property name
|
60
|
-
# :to Method to adapt puppet property to aix command value. Optional.
|
61
|
-
# :from Method to adapt aix command value to puppet property. Optional
|
62
|
-
self.attribute_mapping = [
|
63
|
-
{:aix_attr => :pgrp, :puppet_prop => :gid,
|
64
|
-
:to => :gid_to_attr,
|
65
|
-
:from => :gid_from_attr },
|
66
|
-
{:aix_attr => :id, :puppet_prop => :uid},
|
67
|
-
{:aix_attr => :groups, :puppet_prop => :groups},
|
68
|
-
{:aix_attr => :home, :puppet_prop => :home},
|
69
|
-
{:aix_attr => :shell, :puppet_prop => :shell},
|
70
|
-
{:aix_attr => :expires, :puppet_prop => :expiry,
|
71
|
-
:to => :expiry_to_attr,
|
72
|
-
:from => :expiry_from_attr },
|
73
|
-
{:aix_attr => :maxage, :puppet_prop => :password_max_age},
|
74
|
-
{:aix_attr => :minage, :puppet_prop => :password_min_age},
|
75
|
-
{:aix_attr => :pwdwarntime, :puppet_prop => :password_warn_days},
|
76
|
-
{:aix_attr => :attributes, :puppet_prop => :attributes},
|
77
|
-
{ :aix_attr => :gecos, :puppet_prop => :comment },
|
78
|
-
]
|
79
|
-
|
80
|
-
#--------------
|
81
|
-
# Command definition
|
82
|
-
|
83
|
-
# Return the IA module arguments based on the resource param ia_load_module
|
84
|
-
def get_ia_module_args
|
85
|
-
if @resource[:ia_load_module]
|
86
|
-
["-R", @resource[:ia_load_module].to_s]
|
87
|
-
else
|
88
|
-
[]
|
44
|
+
def gid_to_pgrp(provider, gid)
|
45
|
+
group = group_provider.find(gid, provider.ia_module_args)
|
46
|
+
|
47
|
+
group[:name]
|
89
48
|
end
|
90
|
-
end
|
91
49
|
|
92
|
-
|
93
|
-
|
94
|
-
[command(:lsgroup)] +
|
95
|
-
self.get_ia_module_args +
|
96
|
-
["-a", "id", value]
|
97
|
-
end
|
50
|
+
def pgrp_to_gid(provider, pgrp)
|
51
|
+
group = group_provider.find(pgrp, provider.ia_module_args)
|
98
52
|
|
99
|
-
|
100
|
-
|
101
|
-
end
|
53
|
+
group[:gid]
|
54
|
+
end
|
102
55
|
|
103
|
-
|
104
|
-
|
105
|
-
|
56
|
+
def expiry_to_expires(expiry)
|
57
|
+
return '0' if expiry == "0000-00-00" || expiry.to_sym == :absent
|
58
|
+
|
59
|
+
DateTime.parse(expiry, "%Y-%m-%d %H:%M")
|
60
|
+
.strftime("%m%d%H%M%y")
|
61
|
+
end
|
62
|
+
|
63
|
+
def expires_to_expiry(provider, expires)
|
64
|
+
return :absent if expires == '0'
|
65
|
+
|
66
|
+
unless (match_obj = /\A(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)\z/.match(expires))
|
67
|
+
#TRANSLATORS 'AIX' is the name of an operating system and should not be translated
|
68
|
+
Puppet.warning(_("Could not convert AIX expires date '%{expires}' on %{class_name}[%{resource_name}]") % { expires: expires, class_name: provider.resource.class.name, resource_name: provider.resource.name })
|
69
|
+
return :absent
|
70
|
+
end
|
106
71
|
|
107
|
-
|
108
|
-
|
109
|
-
|
72
|
+
month, day, year = match_obj[1], match_obj[2], match_obj[-1]
|
73
|
+
return "20#{year}-#{month}-#{day}"
|
74
|
+
end
|
75
|
+
|
76
|
+
# We do some validation before-hand to ensure the value's an Array,
|
77
|
+
# a String, etc. in the property. This routine does a final check to
|
78
|
+
# ensure our value doesn't have whitespace before we convert it to
|
79
|
+
# an attribute.
|
80
|
+
def groups_property_to_attribute(groups)
|
81
|
+
if groups =~ /\s/
|
82
|
+
raise ArgumentError, _("Invalid value %{groups}: Groups must be comma separated!") % { groups: groups }
|
83
|
+
end
|
84
|
+
|
85
|
+
groups
|
86
|
+
end
|
87
|
+
|
88
|
+
# We do not directly use the groups attribute value because that will
|
89
|
+
# always include the primary group, even if our user is not one of its
|
90
|
+
# members. Instead, we retrieve our property value by parsing the etc/group file,
|
91
|
+
# which matches what we do on our other POSIX platforms like Linux and Solaris.
|
110
92
|
#
|
111
|
-
#
|
112
|
-
|
113
|
-
|
114
|
-
|
93
|
+
# See https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/com.ibm.aix.files/group_security.htm
|
94
|
+
def groups_attribute_to_property(provider, _groups)
|
95
|
+
Puppet::Util::POSIX.groups_of(provider.resource[:name]).join(',')
|
96
|
+
end
|
115
97
|
end
|
116
98
|
|
117
|
-
|
118
|
-
|
119
|
-
def modifycmd(hash = property_hash)
|
120
|
-
args = self.hash2args(hash)
|
121
|
-
return nil if args.empty?
|
99
|
+
mapping puppet_property: :comment,
|
100
|
+
aix_attribute: :gecos
|
122
101
|
|
123
|
-
|
124
|
-
|
125
|
-
|
102
|
+
mapping puppet_property: :expiry,
|
103
|
+
aix_attribute: :expires,
|
104
|
+
property_to_attribute: method(:expiry_to_expires),
|
105
|
+
attribute_to_property: method(:expires_to_expiry)
|
126
106
|
|
127
|
-
|
128
|
-
|
129
|
-
|
107
|
+
mapping puppet_property: :gid,
|
108
|
+
aix_attribute: :pgrp,
|
109
|
+
property_to_attribute: method(:gid_to_pgrp),
|
110
|
+
attribute_to_property: method(:pgrp_to_gid)
|
130
111
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
super
|
135
|
-
# Reset the password if needed
|
136
|
-
self.password = @resource[:password] if @resource[:password]
|
137
|
-
end
|
112
|
+
mapping puppet_property: :groups,
|
113
|
+
property_to_attribute: method(:groups_property_to_attribute),
|
114
|
+
attribute_to_property: method(:groups_attribute_to_property)
|
138
115
|
|
116
|
+
mapping puppet_property: :home
|
117
|
+
mapping puppet_property: :shell
|
139
118
|
|
140
|
-
|
141
|
-
|
142
|
-
if key == :attributes
|
143
|
-
unless value and value.is_a? Hash
|
144
|
-
raise Puppet::Error, _("Attributes must be a list of pairs key=value on %{class_name}[%{resource_name}]") %
|
145
|
-
{ class_name: @resource.class.name, resource_name: @resource.name }
|
146
|
-
end
|
147
|
-
return value.map { |k,v| k.to_s.strip + "=" + v.to_s.strip}
|
148
|
-
end
|
119
|
+
numeric_mapping puppet_property: :uid,
|
120
|
+
aix_attribute: :id
|
149
121
|
|
150
|
-
|
151
|
-
|
122
|
+
numeric_mapping puppet_property: :password_max_age,
|
123
|
+
aix_attribute: :maxage
|
152
124
|
|
153
|
-
|
154
|
-
|
155
|
-
groupname=nil
|
156
|
-
execute(lsgroupscmd("ALL")).each_line { |entry|
|
157
|
-
attrs = self.parse_attr_list(entry, nil)
|
158
|
-
if attrs and attrs.include? :id and gid == attrs[:id].to_i
|
159
|
-
groupname = entry.split(" ")[0]
|
160
|
-
end
|
161
|
-
}
|
162
|
-
groupname
|
163
|
-
end
|
125
|
+
numeric_mapping puppet_property: :password_min_age,
|
126
|
+
aix_attribute: :minage
|
164
127
|
|
165
|
-
|
166
|
-
|
167
|
-
attrs = self.parse_attr_list(execute(lsgroupscmd(groupname)).split("\n")[0], nil)
|
168
|
-
attrs ? attrs[:id].to_i : nil
|
169
|
-
end
|
128
|
+
numeric_mapping puppet_property: :password_warn_days,
|
129
|
+
aix_attribute: :pwdwarntime
|
170
130
|
|
171
|
-
#
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
#TRANSLATORS 'AIX' is the name of the operating system and should not be translated
|
176
|
-
raise ArgumentError, _("AIX group must be a valid existing group") unless groupname
|
177
|
-
else
|
178
|
-
#TRANSLATORS 'AIX' is the name of the operating system and should not be translated
|
179
|
-
raise ArgumentError, _("AIX group must be a valid existing group") unless groupid_by_name(value)
|
180
|
-
groupname = value
|
181
|
-
end
|
182
|
-
groupname
|
183
|
-
end
|
131
|
+
# Now that we have all of our mappings, let's go ahead and make
|
132
|
+
# the resource methods (property getters + setters for our mapped
|
133
|
+
# properties + a getter for the attributes property).
|
134
|
+
mk_resource_methods
|
184
135
|
|
185
|
-
#
|
186
|
-
|
187
|
-
|
188
|
-
|
136
|
+
# Setting the primary group (pgrp attribute) on AIX causes both the
|
137
|
+
# current and new primary groups to be included in our user's groups,
|
138
|
+
# which is undesirable behavior. Thus, this custom setter resets the
|
139
|
+
# 'groups' property back to its previous value after setting the primary
|
140
|
+
# group.
|
141
|
+
def gid=(value)
|
142
|
+
old_pgrp = gid
|
143
|
+
cur_groups = groups
|
189
144
|
|
190
|
-
|
191
|
-
def gid_from_attr(value)
|
192
|
-
groupid_by_name(value)
|
193
|
-
end
|
145
|
+
set(:gid, value)
|
194
146
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
# that is,"%m%d%H%M%y"
|
200
|
-
newdate = '0'
|
201
|
-
if value.is_a? String and value!="0000-00-00"
|
202
|
-
d = DateTime.parse(value, "%Y-%m-%d %H:%M")
|
203
|
-
newdate = d.strftime("%m%d%H%M%y")
|
147
|
+
begin
|
148
|
+
self.groups = cur_groups
|
149
|
+
rescue Puppet::Error => detail
|
150
|
+
raise Puppet::Error, _("Could not reset the groups property back to %{cur_groups} after setting the primary group on %{resource}[%{name}]. This means that the previous primary group of %{old_pgrp} and the new primary group of %{new_pgrp} have been added to %{cur_groups}. You will need to manually reset the groups property if this is undesirable behavior. Detail: %{detail}") % { cur_groups: cur_groups, resource: @resource.class.name, name: @resource.name, old_pgrp: old_pgrp, new_pgrp: value, detail: detail }, detail.backtrace
|
204
151
|
end
|
205
|
-
newdate
|
206
152
|
end
|
207
153
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
154
|
+
# Helper function that parses the password from the given
|
155
|
+
# password filehandle. This is here to make testing easier
|
156
|
+
# for #password since we cannot configure Mocha to mock out
|
157
|
+
# a method and have it return a block's value, meaning we
|
158
|
+
# cannot test #password directly (not in a simple and obvious
|
159
|
+
# way, at least).
|
160
|
+
# @api private
|
161
|
+
def parse_password(f)
|
162
|
+
# From the docs, a user stanza is formatted as (newlines are explicitly
|
163
|
+
# stated here for clarity):
|
164
|
+
# <user>:\n
|
165
|
+
# <attribute1>=<value1>\n
|
166
|
+
# <attribute2>=<value2>\n
|
167
|
+
#
|
168
|
+
# First, find our user stanza
|
169
|
+
stanza = f.each_line.find { |line| line =~ /\A#{@resource[:name]}:/ }
|
170
|
+
return :absent unless stanza
|
171
|
+
|
172
|
+
# Now find the password line, if it exists. Note our call to each_line here
|
173
|
+
# will pick up right where we left off.
|
174
|
+
match_obj = nil
|
175
|
+
f.each_line.find do |line|
|
176
|
+
# Break if we find another user stanza. This means our user
|
177
|
+
# does not have a password.
|
178
|
+
break if line =~ /^\S+:$/
|
179
|
+
|
180
|
+
match_obj = /password = (\S+)/.match(line)
|
221
181
|
end
|
222
|
-
|
223
|
-
end
|
182
|
+
return :absent unless match_obj
|
224
183
|
|
225
|
-
|
226
|
-
# helper method for tests
|
227
|
-
# AIX reference indicates this file is ASCII
|
228
|
-
# https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/com.ibm.aix.files/passwd_security.htm
|
229
|
-
Puppet::FileSystem.open("/etc/security/passwd", nil, "r:ASCII")
|
184
|
+
match_obj[1]
|
230
185
|
end
|
231
186
|
|
232
|
-
#--------------------------------
|
233
|
-
# Getter and Setter
|
234
|
-
# When the provider is initialized, create getter/setter methods for each
|
235
|
-
# property our resource type supports.
|
236
|
-
# If setter or getter already defined it will not be overwritten
|
237
|
-
|
238
187
|
#- **password**
|
239
188
|
# The user's password, in whatever encrypted format the local machine
|
240
189
|
# requires. Be sure to enclose any value that includes a dollar sign ($)
|
241
190
|
# in single quotes ('). Requires features manages_passwords.
|
242
191
|
#
|
243
|
-
# Retrieve the password parsing
|
192
|
+
# Retrieve the password parsing the /etc/security/passwd file.
|
244
193
|
def password
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
f.each_line { |l| break if l =~ /^#{user}:\s*$/ }
|
250
|
-
if ! f.eof?
|
251
|
-
f.each_line { |l|
|
252
|
-
# If there is a new user stanza, stop
|
253
|
-
break if l =~ /^\S*:\s*$/
|
254
|
-
# If the password= entry is found, return it, stripping trailing space
|
255
|
-
if l =~ /^\s*password\s*=\s*(\S*)\s*$/
|
256
|
-
password = $1; break;
|
257
|
-
end
|
258
|
-
}
|
194
|
+
# AIX reference indicates this file is ASCII
|
195
|
+
# https://www.ibm.com/support/knowledgecenter/en/ssw_aix_72/com.ibm.aix.files/passwd_security.htm
|
196
|
+
Puppet::FileSystem.open("/etc/security/passwd", nil, "r:ASCII") do |f|
|
197
|
+
parse_password(f)
|
259
198
|
end
|
260
|
-
f.close()
|
261
|
-
return password
|
262
199
|
end
|
263
200
|
|
264
201
|
def password=(value)
|
265
202
|
user = @resource[:name]
|
266
203
|
|
267
|
-
# Puppet execute does not support strings as input, only files.
|
268
|
-
# The password is expected to be in an encrypted format given -e is specified:
|
269
|
-
# https://www.ibm.com/support/knowledgecenter/ssw_aix_71/com.ibm.aix.cmds1/chpasswd.htm
|
270
|
-
# /etc/security/passwd is specified as an ASCII file per the AIX documentation
|
271
|
-
tmpfile = Tempfile.new("puppet_#{user}_pw", :encoding => Encoding::ASCII)
|
272
|
-
tmpfile << "#{user}:#{value}\n"
|
273
|
-
tmpfile.close()
|
274
|
-
|
275
|
-
# Options '-e', '-c', use encrypted password and clear flags
|
276
|
-
# Must receive "user:enc_password" as input
|
277
|
-
# command, arguments = {:failonfail => true, :combine => true}
|
278
|
-
# Fix for bugs #11200 and #10915
|
279
|
-
cmd = [self.class.command(:chpasswd), get_ia_module_args, '-e', '-c'].flatten
|
280
204
|
begin
|
281
|
-
|
282
|
-
#
|
205
|
+
# Puppet execute does not support strings as input, only files.
|
206
|
+
# The password is expected to be in an encrypted format given -e is specified:
|
207
|
+
# https://www.ibm.com/support/knowledgecenter/ssw_aix_71/com.ibm.aix.cmds1/chpasswd.htm
|
208
|
+
# /etc/security/passwd is specified as an ASCII file per the AIX documentation
|
209
|
+
tempfile = nil
|
210
|
+
tempfile = Tempfile.new("puppet_#{user}_pw", :encoding => Encoding::ASCII)
|
211
|
+
tempfile << "#{user}:#{value}\n"
|
212
|
+
tempfile.close()
|
213
|
+
|
214
|
+
# Options '-e', '-c', use encrypted password and clear flags
|
215
|
+
# Must receive "user:enc_password" as input
|
216
|
+
# command, arguments = {:failonfail => true, :combine => true}
|
217
|
+
# Fix for bugs #11200 and #10915
|
218
|
+
cmd = [self.class.command(:chpasswd), *ia_module_args, '-e', '-c']
|
219
|
+
execute_options = {
|
220
|
+
:failonfail => false,
|
221
|
+
:combine => true,
|
222
|
+
:stdinfile => tempfile.path
|
223
|
+
}
|
224
|
+
output = execute(cmd, execute_options)
|
225
|
+
|
226
|
+
# chpasswd can return 1, even on success (at least on AIX 6.1); empty output
|
227
|
+
# indicates success
|
283
228
|
if output != ""
|
284
229
|
raise Puppet::ExecutionFailure, "chpasswd said #{output}"
|
285
230
|
end
|
286
231
|
rescue Puppet::ExecutionFailure => detail
|
287
|
-
raise Puppet::Error, "Could not set
|
232
|
+
raise Puppet::Error, "Could not set password on #{@resource.class.name}[#{@resource.name}]: #{detail}", detail.backtrace
|
288
233
|
ensure
|
289
|
-
|
234
|
+
if tempfile
|
235
|
+
# Extra close will noop. This is in case the write to our tempfile
|
236
|
+
# fails.
|
237
|
+
tempfile.close()
|
238
|
+
tempfile.delete()
|
239
|
+
end
|
290
240
|
end
|
291
241
|
end
|
292
242
|
|
293
|
-
def
|
294
|
-
|
295
|
-
managed_attributes = [managed_attributes] unless managed_attributes.is_a?(Array)
|
296
|
-
managed_attributes.map {|attr| key, _ = attr.split("="); key.strip.to_sym}
|
297
|
-
end
|
298
|
-
|
299
|
-
def should_include?(key, managed_keys)
|
300
|
-
!self.class.attribute_mapping_from.include?(key) and
|
301
|
-
!self.class.attribute_ignore.include?(key) and
|
302
|
-
managed_keys.include?(key)
|
303
|
-
end
|
304
|
-
|
305
|
-
def filter_attributes(hash)
|
306
|
-
# Return only managed attributes.
|
307
|
-
managed_keys = managed_attribute_keys(hash)
|
308
|
-
results = hash.select {
|
309
|
-
|k,v| should_include?(k, managed_keys)
|
310
|
-
}.inject({}) {
|
311
|
-
|h, array| h[array[0]] = array[1]; h
|
312
|
-
}
|
313
|
-
results
|
314
|
-
end
|
243
|
+
def create
|
244
|
+
super
|
315
245
|
|
316
|
-
|
317
|
-
|
318
|
-
|
246
|
+
# We specify the 'groups' AIX attribute in AixObject's create method
|
247
|
+
# when creating our user. However, this does not always guarantee that
|
248
|
+
# our 'groups' property is set to the right value. For example, the
|
249
|
+
# primary group will always be included in the 'groups' property. This is
|
250
|
+
# bad if we're explicitly managing the 'groups' property under inclusive
|
251
|
+
# membership, and we are not specifying the primary group in the 'groups'
|
252
|
+
# property value.
|
253
|
+
#
|
254
|
+
# Setting the groups property here a second time will ensure that our user is
|
255
|
+
# created and in the right state. Note that this is an idempotent operation,
|
256
|
+
# so if AixObject's create method already set it to the right value, then this
|
257
|
+
# will noop.
|
258
|
+
if (groups = @resource.should(:groups))
|
259
|
+
self.groups = groups
|
260
|
+
end
|
319
261
|
|
320
|
-
|
321
|
-
|
322
|
-
param = :attributes
|
323
|
-
cmd = modifycmd({param => filter_attributes(attr_hash)})
|
324
|
-
if cmd
|
325
|
-
begin
|
326
|
-
execute(cmd)
|
327
|
-
rescue Puppet::ExecutionFailure => detail
|
328
|
-
raise Puppet::Error, "Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}", detail.backtrace
|
329
|
-
end
|
262
|
+
if (password = @resource.should(:password))
|
263
|
+
self.password = password
|
330
264
|
end
|
331
265
|
end
|
332
266
|
|