cloud-mu 3.1.2 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (201) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +15 -3
  3. data/ansible/roles/mu-windows/README.md +33 -0
  4. data/ansible/roles/mu-windows/defaults/main.yml +2 -0
  5. data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
  6. data/ansible/roles/mu-windows/files/config.xml +76 -0
  7. data/ansible/roles/mu-windows/handlers/main.yml +2 -0
  8. data/ansible/roles/mu-windows/meta/main.yml +53 -0
  9. data/ansible/roles/mu-windows/tasks/main.yml +36 -0
  10. data/ansible/roles/mu-windows/tests/inventory +2 -0
  11. data/ansible/roles/mu-windows/tests/test.yml +5 -0
  12. data/ansible/roles/mu-windows/vars/main.yml +2 -0
  13. data/bin/mu-adopt +10 -13
  14. data/bin/mu-azure-tests +57 -0
  15. data/bin/mu-cleanup +2 -4
  16. data/bin/mu-configure +52 -0
  17. data/bin/mu-deploy +3 -3
  18. data/bin/mu-findstray-tests +25 -0
  19. data/bin/mu-gen-docs +2 -4
  20. data/bin/mu-load-config.rb +2 -3
  21. data/bin/mu-node-manage +15 -16
  22. data/bin/mu-run-tests +135 -37
  23. data/cloud-mu.gemspec +22 -20
  24. data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
  25. data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
  26. data/cookbooks/mu-tools/libraries/helper.rb +3 -2
  27. data/cookbooks/mu-tools/libraries/monkey.rb +35 -0
  28. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  29. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  30. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  31. data/cookbooks/mu-tools/recipes/google_api.rb +2 -2
  32. data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
  33. data/cookbooks/mu-tools/recipes/windows-client.rb +163 -164
  34. data/cookbooks/mu-tools/resources/disk.rb +1 -1
  35. data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
  36. data/extras/clean-stock-amis +25 -19
  37. data/extras/generate-stock-images +1 -0
  38. data/extras/image-generators/AWS/win2k12.yaml +18 -13
  39. data/extras/image-generators/AWS/win2k16.yaml +18 -13
  40. data/extras/image-generators/AWS/win2k19.yaml +21 -0
  41. data/extras/image-generators/Google/centos6.yaml +1 -0
  42. data/extras/image-generators/Google/centos7.yaml +1 -1
  43. data/modules/mommacat.ru +6 -16
  44. data/modules/mu.rb +165 -111
  45. data/modules/mu/adoption.rb +401 -68
  46. data/modules/mu/cleanup.rb +199 -306
  47. data/modules/mu/cloud.rb +100 -1632
  48. data/modules/mu/cloud/database.rb +49 -0
  49. data/modules/mu/cloud/dnszone.rb +46 -0
  50. data/modules/mu/cloud/machine_images.rb +212 -0
  51. data/modules/mu/cloud/providers.rb +81 -0
  52. data/modules/mu/cloud/resource_base.rb +920 -0
  53. data/modules/mu/cloud/server.rb +40 -0
  54. data/modules/mu/cloud/server_pool.rb +1 -0
  55. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  56. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  57. data/modules/mu/cloud/wrappers.rb +165 -0
  58. data/modules/mu/config.rb +171 -1767
  59. data/modules/mu/config/alarm.rb +2 -6
  60. data/modules/mu/config/bucket.rb +4 -4
  61. data/modules/mu/config/cache_cluster.rb +1 -1
  62. data/modules/mu/config/collection.rb +4 -4
  63. data/modules/mu/config/container_cluster.rb +9 -4
  64. data/modules/mu/config/database.rb +83 -104
  65. data/modules/mu/config/database.yml +1 -2
  66. data/modules/mu/config/dnszone.rb +6 -6
  67. data/modules/mu/config/doc_helpers.rb +516 -0
  68. data/modules/mu/config/endpoint.rb +4 -4
  69. data/modules/mu/config/firewall_rule.rb +103 -4
  70. data/modules/mu/config/folder.rb +4 -4
  71. data/modules/mu/config/function.rb +3 -3
  72. data/modules/mu/config/group.rb +4 -4
  73. data/modules/mu/config/habitat.rb +4 -4
  74. data/modules/mu/config/loadbalancer.rb +60 -14
  75. data/modules/mu/config/log.rb +4 -4
  76. data/modules/mu/config/msg_queue.rb +4 -4
  77. data/modules/mu/config/nosqldb.rb +4 -4
  78. data/modules/mu/config/notifier.rb +3 -3
  79. data/modules/mu/config/ref.rb +365 -0
  80. data/modules/mu/config/role.rb +4 -4
  81. data/modules/mu/config/schema_helpers.rb +509 -0
  82. data/modules/mu/config/search_domain.rb +4 -4
  83. data/modules/mu/config/server.rb +97 -70
  84. data/modules/mu/config/server.yml +1 -0
  85. data/modules/mu/config/server_pool.rb +5 -9
  86. data/modules/mu/config/storage_pool.rb +1 -1
  87. data/modules/mu/config/tail.rb +200 -0
  88. data/modules/mu/config/user.rb +4 -4
  89. data/modules/mu/config/vpc.rb +70 -27
  90. data/modules/mu/config/vpc.yml +0 -1
  91. data/modules/mu/defaults/AWS.yaml +83 -60
  92. data/modules/mu/defaults/Azure.yaml +1 -0
  93. data/modules/mu/defaults/Google.yaml +3 -2
  94. data/modules/mu/deploy.rb +30 -26
  95. data/modules/mu/groomer.rb +17 -2
  96. data/modules/mu/groomers/ansible.rb +188 -41
  97. data/modules/mu/groomers/chef.rb +116 -55
  98. data/modules/mu/logger.rb +127 -148
  99. data/modules/mu/master.rb +389 -2
  100. data/modules/mu/master/chef.rb +3 -4
  101. data/modules/mu/master/ldap.rb +3 -3
  102. data/modules/mu/master/ssl.rb +12 -3
  103. data/modules/mu/mommacat.rb +217 -2612
  104. data/modules/mu/mommacat/daemon.rb +397 -0
  105. data/modules/mu/mommacat/naming.rb +473 -0
  106. data/modules/mu/mommacat/search.rb +495 -0
  107. data/modules/mu/mommacat/storage.rb +722 -0
  108. data/modules/mu/{clouds → providers}/README.md +1 -1
  109. data/modules/mu/{clouds → providers}/aws.rb +271 -112
  110. data/modules/mu/{clouds → providers}/aws/alarm.rb +5 -3
  111. data/modules/mu/{clouds → providers}/aws/bucket.rb +26 -22
  112. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +33 -67
  113. data/modules/mu/{clouds → providers}/aws/collection.rb +24 -23
  114. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +681 -721
  115. data/modules/mu/providers/aws/database.rb +1744 -0
  116. data/modules/mu/{clouds → providers}/aws/dnszone.rb +64 -63
  117. data/modules/mu/{clouds → providers}/aws/endpoint.rb +22 -27
  118. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +214 -244
  119. data/modules/mu/{clouds → providers}/aws/folder.rb +7 -7
  120. data/modules/mu/{clouds → providers}/aws/function.rb +17 -22
  121. data/modules/mu/{clouds → providers}/aws/group.rb +23 -23
  122. data/modules/mu/{clouds → providers}/aws/habitat.rb +17 -14
  123. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +57 -48
  124. data/modules/mu/{clouds → providers}/aws/log.rb +15 -12
  125. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +17 -16
  126. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +18 -11
  127. data/modules/mu/{clouds → providers}/aws/notifier.rb +11 -6
  128. data/modules/mu/{clouds → providers}/aws/role.rb +112 -86
  129. data/modules/mu/{clouds → providers}/aws/search_domain.rb +39 -33
  130. data/modules/mu/{clouds → providers}/aws/server.rb +835 -1133
  131. data/modules/mu/{clouds → providers}/aws/server_pool.rb +56 -60
  132. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +24 -42
  133. data/modules/mu/{clouds → providers}/aws/user.rb +21 -22
  134. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  135. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +0 -0
  136. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +2 -1
  137. data/modules/mu/{clouds → providers}/aws/vpc.rb +523 -929
  138. data/modules/mu/providers/aws/vpc_subnet.rb +286 -0
  139. data/modules/mu/{clouds → providers}/azure.rb +29 -9
  140. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +3 -8
  141. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +18 -11
  142. data/modules/mu/{clouds → providers}/azure/habitat.rb +8 -6
  143. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +5 -5
  144. data/modules/mu/{clouds → providers}/azure/role.rb +8 -10
  145. data/modules/mu/{clouds → providers}/azure/server.rb +95 -48
  146. data/modules/mu/{clouds → providers}/azure/user.rb +6 -8
  147. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  148. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  149. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  150. data/modules/mu/{clouds → providers}/azure/vpc.rb +16 -21
  151. data/modules/mu/{clouds → providers}/cloudformation.rb +18 -7
  152. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  153. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  154. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  155. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  156. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  157. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  158. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  159. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  160. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  161. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  162. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +5 -7
  163. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  164. data/modules/mu/{clouds → providers}/google.rb +67 -30
  165. data/modules/mu/{clouds → providers}/google/bucket.rb +13 -15
  166. data/modules/mu/{clouds → providers}/google/container_cluster.rb +84 -77
  167. data/modules/mu/{clouds → providers}/google/database.rb +10 -20
  168. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +15 -14
  169. data/modules/mu/{clouds → providers}/google/folder.rb +20 -17
  170. data/modules/mu/{clouds → providers}/google/function.rb +139 -167
  171. data/modules/mu/{clouds → providers}/google/group.rb +29 -34
  172. data/modules/mu/{clouds → providers}/google/habitat.rb +21 -22
  173. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +18 -20
  174. data/modules/mu/{clouds → providers}/google/role.rb +92 -58
  175. data/modules/mu/{clouds → providers}/google/server.rb +242 -155
  176. data/modules/mu/{clouds → providers}/google/server_pool.rb +25 -44
  177. data/modules/mu/{clouds → providers}/google/user.rb +95 -31
  178. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  179. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  180. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  181. data/modules/mu/{clouds → providers}/google/vpc.rb +103 -79
  182. data/modules/tests/bucket.yml +4 -0
  183. data/modules/tests/centos6.yaml +11 -0
  184. data/modules/tests/centos7.yaml +11 -0
  185. data/modules/tests/centos8.yaml +12 -0
  186. data/modules/tests/ecs.yaml +23 -0
  187. data/modules/tests/includes-and-params.yaml +2 -1
  188. data/modules/tests/rds.yaml +108 -0
  189. data/modules/tests/regrooms/aws-iam.yaml +201 -0
  190. data/modules/tests/regrooms/bucket.yml +19 -0
  191. data/modules/tests/regrooms/rds.yaml +123 -0
  192. data/modules/tests/server-with-scrub-muisms.yaml +1 -0
  193. data/modules/tests/super_simple_bok.yml +1 -3
  194. data/modules/tests/win2k12.yaml +17 -5
  195. data/modules/tests/win2k16.yaml +25 -0
  196. data/modules/tests/win2k19.yaml +25 -0
  197. data/requirements.txt +1 -0
  198. data/spec/mu/clouds/azure_spec.rb +2 -2
  199. metadata +232 -154
  200. data/extras/image-generators/AWS/windows.yaml +0 -18
  201. data/modules/mu/clouds/aws/database.rb +0 -1985
