cloud-mu 3.1.2 → 3.2.0

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.
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