dust-deploy 0.16.4 → 0.16.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,7 +23,6 @@ class Sshd < Recipe
23
23
  def default_config
24
24
  { 'Port' => 22,
25
25
  'Protocol' => 2,
26
- 'AcceptEnv' => 'LANG LC_*',
27
26
  'HostKey' => [ '/etc/ssh/ssh_host_dsa_key',
28
27
  '/etc/ssh/ssh_host_ecdsa_key',
29
28
  '/etc/ssh/ssh_host_rsa_key' ],
@@ -38,7 +37,7 @@ class Sshd < Recipe
38
37
 
39
38
  def generate_default_config
40
39
  @config.boolean_to_string!
41
- @config = default_config.merge @config
40
+ @config = default_config.merge(@config)
42
41
 
43
42
  unless @config['sftp']
44
43
  @config['Subsystem'] ||= 'sftp /usr/lib/openssh/sftp-server' if @node.uses_apt?
@@ -66,11 +65,11 @@ class Sshd < Recipe
66
65
  if value.is_a? Hash
67
66
  value.each do |k, v|
68
67
  conditional_blocks << "#{key} #{k}\n"
69
- v.to_array.each { |x, y| conditional_blocks << " #{x} #{y}\n" }
68
+ Array(v).each { |x, y| conditional_blocks << " #{x} #{y}\n" }
70
69
  end
71
70
 
72
71
  else
73
- value.to_array.each { |value| @sshd_config << "#{key} #{value}\n" }
72
+ Array(value).each { |value| @sshd_config << "#{key} #{value}\n" }
74
73
  end
75
74
  end
76
75
 
@@ -12,7 +12,7 @@ class Sysctl < Recipe
12
12
 
13
13
  # apply template sysctls
14
14
  if templates
15
- templates.to_array.each do |template|
15
+ Array(templates).each do |template|
16
16
  @node.messages.add("configuring sysctls for template #{template}\n")
17
17
  apply template, self.send(template)
18
18
  end
@@ -67,7 +67,7 @@ class Users < Recipe
67
67
  authorized_keys = ''
68
68
 
69
69
  # create the authorized_keys hash for this user
70
- ssh_users.to_array.each do |ssh_user|
70
+ Array(ssh_users).each do |ssh_user|
71
71
  unless users[ssh_user]
72
72
  return @node.messages.add("#{ssh_user} cannot be found in #{@template_path}/public_keys.yaml").failed
73
73
  end
@@ -134,7 +134,7 @@ class ZabbixAgent < Recipe
134
134
 
135
135
  # check for security patches and system updates on emerge systems
136
136
  def enable_apt
137
- updates = [ 'apt.updates,aptitude search \'~U\' |wc -l' ]
137
+ updates = [ 'apt.updates,apt-cache search \'~U\' |wc -l' ]
138
138
  if @node.is_debian?
139
139
  @node.collect_facts
140
140
  updates << "debian.security,debsecan --suite #{@node['lsbdistcodename']} --only-fixed --format packages |wc -l"
@@ -104,7 +104,7 @@ module Dust
104
104
  end
105
105
 
106
106
 
107
- desc 'system_update', 'perform a full system upgrade (using aptitude, emerge, yum)'
107
+ desc 'system_update', 'perform a full system upgrade (using apt-get, emerge, yum)'
108
108
  default_options
109
109
 
110
110
  def system_update
@@ -321,7 +321,7 @@ module Dust
321
321
  exit
322
322
  end
323
323
 
324
- yaml_files.to_array.each do |file|
324
+ Array(yaml_files).each do |file|
325
325
  node = YAML.load ERB.new( File.read(file), nil, '%<>').result
326
326
 
327
327
  # if the file is empty, just skip it
@@ -344,7 +344,7 @@ module Dust
344
344
 
345
345
  # if more than one hostname is specified, create a node
346
346
  # with the same settings for each hostname
347
- node['hostname'].to_array.each do |hostname|
347
+ Array(node['hostname']).each do |hostname|
348
348
  n = node.clone
349
349
 
350
350
  # overwrite hostname with single hostname (in case there are multiple)
@@ -1,13 +1,9 @@
1
- require 'net/ssh'
2
- require 'net/scp'
3
- require 'net/ssh/proxy/socks5'
4
- require 'erb'
5
- require 'tempfile'
6
- require 'colorize'
1
+ # include all extensions
2
+ Dir["#{File.dirname(__FILE__)}/server/*.rb"].each { |file| require file }
7
3
 
8
4
  module Dust
9
5
  class Server
10
- attr_reader :ssh, :messages
6
+ attr_reader :messages
11
7
 
12
8
  def default_options options = {}
13
9
  { :quiet => false, :indent => 1 }.merge options
@@ -23,871 +19,6 @@ module Dust
23
19
  @messages = Messages.new
24
20
  end
25
21
 
26
- def connect
27
- messages.print_hostname_header(@node['hostname']) unless $parallel
28
-
29
- begin
30
- # connect to proxy if given
31
- if @node['proxy']
32
- host, port = @node['proxy'].split ':'
33
- proxy = Net::SSH::Proxy::SOCKS5.new(host, port)
34
- else
35
- proxy = nil
36
- end
37
-
38
- @ssh = Net::SSH.start @node['fqdn'], @node['user'],
39
- { :password => @node['password'],
40
- :port => @node['port'],
41
- :proxy => proxy }
42
- rescue Exception
43
- error_message = "coudln't connect to #{@node['fqdn']}"
44
- error_message << " (via socks5 proxy #{@node['proxy']})" if proxy
45
- messages.add(error_message, :indent => 0).failed
46
- return false
47
- end
48
-
49
- true
50
- end
51
-
52
- def disconnect
53
- @ssh.close
54
- end
55
-
56
- def exec command, options={:live => false, :as_user => false}
57
- sudo_authenticated = false
58
- stdout = ''
59
- stderr = ''
60
- exit_code = nil
61
- exit_signal = nil
62
-
63
- # prepend a newline, if output is live
64
- messages.add("\n", :indent => 0) if options[:live]
65
-
66
- @ssh.open_channel do |channel|
67
-
68
- # if :as_user => user is given, execute as user (be aware of ' usage)
69
- command = "su #{options[:as_user]} -l -c '#{command}'" if options[:as_user]
70
-
71
- # request a terminal (sudo needs it)
72
- # and prepend "sudo"
73
- # command is wrapped in ", escapes " in the command string
74
- # and then executed using "sh -c", so that
75
- # the use of > < && || | and ; doesn't screw things up
76
- if @node['sudo']
77
- channel.request_pty
78
- command = "sudo -k -- sh -c \"#{command.gsub('"','\\"')}\""
79
- end
80
-
81
- channel.exec command do |ch, success|
82
- abort "FAILED: couldn't execute command (ssh.channel.exec)" unless success
83
-
84
- channel.on_data do |ch, data|
85
- # only send password if sudo mode is enabled,
86
- # and only send password once in a session (trying to prevent attacks reading out the password)
87
- if data =~ /\[sudo\] password for #{@node['user']}/
88
-
89
- raise 'password requested, but none given in config!' if @node['password'].empty?
90
- raise 'already sent password, but sudo requested the password again. (wrong password?)' if sudo_authenticated
91
-
92
- # we're not authenticated yet, send password
93
- channel.send_data "#{@node['password']}\n"
94
- sudo_authenticated = true
95
-
96
- else
97
- # skip everything util authenticated (if sudo is used and password given in config)
98
- next if @node['sudo'] and not @node['password'].empty? and not sudo_authenticated
99
-
100
- stdout += data
101
- messages.add(data.green, :indent => 0) if options[:live] and not data.empty?
102
- end
103
- end
104
-
105
- channel.on_extended_data do |ch, type, data|
106
- stderr += data
107
- messages.add(data.red, :indent => 0) if options[:live] and not data.empty?
108
- end
109
-
110
- channel.on_request('exit-status') { |ch, data| exit_code = data.read_long }
111
- channel.on_request('exit-signal') { |ch, data| exit_signal = data.read_long }
112
- end
113
- end
114
-
115
- @ssh.loop
116
-
117
- # sudo usage provokes a heading newline that's unwanted.
118
- stdout.sub! /^(\r\n|\n|\r)/, '' if @node['sudo']
119
-
120
- { :stdout => stdout, :stderr => stderr, :exit_code => exit_code, :exit_signal => exit_signal }
121
- end
122
-
123
- def write destination, content, options = {}
124
- options = default_options.merge options
125
-
126
- msg = messages.add("deploying #{File.basename destination}", options)
127
-
128
- f = Tempfile.new 'dust-write'
129
- f.print content
130
- f.close
131
-
132
- ret = msg.parse_result(scp(f.path, destination, :quiet => true))
133
- f.unlink
134
-
135
- ret
136
- end
137
-
138
- def append destination, newcontent, options = {}
139
- options = default_options.merge options
140
-
141
- msg = messages.add("appending to #{File.basename destination}", options)
142
-
143
- content = exec("cat #{destination}")[:stdout]
144
- content.concat newcontent
145
-
146
- msg.parse_result(write(destination, content, :quiet => true))
147
- end
148
-
149
- def scp(source, destination, options = {})
150
- options = default_options.merge options
151
-
152
- # make sure scp is installed on client
153
- install_package('openssh-clients', :quiet => true) if uses_rpm?
154
-
155
- msg = messages.add("deploying #{File.basename source}", options)
156
-
157
- # check if destination is a directory
158
- is_dir = dir_exists?(destination, :quiet => true)
159
-
160
- # save permissions if the file already exists
161
- ret = exec("stat -c %a:%u:%g #{destination}")
162
- if ret[:exit_code] == 0 and not is_dir
163
- permissions, user, group = ret[:stdout].chomp.split(':')
164
- else
165
- # files = 644, dirs = 755
166
- permissions = 'ug-x,o-wx,u=rwX,g=rX,o=rX'
167
- user = 'root'
168
- group = 'root'
169
- end
170
-
171
- # if in sudo mode, copy file to temporary place, then move using sudo
172
- if @node['sudo']
173
- tmpdir = mktemp(:type => 'directory')
174
- return msg.failed('could not create temporary directory (needed for sudo)') unless tmpdir
175
-
176
- # temporary destination in tmpdir
177
- tmpdest = "#{tmpdir}/#{File.basename(destination)}"
178
-
179
- # allow user to write file without sudo (for scp)
180
- # then change file back to root, and copy to the destination
181
- chown(@node['user'], tmpdir, :quiet => true)
182
- @ssh.scp.upload!(source, tmpdest, :recursive => true)
183
-
184
- # set file permissions
185
- chown("#{user}:#{group}", tmpdest, :quiet => true) if user and group
186
- chmod(permissions, tmpdest, :quiet => true)
187
-
188
- # if destination is a directory, append real filename
189
- destination = "#{destination}/#{File.basename(source)}" if is_dir
190
-
191
- # move the file from the temporary location to where it actually belongs
192
- msg.parse_result(exec("mv -f #{tmpdest} #{destination}")[:exit_code])
193
-
194
- # remove temporary directory
195
- rm(tmpdir, :quiet => true)
196
-
197
- else
198
- @ssh.scp.upload!(source, destination, :recursive => true)
199
- msg.ok
200
-
201
- # set file permissions
202
- chown("#{user}:#{group}", destination, :quiet => true) if user and group
203
- chmod(permissions, destination, :quiet => true)
204
- end
205
-
206
- restorecon(destination, options) # restore SELinux labels
207
- end
208
-
209
- # download a file (sudo not yet supported)
210
- def download source, destination, options = {}
211
- options = default_options.merge options
212
-
213
- # make sure scp is installed on client
214
- install_package 'openssh-clients', :quiet => true if uses_rpm?
215
-
216
- msg = messages.add("downloading #{File.basename source}", options)
217
- msg.parse_result(@ssh.scp.download!(source, destination))
218
- end
219
-
220
- def symlink source, destination, options = {}
221
- options = default_options.merge options
222
-
223
- msg = messages.add("symlinking #{File.basename source} to '#{destination}'", options)
224
- ret = msg.parse_result(exec("ln -s #{source} #{destination}")[:exit_code])
225
- restorecon destination, options # restore SELinux labels
226
- ret
227
- end
228
-
229
- def chmod mode, file, options = {}
230
- options = default_options.merge options
231
-
232
- msg = messages.add("setting mode of #{File.basename file} to #{mode}", options)
233
- msg.parse_result(exec("chmod -R #{mode} #{file}")[:exit_code])
234
- end
235
-
236
- def chown user, file, options = {}
237
- options = default_options.merge options
238
-
239
- msg = messages.add("setting owner of #{File.basename file} to #{user}", options)
240
- msg.parse_result(exec("chown -R #{user} #{file}")[:exit_code])
241
- end
242
-
243
- def chcon(permissions, file, options = {})
244
- options = default_options.merge(options)
245
-
246
- # just return if selinux is not enabled
247
- return true unless selinuxenabled?
248
-
249
- args = ""
250
- args << " --type #{permissions['type']}" if permissions['type']
251
- args << " --recursive #{permissions['recursive']}" if permissions['recursive']
252
- args << " --user #{permissions['user']}" if permissions['user']
253
- args << " --range #{permissions['range']}" if permissions['range']
254
- args << " --role #{permissions['role']}" if permissions['role']
255
-
256
- msg = messages.add("setting selinux permissions of #{File.basename file}", options)
257
- msg.parse_result(exec("chcon #{args} #{file}")[:exit_code])
258
- end
259
-
260
- def selinuxenabled?
261
- return true if exec('selinuxenabled')[:exit_code] == 0
262
- false
263
- end
264
-
265
- def rm file, options = {}
266
- options = default_options.merge options
267
-
268
- msg = messages.add("deleting #{file}", options)
269
- msg.parse_result(exec("rm -rf #{file}")[:exit_code])
270
- end
271
-
272
- def cp source, destination, options = {}
273
- options = default_options.merge options
274
-
275
- # get rid of overly careful aliases
276
- exec 'unalias -a'
277
-
278
- msg = messages.add("copying #{source} to #{destination}", options)
279
- msg.parse_result(exec("cp -a #{source} #{destination}")[:exit_code])
280
- end
281
-
282
- def mv source, destination, options = {}
283
- options = default_options.merge options
284
-
285
- # get rid of overly careful aliases
286
- exec 'unalias -a'
287
-
288
- msg = messages.add("moving #{source} to #{destination}", options)
289
- msg.parse_result(exec("mv #{source} #{destination}")[:exit_code])
290
- end
291
-
292
- def mkdir dir, options = {}
293
- options = default_options.merge options
294
-
295
- return true if dir_exists? dir, :quiet => true
296
-
297
- msg = messages.add("creating directory #{dir}", options)
298
- ret = msg.parse_result(exec("mkdir -p #{dir}")[:exit_code])
299
- restorecon dir, options # restore SELinux labels
300
- ret
301
- end
302
-
303
- # check if restorecon (selinux) is available
304
- # if so, run it on "path" recursively
305
- def restorecon path, options = {}
306
- options = default_options.merge options
307
-
308
- # if selinux is not enabled, just return
309
- return true unless selinuxenabled?
310
-
311
- msg = messages.add("restoring selinux labels for #{path}", options)
312
- msg.parse_result(exec("restorecon -R #{path}")[:exit_code])
313
- end
314
-
315
- def get_system_users options = {}
316
- options = default_options.merge options
317
-
318
- msg = messages.add("getting all system users", options)
319
- ret = exec 'getent passwd |cut -d: -f1'
320
- msg.parse_result(ret[:exit_code])
321
-
322
- users = []
323
- ret[:stdout].each do |user|
324
- users.push user.chomp
325
- end
326
- users
327
- end
328
-
329
- # checks if one of the packages is installed
330
- def package_installed? packages, options = {}
331
- options = default_options.merge options
332
-
333
- packages = [ packages ] if packages.is_a? String
334
-
335
- msg = messages.add("checking if #{packages.join(' or ')} is installed", options)
336
-
337
- packages.each do |package|
338
- if uses_apt?
339
- return msg.ok if exec("dpkg -l #{package} |grep '^ii'")[:exit_code] == 0
340
- elsif uses_emerge?
341
- return msg.ok unless exec("qlist -I #{package}")[:stdout].empty?
342
- elsif uses_rpm?
343
- return msg.ok if exec("rpm -q #{package}")[:exit_code] == 0
344
- elsif uses_pacman?
345
- return msg.ok if exec("pacman -Q #{package}")[:exit_code] == 0
346
- elsif uses_opkg?
347
- return msg.ok unless exec("opkg status #{package}")[:stdout].empty?
348
- end
349
- end
350
-
351
- msg.failed
352
- end
353
-
354
- def install_package(package, options = {})
355
- options = default_options.merge(options)
356
- options[:env] ||= ''
357
-
358
- if package_installed?(package, :quiet => true)
359
- return messages.add("package #{package} already installed", options).ok
360
- end
361
-
362
- # if package is an url, download and install the package file
363
- if package =~ /^(http:\/\/|https:\/\/|ftp:\/\/)/
364
- if uses_apt?
365
- messages.add("installing #{package}\n", options)
366
- return false unless install_package('wget')
367
-
368
- msg = messages.add('downloading package', options.merge(:indent => options[:indent] + 1))
369
-
370
- # creating temporary file
371
- tmpfile = mktemp
372
- return msg.failed('could not create temporary file') unless tmpfile
373
-
374
- msg.parse_result(exec("wget #{package} -O #{tmpfile}")[:exit_code])
375
-
376
- msg = messages.add('installing package', options.merge(:indent => options[:indent] + 1))
377
- ret = msg.parse_result(exec("dpkg -i #{tmpfile}")[:exit_code])
378
-
379
- msg = messages.add('deleting downloaded file', options.merge(:indent => options[:indent] + 1))
380
- msg.parse_result(rm(tmpfile, :quiet => true))
381
-
382
- return ret
383
-
384
- elsif uses_rpm?
385
- msg = messages.add("installing #{package}", options)
386
- return msg.parse_result(exec("rpm -U #{package}")[:exit_code])
387
-
388
- else
389
- return msg.failed("\ninstalling packages from url not yet supported " +
390
- "for your distribution. feel free to contribute!").failed
391
- end
392
-
393
- # package is not an url, use package manager
394
- else
395
- msg = messages.add("installing #{package}", options)
396
-
397
- if uses_apt?
398
- exec "DEBIAN_FRONTEND=noninteractive aptitude install -y #{package}"
399
- elsif uses_emerge?
400
- exec "#{options[:env]} emerge #{package}"
401
- elsif uses_rpm?
402
- exec "yum install -y #{package}"
403
- elsif uses_pacman?
404
- exec "echo y |pacman -S #{package}"
405
- elsif uses_opkg?
406
- exec "opkg install #{package}"
407
- else
408
- return msg.failed("\ninstall_package only supports apt, emerge and yum systems at the moment")
409
- end
410
-
411
- # check if package actually was installed
412
- return msg.parse_result(package_installed?(package, :quiet => true))
413
- end
414
- end
415
-
416
- # check if installed package is at least version min_version
417
- def package_min_version?(package, min_version, options = {})
418
- msg = messages.add("checking if #{package} is at least version #{min_version}", options)
419
- return msg.failed unless package_installed?(package, :quiet => true)
420
-
421
- if uses_apt?
422
- v = exec("dpkg --list |grep #{package}")[:stdout].chomp
423
- elsif uses_rpm?
424
- v = exec("rpm -q #{package}")[:stdout].chomp
425
- elsif uses_pacman?
426
- v = exec("pacman -Q #{package}")[:stdout].chomp
427
- else
428
- return msg.failed('os not supported')
429
- end
430
-
431
- # convert version numbers to arrays
432
- current_version = v.to_s.split(/[-. ]/ ).select {|j| j =~ /^[0-9]+$/ }
433
- min_version = min_version.to_s.split(/[-. ]/ ).select {|j| j =~ /^[0-9]+$/ }
434
-
435
- # compare
436
- min_version.each_with_index do |i, pos|
437
- break unless current_version[pos]
438
- return msg.failed if i.to_i < current_version[pos].to_i
439
- end
440
-
441
- msg.ok
442
- end
443
-
444
- def remove_package package, options = {}
445
- options = default_options.merge options
446
-
447
- unless package_installed? package, :quiet => true
448
- return messages.add("package #{package} not installed", options).ok
449
- end
450
-
451
- msg = messages.add("removing #{package}", options)
452
- if uses_apt?
453
- msg.parse_result(exec("DEBIAN_FRONTEND=noninteractive aptitude purge -y #{package}")[:exit_code])
454
- elsif uses_emerge?
455
- msg.parse_result(exec("emerge --unmerge #{package}")[:exit_code])
456
- elsif uses_rpm?
457
- msg.parse_result(exec("yum erase -y #{package}")[:exit_code])
458
- elsif uses_pacman?
459
- msg.parse_result(exec("echo y |pacman -R #{package}")[:exit_code])
460
- elsif uses_opkg?
461
- msg.parse_result(exec("opkg remove #{package}")[:exit_code])
462
- else
463
- msg.failed
464
- end
465
- end
466
-
467
- def update_repos options = {}
468
- options = default_options.merge options
469
-
470
- msg = messages.add('updating system repositories', options)
471
-
472
- if uses_apt?
473
- ret = exec 'aptitude update', options
474
- elsif uses_emerge?
475
- ret = exec 'emerge --sync', options
476
- elsif uses_rpm?
477
- ret = exec 'yum check-update', options
478
-
479
- # yum returns != 0 if packages that need to be updated are found
480
- # we don't want that this is producing an error
481
- ret[:exit_code] = 0 if ret[:exit_code] == 100
482
- elsif uses_pacman?
483
- ret = exec 'pacman -Sy', options
484
- elsif uses_opkg?
485
- ret = exec 'opkg update', options
486
- else
487
- return msg.failed
488
- end
489
-
490
- unless options[:live]
491
- msg.parse_result(ret[:exit_code])
492
- end
493
-
494
- ret[:exit_code]
495
- end
496
-
497
- def system_update options = {}
498
- options = default_options.merge(:live => true).merge(options)
499
-
500
- update_repos
501
-
502
- msg = messages.add('installing system updates', options)
503
-
504
- if uses_apt?
505
- ret = exec 'DEBIAN_FRONTEND=noninteractive aptitude full-upgrade -y', options
506
- elsif uses_emerge?
507
- ret = exec 'emerge -uND @world', options
508
- elsif uses_rpm?
509
- ret = exec 'yum upgrade -y', options
510
- elsif uses_pacman?
511
- # pacman has no --yes option that i know of, so echoing y
512
- ret = exec 'echo y |pacman -Su', options
513
- elsif uses_opkg?
514
- # upgrading openwrt is very experimental, and should not used normally
515
- ret = exec 'opkg upgrade $(echo $(opkg list-upgradable |cut -d' ' -f1 |grep -v Multiple))', options
516
- else
517
- msg.failed('system not (yet) supported')
518
- return false
519
- end
520
-
521
- unless options[:live]
522
- msg.parse_result(ret[:exit_code])
523
- end
524
-
525
- ret[:exit_code]
526
- end
527
-
528
- # determining the system packet manager has to be done without facter
529
- # because it's used to find out whether facter is installed / install facter
530
- def uses_apt? options = {}
531
- options = default_options(:quiet => true).merge options
532
-
533
- return @uses_apt if defined? @uses_apt
534
- msg = messages.add('determining whether node uses apt', options)
535
- @uses_apt = msg.parse_result(exec('test -e /etc/debian_version')[:exit_code])
536
- end
537
-
538
- def uses_rpm? options = {}
539
- options = default_options(:quiet => true).merge options
540
-
541
- return @uses_rpm if defined? @uses_rpm
542
- msg = messages.add('determining whether node uses rpm', options)
543
- @uses_rpm = msg.parse_result(exec('test -e /etc/redhat-release')[:exit_code])
544
- end
545
-
546
- def uses_emerge? options = {}
547
- options = default_options(:quiet => true).merge options
548
-
549
- return @uses_emerge if defined? @uses_emerge
550
- msg = messages.add('determining whether node uses emerge', options)
551
- @uses_emerge = msg.parse_result(exec('test -e /etc/gentoo-release')[:exit_code])
552
- end
553
-
554
- def uses_pacman? options = {}
555
- options = default_options(:quiet => true).merge options
556
-
557
- return @uses_pacman if defined? @uses_pacman
558
- msg = messages.add('determining whether node uses pacman', options)
559
- @uses_pacman = msg.parse_result(exec('test -e /etc/arch-release')[:exit_code])
560
- end
561
-
562
- def uses_opkg? options = {}
563
- options = default_options(:quiet => true).merge options
564
-
565
- return @uses_opkg if defined? @uses_opkg
566
- msg = messages.add('determining whether node uses opkg', options)
567
- @uses_opkg = msg.parse_result(exec('test -e /etc/opkg.conf')[:exit_code])
568
- end
569
-
570
- def is_os? os_list, options = {}
571
- options = default_options(:quiet => true).merge options
572
-
573
- msg = messages.add("checking if this machine runs #{os_list.join(' or ')}", options)
574
- return msg.failed unless collect_facts options
575
-
576
- os_list.each do |os|
577
- if @node['operatingsystem'].downcase == os.downcase
578
- return msg.ok
579
- end
580
- end
581
-
582
- msg.failed
583
- false
584
- end
585
-
586
- def is_debian? options = {}
587
- options = default_options(:quiet => true).merge options
588
-
589
- return false unless uses_apt?
590
- is_os? ['debian'], options
591
- end
592
-
593
- def is_ubuntu? options = {}
594
- options = default_options(:quiet => true).merge options
595
-
596
- return false unless uses_apt?
597
- is_os? ['ubuntu'], options
598
- end
599
-
600
- def is_gentoo? options = {}
601
- options = default_options(:quiet => true).merge options
602
-
603
- return false unless uses_emerge?
604
- is_os? ['gentoo'], options
605
- end
606
-
607
- def is_centos? options = {}
608
- options = default_options(:quiet => true).merge options
609
-
610
- return false unless uses_rpm?
611
- is_os? ['centos'], options
612
- end
613
-
614
- def is_scientific? options = {}
615
- options = default_options(:quiet => true).merge options
616
-
617
- return false unless uses_rpm?
618
- is_os? ['scientific'], options
619
- end
620
-
621
- def is_fedora? options = {}
622
- options = default_options(:quiet => true).merge options
623
-
624
- return false unless uses_rpm?
625
- is_os? ['fedora'], options
626
- end
627
-
628
- def is_arch? options = {}
629
- options = default_options(:quiet => true).merge options
630
-
631
- return false unless uses_pacman?
632
- is_os? ['archlinux'], options
633
- end
634
-
635
- def is_executable? file, options = {}
636
- options = default_options.merge options
637
-
638
- msg = messages.add("checking if file #{file} exists and is executeable", options)
639
- msg.parse_result(exec("test -x $(which #{file})")[:exit_code])
640
- end
641
-
642
- def file_exists? file, options = {}
643
- options = default_options.merge options
644
-
645
- msg = messages.add("checking if file #{file} exists", options)
646
-
647
- # don't treat directories as files
648
- return msg.failed if dir_exists?(file, :quiet => true)
649
-
650
- msg.parse_result(exec("test -e #{file}")[:exit_code])
651
- end
652
-
653
- def dir_exists? dir, options = {}
654
- options = default_options.merge options
655
-
656
- msg = messages.add("checking if directory #{dir} exists", options)
657
- msg.parse_result(exec("test -d #{dir}")[:exit_code])
658
- end
659
-
660
- def autostart_service service, options = {}
661
- options = default_options.merge options
662
-
663
- msg = messages.add("autostart #{service} on boot", options)
664
-
665
- if uses_rpm?
666
- if file_exists? '/bin/systemctl', :quiet => true
667
- msg.parse_result(exec("systemctl enable #{service}.service")[:exit_code])
668
- else
669
- msg.parse_result(exec("chkconfig #{service} on")[:exit_code])
670
- end
671
-
672
- elsif uses_apt?
673
- msg.parse_result(exec("update-rc.d #{service} defaults")[:exit_code])
674
-
675
- elsif uses_emerge?
676
- msg.parse_result(exec("rc-update add #{service} default")[:exit_code])
677
-
678
- # archlinux needs his autostart daemons in /etc/rc.conf, in the DAEMONS line
679
- #elsif uses_pacman?
680
-
681
- else
682
- msg.failed
683
- end
684
- end
685
-
686
- # invoke 'command' on the service (e.g. @node.service 'postgresql', 'restart')
687
- def service service, command, options = {}
688
- options = default_options.merge options
689
-
690
- return messages.add("service: '#{service}' unknown", options).failed unless service.is_a? String
691
-
692
- # try systemd, then upstart, then sysvconfig, then rc.d, then initscript
693
- if file_exists? '/bin/systemctl', :quiet => true
694
- msg = messages.add("#{command}ing #{service} (via systemd)", options)
695
- ret = exec("systemctl #{command} #{service}.service")
696
-
697
- elsif file_exists? "/etc/init/#{service}", :quiet => true
698
- msg = messages.add("#{command}ing #{service} (via upstart)", options)
699
- ret = exec("#{command} #{service}")
700
-
701
- elsif file_exists? '/sbin/service', :quiet => true or file_exists? '/usr/sbin/service', :quiet => true
702
- msg = messages.add("#{command}ing #{service} (via sysvconfig)", options)
703
- ret = exec("service #{service} #{command}")
704
-
705
- elsif file_exists? '/usr/sbin/rc.d', :quiet => true
706
- msg = messages.add("#{command}ing #{service} (via rc.d)", options)
707
- ret = exec("rc.d #{command} #{service}")
708
-
709
- else
710
- msg = messages.add("#{command}ing #{service} (via initscript)", options)
711
- ret = exec("/etc/init.d/#{service} #{command}")
712
- end
713
-
714
- msg.parse_result(ret[:exit_code])
715
- ret
716
- end
717
-
718
- def restart_service service, options = {}
719
- options = default_options.merge options
720
-
721
- service service, 'restart', options
722
- end
723
-
724
- def reload_service service, options = {}
725
- options = default_options.merge options
726
-
727
- service service, 'reload', options
728
- end
729
-
730
- def print_service_status(service, options = {})
731
- options = default_options.merge(:indent => 0).merge(options)
732
- ret = service(service, 'status', options)
733
- messages.add('', options).print_output(ret)
734
- ret
735
- end
736
-
737
- # check whether a user exists on this node
738
- def user_exists? user, options = {}
739
- options = default_options.merge options
740
-
741
- msg = messages.add("checking if user #{user} exists", options)
742
- msg.parse_result(exec("id #{user}")[:exit_code])
743
- end
744
-
745
- # manages users (create, modify)
746
- def manage_user(user, options = {})
747
- options = default_options.merge(options)
748
- options = { 'home' => nil, 'shell' => nil, 'uid' => nil, 'remove' => false,
749
- 'gid' => nil, 'groups' => nil, 'system' => false }.merge(options)
750
-
751
- # delete user from system
752
- if options['remove']
753
- if user_exists?(user, :quiet => true)
754
- msg = messages.add("deleting user #{user} from system", { :indent => options[:indent] }.merge(options))
755
- return msg.parse_result(exec("userdel --remove #{user}")[:exit_code])
756
- end
757
-
758
- return messages.add("user #{user} not present in system", options).ok
759
- end
760
-
761
- if user_exists?(user, :quiet => true)
762
- args = ""
763
- args << " --move-home --home #{options['home']}" if options['home']
764
- args << " --shell #{options['shell']}" if options['shell']
765
- args << " --uid #{options['uid']}" if options['uid']
766
- args << " --gid #{options['gid']}" if options['gid']
767
- args << " --append --groups #{Array(options['groups']).join(',')}" if options['groups']
768
-
769
- if args.empty?
770
- ret = messages.add("user #{user} already set up correctly", options).ok
771
- else
772
- msg = messages.add("modifying user #{user}", { :indent => options[:indent] }.merge(options))
773
- ret = msg.parse_result(exec("usermod #{args} #{user}")[:exit_code])
774
- end
775
-
776
- else
777
- args = ""
778
- args = "--create-home" unless options['system']
779
- args << " --system" if options['system']
780
- args << " --home #{options['home']}" if options['home'] and not options['system']
781
- args << " --shell #{options['shell']}" if options['shell']
782
- args << " --uid #{options['uid']}" if options['uid']
783
- args << " --gid #{options['gid']}" if options['gid']
784
- args << " --groups #{Array(options['groups']).join(',')}" if options['groups']
785
-
786
- msg = messages.add("creating user #{user}", { :indent => options[:indent] }.merge(options))
787
- ret = msg.parse_result(exec("useradd #{user} #{args}")[:exit_code])
788
- end
789
-
790
- # set selinux permissions
791
- chcon({ 'type' => 'user_home_dir_t' }, get_home(user), options)
792
- return ret
793
- end
794
-
795
- # returns the home directory of this user
796
- def get_home(user, options = {})
797
- options = default_options(:quiet => true).merge(options)
798
-
799
- msg = messages.add("getting home directory of #{user}", options)
800
- ret = exec("getent passwd |cut -d':' -f1,6 |grep '^#{user}' |head -n1 |cut -d: -f2")
801
- if msg.parse_result(ret[:exit_code]) and not ret[:stdout].chomp.empty?
802
- return ret[:stdout].chomp
803
- else
804
- return false
805
- end
806
- end
807
-
808
- # returns shell of this user
809
- def get_shell(user, options = {})
810
- options = default_options(:quiet => true).merge(options)
811
-
812
- msg = messages.add("getting shell of #{user}", options)
813
- ret = exec("getent passwd |cut -d':' -f1,7 |grep '^#{user}' |head -n1 |cut -d: -f2")
814
- if msg.parse_result(ret[:exit_code])
815
- return ret[:stdout].chomp
816
- else
817
- return false
818
- end
819
- end
820
-
821
- # returns primary group id of this user
822
- def get_gid(user, options = {})
823
- options = default_options(:quiet => true).merge(options)
824
-
825
- msg = messages.add("getting primary gid of #{user}", options)
826
- ret = exec("id -g #{user}")
827
- if msg.parse_result(ret[:exit_code])
828
- return ret[:stdout].chomp
829
- else
830
- return false
831
- end
832
- end
833
-
834
- # collect additional system facts using puppets facter
835
- def collect_facts options = {}
836
- options = default_options.merge options
837
-
838
- # if facts already have been collected, just return
839
- return true if @node['operatingsystem']
840
-
841
- # check if lsb-release (on apt systems) and facter are installed
842
- # and install them if not
843
- if uses_apt? and not package_installed? 'lsb-release', :quiet => true
844
- install_package 'lsb-release', :quiet => false
845
- end
846
-
847
- unless package_installed? 'facter', :quiet => true
848
- return false unless install_package 'facter', :quiet => false
849
- end
850
-
851
- msg = messages.add("collecting additional system facts (using facter)", options)
852
-
853
- # run facter with -y for yaml output, and merge results into @node
854
- ret = exec 'facter -y'
855
- @node = YAML.load(ret[:stdout]).merge(@node)
856
-
857
- msg.parse_result(ret[:exit_code])
858
- end
859
-
860
- # if file is a regular file, copy it using scp
861
- # if it's an file.erb exists, render template and push to server
862
- def deploy_file file, destination, options = {}
863
- options = default_options(:binding => binding).merge options
864
-
865
- if File.exists? file
866
- scp file, destination, options
867
-
868
- elsif File.exists? "#{file}.erb"
869
- template = ERB.new( File.read("#{file}.erb"), nil, '%<>')
870
- write destination, template.result(options[:binding]), options
871
-
872
- else
873
- messages.add("'#{file}' was not found.", options).failed
874
- end
875
- end
876
-
877
- # create a temporary file
878
- def mktemp(options = { :type => 'file' })
879
- if options[:type] == 'file'
880
- ret = exec('mktemp --tmpdir dust.XXXXXXXXXX')
881
- elsif options[:type] == 'directory'
882
- ret = exec('mktemp -d --tmpdir dust.XXXXXXXXXX')
883
- else
884
- return messages.add("mktemp: unknown type '#{options[:type]}'").failed
885
- end
886
-
887
- return false if ret[:exit_code] != 0
888
- ret[:stdout].chomp
889
- end
890
-
891
22
 
892
23
  private
893
24
 
@@ -905,6 +36,5 @@ module Dust
905
36
  super
906
37
  end
907
38
  end
908
-
909
39
  end
910
40
  end