@@ -35,6 +35,12 @@ module MU
35
35
  end
36
36
  }
37
37
 
38
+ # Are the Chef libraries present and accounted for?
39
+ def self.available?(windows = false)
40
+ loadChefLib
41
+ @chefloaded
42
+ end
43
+
38
44
  @chefloaded = false
39
45
  @chefload_semaphore = Mutex.new
40
46
  # Autoload is too brain-damaged to get Chef's subclasses/submodules, so
@@ -184,13 +190,13 @@ module MU
184
190
  if !item.nil?
185
191
  begin
186
192
  loaded = ::ChefVault::Item.load(vault, item)
187
- rescue ::ChefVault::Exceptions::KeysNotFound => e
193
+ rescue ::ChefVault::Exceptions::KeysNotFound
188
194
  raise MuNoSuchSecret, "Can't load the Chef Vault #{vault}:#{item}. Does it exist? Chef user: #{MU.chef_user}"
189
195
  end
190
196
  else
191
197
  # If we didn't ask for a particular item, list what we have.
192
198
  begin
193
- loaded = ::Chef::DataBag.load(vault).keys.select { |k, v| !k.match(/_keys$/) }
199
+ loaded = ::Chef::DataBag.load(vault).keys.select { |k| !k.match(/_keys$/) }
194
200
  rescue Net::HTTPServerException
195
201
  raise MuNoSuchSecret, "Failed to retrieve Vault #{vault}"
196
202
  end
@@ -258,7 +264,6 @@ module MU
258
264
  knifeAddToRunList(multiple: @config['run_list'])
259
265
  end
260
266
 
261
- pending_reboot_count = 0
262
267
  chef_node = ::Chef::Node.load(@server.mu_name)
263
268
  if !@config['application_attributes'].nil?
264
269
  MU.log "Setting node:#{@server.mu_name} application_attributes", MU::DEBUG, details: @config['application_attributes']
@@ -300,7 +305,7 @@ module MU
300
305
  cmd = "#{upgrade_cmd} chef-client --color || echo #{error_signal}"
301
306
  end
302
307
  Timeout::timeout(timeout) {
303
- retval = ssh.exec!(cmd) { |ch, stream, data|
308
+ ssh.exec!(cmd) { |_ch, _stream, data|
304
309
  extra_logfile = if Dir.exist?(@server.deploy.deploy_dir)
305
310
  File.open(@server.deploy.deploy_dir+"/log", "a")
306
311
  end
@@ -330,7 +335,7 @@ module MU
330
335
  }
331
336
  else
332
337
  MU.log "Invoking Chef over WinRM on #{@server.mu_name}: #{purpose}"
333
- winrm = @server.getWinRMSession(haveBootstrapped? ? 1 : max_retries)
338
+ winrm = @server.getWinRMSession(haveBootstrapped? ? 2 : max_retries)
334
339
  if @server.windows? and @server.windowsRebootPending?(winrm)
335
340
  # Windows frequently gets stuck here
336
341
  if retries > 5
@@ -363,7 +368,7 @@ module MU
363
368
  }
364
369
 
365
370
  if resp.exitcode == 1 and output_lines.join("\n").match(/Chef Client finished/)
366
- MU.log "resp.exit code 1"
371
+ MU.log output_lines.last
367
372
  elsif resp.exitcode != 0
368
373
  raise MU::Cloud::BootstrapTempFail if resp.exitcode == 35 or output_lines.join("\n").match(/REBOOT_SCHEDULED| WARN: Reboot requested:|Rebooting server at a recipe's request|Chef::Exceptions::Reboot/)
369
374
  raise MU::Groomer::RunError, output_lines.slice(output_lines.length-50, output_lines.length).join("")
@@ -380,7 +385,7 @@ module MU
380
385
  sleep 30
381
386
  end
382
387
  retry
383
- rescue RuntimeError, SystemCallError, Timeout::Error, SocketError, Errno::ECONNRESET, IOError, Net::SSH::Exception, MU::Groomer::RunError, WinRM::WinRMError, MU::MuError => e
388
+ rescue SystemExit, Timeout::Error, MU::Cloud::BootstrapTempFail, Net::HTTPServerException, HTTPClient::ConnectTimeoutError, WinRM::WinRMError, Net::SSH::AuthenticationFailed, Net::SSH::Disconnect, Net::SSH::ConnectionTimeout, Net::SSH::Proxy::ConnectError, Net::SSH::Exception, Errno::ECONNRESET, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Errno::EPIPE, SocketError, IOError => e
384
389
  begin
385
390
  ssh.close if !ssh.nil?
386
391
  rescue Net::SSH::Exception, IOError => e
@@ -390,6 +395,8 @@ module MU
390
395
  MU.log "ssh session to #{@server.mu_name} was closed unexpectedly, waiting before trying again", MU::NOTICE
391
396
  end
392
397
  sleep 10
398
+ rescue StandardError => e
399
+ MU.log "Error I don't recognize closing ssh tunnel", MU::WARN, details: e.inspect
393
400
  end
394
401
  if e.instance_of?(MU::Groomer::RunError) and retries == 0 and max_retries > 1 and purpose != "Base Windows configuration"
395
402
  MU.log "Got a run error, will attempt to install/update Chef Client on next attempt", MU::NOTICE
@@ -404,7 +411,7 @@ module MU
404
411
  begin
405
412
  preClean(true) # drop any Chef install that's not ours
406
413
  @server.reboot # try gently rebooting the thing
407
- rescue Exception => e # it's ok to fail here (and to ignore failure)
414
+ rescue StandardError => e # it's ok to fail here (and to ignore failure)
408
415
  MU.log "preclean err #{e.inspect}", MU::ERR
409
416
  end
410
417
  reboot_first_fail = false
@@ -414,9 +421,9 @@ module MU
414
421
  if retries < max_retries
415
422
  retries += 1
416
423
  MU.log "#{@server.mu_name}: Chef run '#{purpose}' failed after #{Time.new - runstart} seconds, retrying (#{retries}/#{max_retries})", MU::WARN, details: e.message.dup
417
- if purpose != "Base Windows configuration"
418
- windows_try_ssh = !windows_try_ssh
419
- end
424
+ # if purpose != "Base Windows configuration"
425
+ # windows_try_ssh = !windows_try_ssh
426
+ # end
420
427
  if e.is_a?(WinRM::WinRMError)
421
428
  if @server.windows? and retries >= 3 and retries % 3 == 0
422
429
  # Mix in a hard reboot if WinRM isn't answering
@@ -429,7 +436,7 @@ module MU
429
436
  @server.deploy.sendAdminSlack("Chef run '#{purpose}' failed on `#{@server.mu_name}` :crying_cat_face:", msg: e.message)
430
437
  raise MU::Groomer::RunError, "#{@server.mu_name}: Chef run '#{purpose}' failed #{max_retries} times, last error was: #{e.message}"
431
438
  end
432
- rescue Exception => e
439
+ rescue StandardError => e
433
440
  @server.deploy.sendAdminSlack("Chef run '#{purpose}' failed on `#{@server.mu_name}` :crying_cat_face:", msg: e.inspect)
434
441
  raise MU::Groomer::RunError, "Caught unexpected #{e.inspect} on #{@server.mu_name} in @groomer.run at #{e.backtrace[0]}"
435
442
 
@@ -443,8 +450,8 @@ module MU
443
450
  def splunkVaultInit
444
451
  self.class.loadChefLib
445
452
  begin
446
- loaded = ::ChefVault::Item.load("splunk", "admin_user")
447
- rescue ::ChefVault::Exceptions::KeysNotFound => e
453
+ ::ChefVault::Item.load("splunk", "admin_user")
454
+ rescue ::ChefVault::Exceptions::KeysNotFound
448
455
  pw = Password.pronounceable(12..14)
449
456
  creds = {
450
457
  "username" => "admin",
@@ -550,7 +557,7 @@ module MU
550
557
  winrm = @server.getWinRMSession(1, 30, winrm_retries: 2)
551
558
  pp winrm.run(cmd)
552
559
  return
553
- rescue Net::SSH::Disconnect, SystemCallError, Timeout::Error, Errno::ECONNRESET, Errno::EHOSTUNREACH, Net::SSH::Proxy::ConnectError, SocketError, Net::SSH::Disconnect, Net::SSH::AuthenticationFailed, IOError, Net::HTTPServerException, SystemExit, Errno::ECONNREFUSED, Errno::EPIPE, WinRM::WinRMError, HTTPClient::ConnectTimeoutError, RuntimeError, MU::Cloud::BootstrapTempFail, MU::MuError => e
560
+ rescue SystemExit, Timeout::Error, MU::Cloud::BootstrapTempFail, MU::MuError, Net::HTTPServerException, HTTPClient::ConnectTimeoutError, WinRM::WinRMError, Net::SSH::AuthenticationFailed, Net::SSH::Disconnect, Net::SSH::ConnectionTimeout, Net::SSH::Proxy::ConnectError, Net::SSH::Exception, Errno::ECONNRESET, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Errno::EPIPE, SocketError, IOError
554
561
  MU.log "WinRM failure attempting Chef upgrade on #{@server.mu_name}, will fall back to ssh", MU::WARN
555
562
  cmd = %Q{powershell.exe -inputformat none -noprofile "#{cmd}"}
556
563
  end
@@ -558,7 +565,7 @@ module MU
558
565
 
559
566
  MU.log "Attempting Chef upgrade via ssh on #{@server.mu_name}", MU::NOTICE, details: cmd
560
567
  ssh = @server.getSSHSession(1)
561
- retval = ssh.exec!(cmd) { |ch, stream, data|
568
+ ssh.exec!(cmd) { |_ch, _stream, data|
562
569
  puts data
563
570
  }
564
571
  end
@@ -580,7 +587,7 @@ module MU
580
587
  @config['cleaned_chef'] = true
581
588
  end
582
589
 
583
- nat_ssh_key, nat_ssh_user, nat_ssh_host, canonical_addr, ssh_user, ssh_key_name = @server.getSSHConfig
590
+ _nat_ssh_key, _nat_ssh_user, _nat_ssh_host, canonical_addr, ssh_user, ssh_key_name = @server.getSSHConfig
584
591
 
585
592
  MU.log "Bootstrapping #{@server.mu_name} (#{canonical_addr}) with knife"
586
593
 
@@ -593,11 +600,12 @@ module MU
593
600
  json_attribs['skipinitialupdates'] = @config['skipinitialupdates']
594
601
  end
595
602
 
596
- if !@config['vault_access'].nil?
597
- vault_access = @config['vault_access']
598
- else
599
- vault_access = []
600
- end
603
+ # XXX this seems to break Knife Bootstrap
604
+ # vault_access = if !@config['vault_access'].nil?
605
+ # @config['vault_access']
606
+ # else
607
+ # []
608
+ # end
601
609
 
602
610
  @server.windows? ? max_retries = 25 : max_retries = 10
603
611
  @server.windows? ? timeout = 1800 : timeout = 300
@@ -617,13 +625,20 @@ module MU
617
625
  kb.name_args = [@server.mu_name]
618
626
  kb.config[:manual] = true
619
627
  kb.config[:winrm_transport] = :ssl
620
- kb.config[:host] = @server.mu_name
621
628
  kb.config[:winrm_port] = 5986
622
629
  kb.config[:session_timeout] = timeout
623
630
  kb.config[:operation_timeout] = timeout
624
- kb.config[:winrm_authentication_protocol] = :cert
625
- kb.config[:winrm_client_cert] = "#{MU.mySSLDir}/#{@server.mu_name}-winrm.crt"
626
- kb.config[:winrm_client_key] = "#{MU.mySSLDir}/#{@server.mu_name}-winrm.key"
631
+ if retries % 2 == 0
632
+ kb.config[:host] = canonical_addr
633
+ kb.config[:winrm_authentication_protocol] = :basic
634
+ kb.config[:winrm_user] = @server.config['windows_admin_username']
635
+ kb.config[:winrm_password] = @server.getWindowsAdminPassword
636
+ else
637
+ kb.config[:host] = @server.mu_name
638
+ kb.config[:winrm_authentication_protocol] = :cert
639
+ kb.config[:winrm_client_cert] = "#{MU.mySSLDir}/#{@server.mu_name}-winrm.crt"
640
+ kb.config[:winrm_client_key] = "#{MU.mySSLDir}/#{@server.mu_name}-winrm.key"
641
+ end
627
642
  # kb.config[:ca_trust_file] = "#{MU.mySSLDir}/Mu_CA.pem"
628
643
  # XXX ca_trust_file doesn't work for some reason, so we have to set the below for now
629
644
  kb.config[:winrm_ssl_verify_mode] = :verify_none
@@ -658,7 +673,7 @@ module MU
658
673
  }
659
674
  # throws Net::HTTPServerException if we haven't really bootstrapped
660
675
  ::Chef::Node.load(@server.mu_name)
661
- rescue Net::SSH::Disconnect, SystemCallError, Timeout::Error, Errno::ECONNRESET, Errno::EHOSTUNREACH, Net::SSH::Proxy::ConnectError, SocketError, Net::SSH::Disconnect, Net::SSH::AuthenticationFailed, IOError, Net::HTTPServerException, SystemExit, Errno::ECONNREFUSED, Errno::EPIPE, WinRM::WinRMError, HTTPClient::ConnectTimeoutError, RuntimeError, MU::Cloud::BootstrapTempFail, Net::SSH::Exception, Net::SSH::ConnectionTimeout => e
676
+ rescue SystemExit, Timeout::Error, MU::Cloud::BootstrapTempFail, Net::HTTPServerException, HTTPClient::ConnectTimeoutError, WinRM::WinRMError, Net::SSH::AuthenticationFailed, Net::SSH::Disconnect, Net::SSH::ConnectionTimeout, Net::SSH::Proxy::ConnectError, Net::SSH::Exception, Errno::ECONNRESET, Errno::EHOSTUNREACH, Errno::ECONNREFUSED, Errno::EPIPE, SocketError, IOError => e
662
677
  if retries < max_retries
663
678
  retries += 1
664
679
  # Bad Chef installs are possible culprits of bootstrap failures, so
@@ -671,9 +686,9 @@ module MU
671
686
  !@config['forced_preclean']
672
687
  begin
673
688
  preClean(false) # it's ok for this to fail
674
- rescue Exception => e
689
+ rescue StandardError => e
675
690
  end
676
- MU::Groomer::Chef.cleanup(@server.mu_name, nodeonly: true)
691
+ MU::Groomer::Chef.purge(@server.mu_name, nodeonly: true)
677
692
  @config['forced_preclean'] = true
678
693
  @server.reboot if @server.windows? # *sigh*
679
694
  end
@@ -683,7 +698,7 @@ module MU
683
698
  else
684
699
  raise MuError, "#{@server.mu_name}: Knife Bootstrap failed too many times with #{e.inspect}"
685
700
  end
686
- rescue Exception => e
701
+ rescue StandardError => e
687
702
  MU.log e.inspect, MU::ERR, details: e.backtrace
688
703
  sleep 10*retries
689
704
  retry
@@ -700,7 +715,7 @@ retry
700
715
  end
701
716
  }
702
717
  knifeAddToRunList("role[mu-node]")
703
- knifeAddToRunList("mu-tools::selinux")
718
+ knifeAddToRunList("recipe[mu-tools::selinux]")
704
719
 
705
720
  grantSecretAccess(@server.mu_name, "windows_credentials") if @server.windows?
706
721
  grantSecretAccess(@server.mu_name, "ssl_cert")
@@ -748,7 +763,7 @@ retry
748
763
  return
749
764
  end
750
765
 
751
- @server.describe(update_cache: true) # Make sure we're fresh
766
+ @server.describe
752
767
  saveChefMetadata
753
768
  begin
754
769
  chef_node = ::Chef::Node.load(@server.mu_name)
@@ -785,17 +800,57 @@ retry
785
800
  chef_node.save
786
801
  end
787
802
  return chef_node['deployment']
788
- rescue Net::HTTPServerException => e
803
+ rescue Net::HTTPServerException
789
804
  MU.log "Attempted to save deployment to Chef node #{@server.mu_name} before it was bootstrapped.", MU::DEBUG
790
805
  end
791
806
  end
792
807
 
808
+ # Purge Chef resources matching a particular deploy
809
+ # @param deploy_id [String]
810
+ # @param noop [Boolean]
811
+ def self.cleanup(deploy_id, noop = false)
812
+ return nil if deploy_id.nil? or deploy_id.empty?
813
+ begin
814
+ if File.exist?(Etc.getpwuid(Process.uid).dir+"/.chef/knife.rb")
815
+ ::Chef::Config.from_file(Etc.getpwuid(Process.uid).dir+"/.chef/knife.rb")
816
+ end
817
+ deadnodes = []
818
+ ::Chef::Config[:environment] ||= MU.environment
819
+ q = ::Chef::Search::Query.new
820
+ begin
821
+ q.search("node", "tags_MU-ID:#{deploy_id}").each { |item|
822
+ next if item.is_a?(Integer)
823
+ item.each { |node|
824
+ deadnodes << node.name
825
+ }
826
+ }
827
+ rescue Net::HTTPServerException
828
+ end
829
+
830
+ begin
831
+ q.search("node", "name:#{deploy_id}-*").each { |item|
832
+ next if item.is_a?(Integer)
833
+ item.each { |node|
834
+ deadnodes << node.name
835
+ }
836
+ }
837
+ rescue Net::HTTPServerException
838
+ end
839
+ MU.log "Missed some Chef resources in node cleanup, purging now", MU::NOTICE if deadnodes.size > 0
840
+ deadnodes.uniq.each { |node|
841
+ MU::Groomer::Chef.purge(node, [], noop)
842
+ }
843
+ rescue LoadError
844
+ end
845
+
846
+ end
847
+
793
848
  # Expunge Chef resources associated with a node.
794
849
  # @param node [String]: The Mu name of the node in question.
795
850
  # @param vaults_to_clean [Array<Hash>]: Some vaults to expunge
796
851
  # @param noop [Boolean]: Skip actual deletion, just state what we'd do
797
852
  # @param nodeonly [Boolean]: Just delete the node and its keys, but leave other artifacts
798
- def self.cleanup(node, vaults_to_clean = [], noop = false, nodeonly: false)
853
+ def self.purge(node, vaults_to_clean = [], noop = false, nodeonly: false)
799
854
  loadChefLib
800
855
  MU.log "Deleting Chef resources associated with #{node}"
801
856
  if !nodeonly
@@ -804,7 +859,7 @@ retry
804
859
  MU.log "knife vault remove #{vault['vault']} #{vault['item']} --search name:#{node}", MU::NOTICE
805
860
  begin
806
861
  ::Chef::Knife.run(['vault', 'remove', vault['vault'], vault['item'], "--search", "name:#{node}"]) if !noop
807
- rescue Exception => e
862
+ rescue StandardError => e
808
863
  MU.log "Error removing vault access for #{node} from #{vault['vault']} #{vault['item']}", MU::ERR, details: e.inspect
809
864
  end
810
865
  MU::MommaCat.unlock("vault-#{vault['vault']}")
@@ -858,7 +913,7 @@ retry
858
913
  ::Chef::Knife.run(['vault', 'refresh', vault['vault'], vault['item']])
859
914
  end
860
915
  end
861
- rescue JSON::ParserError => e
916
+ rescue JSON::ParserError
862
917
  MU.log "Error parsing JSON from data bag #{vault['vault']} #{vault['item']}_keys, skipping vault client cleanse", MU::WARN
863
918
  end
864
919
  end
@@ -887,18 +942,34 @@ retry
887
942
  MU.log "Granting #{host} access to #{vault} #{item}"
888
943
  begin
889
944
  ::Chef::Knife.run(['vault', 'update', vault, item, "--search", "name:#{host}"])
890
- rescue Exception => e
945
+ rescue StandardError => e
891
946
  MU.log e.inspect, MU::ERR, details: caller
892
947
  end
893
948
  MU::MommaCat.unlock("vault-#{vault}", true)
894
949
  end
895
950
 
951
+ # Execute a +knife+ command, and return its exit status and output
952
+ # @param cmd [String]: The knife subcommand to run, such as +vault list+
953
+ # @param showoutput [String]: Print the results to stdout
954
+ # @return [Array<Integer,String>]
955
+ def self.knifeCmd(cmd, showoutput = false)
956
+ MU.log "knife #{cmd}", MU::NOTICE if showoutput
957
+ output = `#{MU::Groomer::Chef.knife} #{cmd}`
958
+ exitstatus = $?.exitstatus
959
+
960
+ if showoutput
961
+ puts output
962
+ puts "Exit status: #{exitstatus}"
963
+ end
964
+ return [exitstatus, output]
965
+ end
966
+
896
967
  private
897
968
 
898
969
  # Save common Mu attributes to this node's Chef node structure.
899
970
  def saveChefMetadata
900
971
  self.class.loadChefLib
901
- nat_ssh_key, nat_ssh_user, nat_ssh_host, canonical_addr, ssh_user, ssh_key_name = @server.getSSHConfig
972
+ @server.getSSHConfig # why though
902
973
  MU.log "Saving #{@server.mu_name} Chef artifacts"
903
974
 
904
975
  begin
@@ -998,7 +1069,7 @@ retry
998
1069
  deploy = MU::MommaCat.getLitter(MU.deploy_id, use_cache: false)
999
1070
  @config['dependencies'].each{ |dep|
1000
1071
  if dep['type'] == "database" && deploy.deployment.has_key?("databases") && deploy.deployment["databases"].has_key?(dep['name'])
1001
- deploy.deployment["databases"][dep['name']].each { |name, database|
1072
+ deploy.deployment["databases"][dep['name']].values.each { |database|
1002
1073
  grantSecretAccess(database['vault_name'], database['vault_item']) if database.has_key?("vault_name") && database.has_key?("vault_item")
1003
1074
  }
1004
1075
  end
@@ -1019,18 +1090,6 @@ retry
1019
1090
  @secrets_granted["#{vault}:#{item}"] = item
1020
1091
  end
1021
1092
 
1022
- def self.knifeCmd(cmd, showoutput = false)
1023
- MU.log "knife #{cmd}", MU::NOTICE if showoutput
1024
- output = `#{MU::Groomer::Chef.knife} #{cmd}`
1025
- exitstatus = $?.exitstatus
1026
-
1027
- if showoutput
1028
- puts output
1029
- puts "Exit status: #{exitstatus}"
1030
- end
1031
- return [exitstatus, output]
1032
- end
1033
-
1034
1093
  def knifeCmd(cmd, showoutput = false)
1035
1094
  self.class.knifeCmd(cmd, showoutput)
1036
1095
  end
@@ -1063,9 +1122,11 @@ retry
1063
1122
  if multiple.size == 0
1064
1123
  multiple = [rl_entry]
1065
1124
  end
1066
- multiple.each { |entry|
1125
+ multiple.map! { |entry|
1067
1126
  if !entry.match(/^role|recipe\[/)
1068
- entry = "#{type}[#{entry}]"
1127
+ "#{type}[#{entry}]"
1128
+ else
1129
+ entry
1069
1130
  end
1070
1131
  }
1071
1132
 
@@ -1110,8 +1171,8 @@ retry
1110
1171
  MU.log("Adding #{rl_string} to Chef run_list of #{@server.mu_name}")
1111
1172
  MU.log("Running #{query}", MU::DEBUG)
1112
1173
  output=%x{#{query}}
1113
- # XXX rescue Exception is bad style
1114
- rescue Exception => e
1174
+ # XXX rescue StandardError is bad style
1175
+ rescue StandardError => e
1115
1176
  raise MuError, "FAIL: #{MU::Groomer::Chef.knife} node run_list add #{@server.mu_name} \"#{rl_string}\": #{e.message} (output was #{output})"
1116
1177
  end
1117
1178
  end
@@ -33,6 +33,33 @@ module MU
33
33
  # Show DEBUG log entries and extra call stack and threading info
34
34
  LOUD = 2.freeze
35
35
 
36
+ # stash a hash map for color outputs
37
+ COLORMAP = {
38
+ MU::DEBUG => { :html => "orange", :ansi => :yellow },
39
+ MU::INFO => { :html => "green", :ansi => :green },
40
+ MU::NOTICE => { :html => "yellow", :ansi => :yellow },
41
+ MU::WARN => { :html => "orange", :ansi => :light_red },
42
+ MU::ERR => { :html => "red", :ansi => :red }
43
+ }.freeze
44
+
45
+ # minimum log verbosity at which we'll print various types of messages
46
+ PRINT_MSG_IF = {
47
+ MU::DEBUG => { :msg => LOUD, :details => LOUD },
48
+ MU::INFO => { :msg => NORMAL, :details => LOUD },
49
+ MU::NOTICE => { :msg => nil, :details => QUIET },
50
+ MU::WARN => { :msg => nil, :details => SILENT },
51
+ MU::ERR => { :msg => nil, :details => nil }
52
+ }.freeze
53
+
54
+ # Syslog equivalents of our log levels
55
+ SYSLOG_MAP = {
56
+ MU::DEBUG => Syslog::LOG_DEBUG,
57
+ MU::INFO => Syslog::LOG_NOTICE,
58
+ MU::NOTICE => Syslog::LOG_NOTICE,
59
+ MU::WARN => Syslog::LOG_WARNING,
60
+ MU::ERR => Syslog::LOG_ERR
61
+ }.freeze
62
+
36
63
  attr_accessor :verbosity
37
64
  @verbosity = MU::Logger::NORMAL
38
65
  @quiet = false
@@ -66,66 +93,38 @@ module MU
66
93
  def log(msg,
67
94
  level=INFO,
68
95
  details: nil,
69
- html: @html,
70
- verbosity: @verbosity,
71
- handle: @handle,
72
- color: @color,
96
+ html: nil,
97
+ verbosity: nil,
98
+ handle: nil,
99
+ color: nil,
73
100
  deploy: MU.mommacat
74
101
  )
75
102
  verbosity ||= @verbosity
76
- return if verbosity == MU::Logger::SILENT
77
- return if verbosity < MU::Logger::LOUD and level == DEBUG
78
- return if verbosity < MU::Logger::NORMAL and level == INFO
103
+ html ||= @html
104
+ handle ||= @handle
105
+ color ||= @color
79
106
 
80
- # By which we mean, "get the filename (with the .rb stripped off) which
81
- # originated the call to this method. Which, for our purposes, is the
82
- # MU subclass that called us. Useful information. And it looks like Perl.
83
- mod_root = Regexp.quote("#{ENV['MU_LIBDIR']}/modules/mu/")
84
- bin_root = Regexp.quote("#{ENV['MU_INSTALLDIR']}/bin/")
85
- caller_name = caller[1]
107
+ if verbosity == MU::Logger::SILENT or (verbosity < MU::Logger::LOUD and level == DEBUG) or (verbosity < MU::Logger::NORMAL and level == INFO)
108
+ return
109
+ end
86
110
 
87
- caller_name.sub!(/:.*/, "")
88
- caller_name.sub!(/^\.\//, "")
89
- caller_name.sub!(/^#{mod_root}/, "")
90
- caller_name.sub!(/^#{bin_root}/, "")
91
- caller_name.sub!(/\.r[ub]$/, "")
92
- caller_name.sub!(/#{Regexp.quote(MU.myRoot)}\//, "")
93
- caller_name.sub!(/^modules\//, "")
111
+ if level == SUMMARY
112
+ @summary << msg
113
+ return
114
+ end
115
+
116
+ caller_name = extract_caller_name(caller[1])
94
117
 
95
118
  time = Time.now.strftime("%b %d %H:%M:%S").to_s
96
119
 
97
120
  Syslog.open("Mu/"+caller_name, Syslog::LOG_PID, Syslog::LOG_DAEMON | Syslog::LOG_LOCAL3) if !Syslog.opened?
98
- if !details.nil?
99
- if details.is_a?(Hash) and details.has_key?(:details)
100
- details = details[:details]
101
- end
102
- details = PP.pp(details, '') if !details.is_a?(String)
103
- end
104
- details = "<pre>"+details+"</pre>" if @html
105
- # We get passed literal quoted newlines sometimes, fix 'em. Get Windows'
106
- # ugly line feeds too.
107
- if !details.nil?
108
- details = details.dup # in case it's frozen or something
109
- details.gsub!(/\\n/, "\n")
110
- details.gsub!(/(\\r|\r)/, "")
111
- end
121
+
122
+ details = format_details(details, html)
112
123
 
113
124
  msg = msg.first if msg.is_a?(Array)
114
125
  msg = "" if msg == nil
115
126
  msg = msg.to_s if !msg.is_a?(String) and msg.respond_to?(:to_s)
116
127
 
117
- # wrapper for writing a log entry to multiple filehandles
118
- # @param handles [Array<IO>]
119
- # @param msgs [Array<String>]
120
- def write(handles = [], msgs = [])
121
- return if handles.nil? or msgs.nil?
122
- handles.each { |h|
123
- msgs.each { |m|
124
- h.puts m
125
- }
126
- }
127
- end
128
-
129
128
  @@log_semaphere.synchronize {
130
129
  handles = [handle]
131
130
  extra_logfile = if deploy and deploy.deploy_dir and Dir.exist?(deploy.deploy_dir)
@@ -134,110 +133,41 @@ module MU
134
133
  handles << extra_logfile if extra_logfile
135
134
  msgs = []
136
135
 
137
- case level
138
- when SUMMARY
139
- @summary << msg
140
- when DEBUG
141
- if verbosity >= MU::Logger::LOUD
142
- if @html
143
- html_out "#{time} - #{caller_name} - #{msg}", "orange"
144
- html_out "&nbsp;#{details}" if details
145
- elsif color
146
- msgs << "#{time} - #{caller_name} - #{msg}".yellow.on_black
147
- msgs << "#{details}".white.on_black if details
148
- else
149
- msgs << "#{time} - #{caller_name} - #{msg}"
150
- msgs << "#{details}" if details
151
- end
152
- Syslog.log(Syslog::LOG_DEBUG, msg.gsub(/%/, ''))
153
- Syslog.log(Syslog::LOG_DEBUG, details.gsub(/%/, '')) if details
154
- end
155
- when INFO
156
- if verbosity >= MU::Logger::NORMAL
157
- if @html
158
- html_out "#{time} - #{caller_name} - #{msg}", "green"
159
- elsif color
160
- msgs << "#{time} - #{caller_name} - #{msg}".green.on_black
161
- else
162
- msgs << "#{time} - #{caller_name} - #{msg}"
163
- end
164
- if verbosity >= MU::Logger::LOUD
165
- if @html
166
- html_out "&nbsp;#{details}"
167
- elsif color
168
- msgs << "#{details}".white.on_black if details
169
- else
170
- msgs << "#{details}" if details
171
- end
172
- end
173
- Syslog.log(Syslog::LOG_NOTICE, msg.gsub(/%/, ''))
174
- Syslog.log(Syslog::LOG_NOTICE, details.gsub(/%/, '')) if details
175
- end
176
- when NOTICE
177
- if @html
178
- html_out "#{time} - #{caller_name} - #{msg}", "yellow"
179
- elsif color
180
- msgs << "#{time} - #{caller_name} - #{msg}".yellow.on_black
181
- else
182
- msgs << "#{time} - #{caller_name} - #{msg}"
183
- end
184
- if verbosity >= MU::Logger::QUIET
185
- if @html
186
- html_out "#{caller_name} - #{msg}"
187
- elsif color
188
- msgs << "#{details}".white.on_black if details
189
- else
190
- msgs << "#{details}" if details
191
- end
192
- end
193
- Syslog.log(Syslog::LOG_NOTICE, msg.gsub(/%/, ''))
194
- Syslog.log(Syslog::LOG_NOTICE, details.gsub(/%/, '')) if details
195
- when WARN
196
- if @html
197
- html_out "#{time} - #{caller_name} - #{msg}", "orange"
198
- elsif color
199
- msgs << "#{time} - #{caller_name} - #{msg}".light_red.on_black
200
- else
201
- msgs << "#{time} - #{caller_name} - #{msg}"
202
- end
203
- if verbosity >= MU::Logger::SILENT
204
- if @html
205
- html_out "#{caller_name} - #{msg}"
206
- elsif color
207
- msgs << "#{details}".white.on_black if details
208
- else
209
- msgs << "#{details}" if details
210
- end
211
- end
212
- Syslog.log(Syslog::LOG_WARNING, msg.gsub(/%/, ''))
213
- Syslog.log(Syslog::LOG_WARNING, details.gsub(/%/, '')) if details
214
- when ERR
215
- if @html
216
- html_out "#{time} - #{caller_name} - #{msg}", "red"
217
- html_out "&nbsp;#{details}" if details
218
- elsif color
219
- msgs << "#{time} - #{caller_name} - #{msg}".red.on_black
220
- msgs << "#{details}".white.on_black if details
221
- else
222
- msgs << "#{time} - #{caller_name} - #{msg}"
223
- msgs << "#{details}" if details
224
- end
225
- Syslog.log(Syslog::LOG_ERR, msg.gsub(/%/, ''))
226
- Syslog.log(Syslog::LOG_ERR, details.gsub(/%/, '')) if details
136
+ if !PRINT_MSG_IF[level][:msg] or level >= PRINT_MSG_IF[level][:msg]
137
+ if html
138
+ html_out "#{time} - #{caller_name} - #{msg}", COLORMAP[level][:html]
139
+ else
140
+ str = "#{time} - #{caller_name} - #{msg}"
141
+ str = str.send(COLORMAP[level][:ansi]).on_black if color
142
+ msgs << str
143
+ end
144
+ Syslog.log(SYSLOG_MAP[level], msg.gsub(/%/, ''))
145
+ end
146
+
147
+ if details and (!PRINT_MSG_IF[level][:details] or level >= PRINT_MSG_IF[level][:details])
148
+ if html
149
+ html_out "&nbsp;#{details}"
227
150
  else
228
- if @html
229
- html_out "#{time} - #{caller_name} - #{msg}"
230
- html_out "&nbsp;#{details}" if details
231
- elsif color
232
- msgs << "#{time} - #{caller_name} - #{msg}".white.on_black
233
- msgs << "#{details}".white.on_black if details
234
- else
235
- msgs << "#{time} - #{caller_name} - #{msg}"
236
- msgs << "#{details}" if details
237
- end
238
- Syslog.log(Syslog::LOG_NOTICE, msg.gsub(/%/, ''))
239
- Syslog.log(Syslog::LOG_NOTICE, details.gsub(/%/, '')) if details
151
+ details = details.white.on_black if color
152
+ msgs << details
153
+ end
154
+ Syslog.log(SYSLOG_MAP[level], details.gsub(/%/, ''))
240
155
  end
156
+
157
+ # else
158
+ # if html
159
+ # html_out "#{time} - #{caller_name} - #{msg}"
160
+ # html_out "&nbsp;#{details}" if details
161
+ # elsif color
162
+ # msgs << "#{time} - #{caller_name} - #{msg}".white.on_black
163
+ # msgs << "#{details}".white.on_black if details
164
+ # else
165
+ # msgs << "#{time} - #{caller_name} - #{msg}"
166
+ # msgs << "#{details}" if details
167
+ # end
168
+ # Syslog.log(Syslog::LOG_NOTICE, msg.gsub(/%/, ''))
169
+ # Syslog.log(Syslog::LOG_NOTICE, details.gsub(/%/, '')) if details
170
+
241
171
  write(handles, msgs)
242
172
 
243
173
  extra_logfile.close if extra_logfile
@@ -247,6 +177,43 @@ module MU
247
177
 
248
178
  private
249
179
 
180
+ def format_details(details, html = false)
181
+ return if details.nil?
182
+
183
+ if details.is_a?(Hash) and details.has_key?(:details)
184
+ details = details[:details]
185
+ end
186
+ details = PP.pp(details, '') if !details.is_a?(String)
187
+
188
+ details = "<pre>"+details+"</pre>" if html
189
+ # We get passed literal quoted newlines sometimes, fix 'em. Get Windows'
190
+ # ugly line feeds too.
191
+
192
+ details = details.dup # in case it's frozen or something
193
+ details.gsub!(/\\n/, "\n")
194
+ details.gsub!(/(\\r|\r)/, "")
195
+
196
+ details
197
+ end
198
+
199
+ # By which we mean, "get the filename (with the .rb stripped off) which
200
+ # originated the call to this method. Which, for our purposes, is the
201
+ # MU subclass that called us. Useful information. And it looks like Perl.
202
+ def extract_caller_name(caller_name)
203
+ return nil if !caller_name or !caller_name.is_a?(String)
204
+ mod_root = Regexp.quote("#{ENV['MU_LIBDIR']}/modules/mu/")
205
+ bin_root = Regexp.quote("#{ENV['MU_INSTALLDIR']}/bin/")
206
+
207
+ caller_name.sub!(/:.*/, "")
208
+ caller_name.sub!(/^\.\//, "")
209
+ caller_name.sub!(/^#{mod_root}/, "")
210
+ caller_name.sub!(/^#{bin_root}/, "")
211
+ caller_name.sub!(/\.r[ub]$/, "")
212
+ caller_name.sub!(/#{Regexp.quote(MU.myRoot)}\//, "")
213
+ caller_name.sub!(/^modules\//, "")
214
+ caller_name
215
+ end
216
+
250
217
  # Output a log message as HTML.
251
218
  #
252
219
  # @param msg [String]: The log message to print
@@ -256,5 +223,17 @@ module MU
256
223
  @handle.puts "<span style='color:#{rgb.css_rgb};'>#{msg}</span>"
257
224
  end
258
225
 
226
+ # wrapper for writing a log entry to multiple filehandles
227
+ # @param handles [Array<IO>]
228
+ # @param msgs [Array<String>]
229
+ def write(handles = [], msgs = [])
230
+ return if handles.nil? or msgs.nil?
231
+ handles.each { |h|
232
+ msgs.each { |m|
233
+ h.puts m
234
+ }
235
+ }
236
+ end
237
+
259
238
  end #class
260
239
  end #module