cloud-mu 3.1.4 → 3.3.1
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.
- checksums.yaml +4 -4
- data/Dockerfile +5 -1
- data/ansible/roles/mu-windows/README.md +33 -0
- data/ansible/roles/mu-windows/defaults/main.yml +2 -0
- data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
- data/ansible/roles/mu-windows/files/config.xml +76 -0
- data/ansible/roles/mu-windows/handlers/main.yml +2 -0
- data/ansible/roles/mu-windows/meta/main.yml +53 -0
- data/ansible/roles/mu-windows/tasks/main.yml +36 -0
- data/ansible/roles/mu-windows/tests/inventory +2 -0
- data/ansible/roles/mu-windows/tests/test.yml +5 -0
- data/ansible/roles/mu-windows/vars/main.yml +2 -0
- data/bin/mu-adopt +16 -12
- data/bin/mu-azure-tests +57 -0
- data/bin/mu-cleanup +2 -4
- data/bin/mu-configure +52 -0
- data/bin/mu-deploy +3 -3
- data/bin/mu-findstray-tests +25 -0
- data/bin/mu-gen-docs +2 -4
- data/bin/mu-load-config.rb +2 -1
- data/bin/mu-node-manage +15 -16
- data/bin/mu-run-tests +37 -12
- data/cloud-mu.gemspec +5 -3
- data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
- data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
- data/cookbooks/mu-tools/libraries/helper.rb +1 -1
- data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
- data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
- data/cookbooks/mu-tools/recipes/eks.rb +2 -2
- data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
- data/cookbooks/mu-tools/recipes/windows-client.rb +163 -164
- data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
- data/extras/clean-stock-amis +25 -19
- data/extras/generate-stock-images +1 -0
- data/extras/image-generators/AWS/win2k12.yaml +18 -13
- data/extras/image-generators/AWS/win2k16.yaml +18 -13
- data/extras/image-generators/AWS/win2k19.yaml +21 -0
- data/modules/mommacat.ru +1 -1
- data/modules/mu.rb +158 -107
- data/modules/mu/adoption.rb +386 -59
- data/modules/mu/cleanup.rb +214 -303
- data/modules/mu/cloud.rb +128 -1632
- data/modules/mu/cloud/database.rb +49 -0
- data/modules/mu/cloud/dnszone.rb +44 -0
- data/modules/mu/cloud/machine_images.rb +212 -0
- data/modules/mu/cloud/providers.rb +81 -0
- data/modules/mu/cloud/resource_base.rb +926 -0
- data/modules/mu/cloud/server.rb +40 -0
- data/modules/mu/cloud/server_pool.rb +1 -0
- data/modules/mu/cloud/ssh_sessions.rb +228 -0
- data/modules/mu/cloud/winrm_sessions.rb +237 -0
- data/modules/mu/cloud/wrappers.rb +169 -0
- data/modules/mu/config.rb +135 -82
- data/modules/mu/config/alarm.rb +2 -6
- data/modules/mu/config/bucket.rb +32 -3
- data/modules/mu/config/cache_cluster.rb +2 -2
- data/modules/mu/config/cdn.rb +100 -0
- data/modules/mu/config/collection.rb +1 -1
- data/modules/mu/config/container_cluster.rb +7 -2
- data/modules/mu/config/database.rb +84 -105
- data/modules/mu/config/database.yml +1 -2
- data/modules/mu/config/dnszone.rb +5 -4
- data/modules/mu/config/doc_helpers.rb +5 -6
- data/modules/mu/config/endpoint.rb +2 -1
- data/modules/mu/config/firewall_rule.rb +3 -19
- data/modules/mu/config/folder.rb +1 -1
- data/modules/mu/config/function.rb +17 -8
- data/modules/mu/config/group.rb +1 -1
- data/modules/mu/config/habitat.rb +1 -1
- data/modules/mu/config/job.rb +89 -0
- data/modules/mu/config/loadbalancer.rb +57 -11
- data/modules/mu/config/log.rb +1 -1
- data/modules/mu/config/msg_queue.rb +1 -1
- data/modules/mu/config/nosqldb.rb +1 -1
- data/modules/mu/config/notifier.rb +8 -19
- data/modules/mu/config/ref.rb +92 -14
- data/modules/mu/config/role.rb +1 -1
- data/modules/mu/config/schema_helpers.rb +38 -37
- data/modules/mu/config/search_domain.rb +1 -1
- data/modules/mu/config/server.rb +12 -13
- data/modules/mu/config/server.yml +1 -0
- data/modules/mu/config/server_pool.rb +3 -7
- data/modules/mu/config/storage_pool.rb +1 -1
- data/modules/mu/config/tail.rb +11 -0
- data/modules/mu/config/user.rb +1 -1
- data/modules/mu/config/vpc.rb +27 -23
- data/modules/mu/config/vpc.yml +0 -1
- data/modules/mu/defaults/AWS.yaml +91 -68
- data/modules/mu/defaults/Azure.yaml +1 -0
- data/modules/mu/defaults/Google.yaml +1 -0
- data/modules/mu/deploy.rb +33 -19
- data/modules/mu/groomer.rb +16 -1
- data/modules/mu/groomers/ansible.rb +123 -21
- data/modules/mu/groomers/chef.rb +64 -11
- data/modules/mu/logger.rb +120 -144
- data/modules/mu/master.rb +97 -4
- data/modules/mu/master/ssl.rb +0 -1
- data/modules/mu/mommacat.rb +154 -867
- data/modules/mu/mommacat/daemon.rb +23 -14
- data/modules/mu/mommacat/naming.rb +110 -3
- data/modules/mu/mommacat/search.rb +495 -0
- data/modules/mu/mommacat/storage.rb +225 -192
- data/modules/mu/{clouds → providers}/README.md +1 -1
- data/modules/mu/{clouds → providers}/aws.rb +281 -64
- data/modules/mu/{clouds → providers}/aws/alarm.rb +3 -3
- data/modules/mu/{clouds → providers}/aws/bucket.rb +275 -41
- data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +14 -50
- data/modules/mu/providers/aws/cdn.rb +782 -0
- data/modules/mu/{clouds → providers}/aws/collection.rb +5 -5
- data/modules/mu/{clouds → providers}/aws/container_cluster.rb +708 -749
- data/modules/mu/providers/aws/database.rb +1744 -0
- data/modules/mu/{clouds → providers}/aws/dnszone.rb +75 -57
- data/modules/mu/providers/aws/endpoint.rb +1072 -0
- data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +212 -242
- data/modules/mu/{clouds → providers}/aws/folder.rb +1 -1
- data/modules/mu/{clouds → providers}/aws/function.rb +289 -134
- data/modules/mu/{clouds → providers}/aws/group.rb +18 -20
- data/modules/mu/{clouds → providers}/aws/habitat.rb +3 -3
- data/modules/mu/providers/aws/job.rb +466 -0
- data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +50 -41
- data/modules/mu/{clouds → providers}/aws/log.rb +5 -5
- data/modules/mu/{clouds → providers}/aws/msg_queue.rb +14 -11
- data/modules/mu/{clouds → providers}/aws/nosqldb.rb +96 -5
- data/modules/mu/{clouds → providers}/aws/notifier.rb +135 -63
- data/modules/mu/{clouds → providers}/aws/role.rb +94 -57
- data/modules/mu/{clouds → providers}/aws/search_domain.rb +173 -42
- data/modules/mu/{clouds → providers}/aws/server.rb +782 -1107
- data/modules/mu/{clouds → providers}/aws/server_pool.rb +36 -46
- data/modules/mu/{clouds → providers}/aws/storage_pool.rb +21 -38
- data/modules/mu/{clouds → providers}/aws/user.rb +12 -16
- data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
- data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +2 -1
- data/modules/mu/{clouds → providers}/aws/vpc.rb +429 -849
- data/modules/mu/providers/aws/vpc_subnet.rb +286 -0
- data/modules/mu/{clouds → providers}/azure.rb +13 -0
- data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
- data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
- data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
- data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
- data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
- data/modules/mu/{clouds → providers}/azure/server.rb +32 -24
- data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
- data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
- data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
- data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
- data/modules/mu/{clouds → providers}/cloudformation.rb +10 -0
- data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
- data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
- data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
- data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
- data/modules/mu/{clouds → providers}/docker.rb +0 -0
- data/modules/mu/{clouds → providers}/google.rb +29 -6
- data/modules/mu/{clouds → providers}/google/bucket.rb +5 -5
- data/modules/mu/{clouds → providers}/google/container_cluster.rb +59 -37
- data/modules/mu/{clouds → providers}/google/database.rb +5 -12
- data/modules/mu/{clouds → providers}/google/firewall_rule.rb +5 -5
- data/modules/mu/{clouds → providers}/google/folder.rb +5 -9
- data/modules/mu/{clouds → providers}/google/function.rb +14 -8
- data/modules/mu/{clouds → providers}/google/group.rb +9 -17
- data/modules/mu/{clouds → providers}/google/habitat.rb +4 -8
- data/modules/mu/{clouds → providers}/google/loadbalancer.rb +5 -5
- data/modules/mu/{clouds → providers}/google/role.rb +50 -31
- data/modules/mu/{clouds → providers}/google/server.rb +142 -55
- data/modules/mu/{clouds → providers}/google/server_pool.rb +14 -14
- data/modules/mu/{clouds → providers}/google/user.rb +34 -24
- data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
- data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
- data/modules/mu/{clouds → providers}/google/vpc.rb +46 -15
- data/modules/tests/aws-jobs-functions.yaml +46 -0
- data/modules/tests/centos6.yaml +15 -0
- data/modules/tests/centos7.yaml +15 -0
- data/modules/tests/centos8.yaml +12 -0
- data/modules/tests/ecs.yaml +23 -0
- data/modules/tests/eks.yaml +1 -1
- data/modules/tests/functions/node-function/lambda_function.js +10 -0
- data/modules/tests/functions/python-function/lambda_function.py +12 -0
- data/modules/tests/includes-and-params.yaml +2 -1
- data/modules/tests/microservice_app.yaml +288 -0
- data/modules/tests/rds.yaml +108 -0
- data/modules/tests/regrooms/rds.yaml +123 -0
- data/modules/tests/server-with-scrub-muisms.yaml +2 -1
- data/modules/tests/super_complex_bok.yml +2 -2
- data/modules/tests/super_simple_bok.yml +3 -5
- data/modules/tests/win2k12.yaml +25 -0
- data/modules/tests/win2k16.yaml +25 -0
- data/modules/tests/win2k19.yaml +25 -0
- data/requirements.txt +1 -0
- data/spec/mu/clouds/azure_spec.rb +2 -2
- metadata +169 -93
- data/extras/image-generators/AWS/windows.yaml +0 -18
- data/modules/mu/clouds/aws/database.rb +0 -1974
- data/modules/mu/clouds/aws/endpoint.rb +0 -596
- data/modules/tests/needwork/win2k12.yaml +0 -13
data/modules/mu/groomer.rb
CHANGED
|
@@ -30,6 +30,21 @@ module MU
|
|
|
30
30
|
["Chef", "Ansible"]
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
# List of known/supported groomers which are installed and appear to be working
|
|
34
|
+
# @return [Array<String>]
|
|
35
|
+
def self.availableGroomers
|
|
36
|
+
available = []
|
|
37
|
+
MU::Groomer.supportedGroomers.each { |groomer|
|
|
38
|
+
begin
|
|
39
|
+
groomerbase = loadGroomer(groomer)
|
|
40
|
+
available << groomer if groomerbase.available?
|
|
41
|
+
rescue LoadError
|
|
42
|
+
end
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
available
|
|
46
|
+
end
|
|
47
|
+
|
|
33
48
|
# Instance methods that any Groomer plugin must implement
|
|
34
49
|
def self.requiredMethods
|
|
35
50
|
[:preClean, :bootstrap, :haveBootstrapped?, :run, :saveDeployData, :getSecret, :saveSecret, :deleteSecret, :reinstall]
|
|
@@ -37,7 +52,7 @@ module MU
|
|
|
37
52
|
|
|
38
53
|
# Class methods that any Groomer plugin must implement
|
|
39
54
|
def self.requiredClassMethods
|
|
40
|
-
[:getSecret, :cleanup, :saveSecret, :deleteSecret]
|
|
55
|
+
[:getSecret, :cleanup, :saveSecret, :deleteSecret, :available?]
|
|
41
56
|
end
|
|
42
57
|
|
|
43
58
|
class Ansible;
|
|
@@ -24,6 +24,10 @@ module MU
|
|
|
24
24
|
class NoAnsibleExecError < MuError;
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
# One or more Python dependencies missing
|
|
28
|
+
class AnsibleLibrariesError < MuError;
|
|
29
|
+
end
|
|
30
|
+
|
|
27
31
|
# Location in which we'll find our Ansible executables. This only applies
|
|
28
32
|
# to full-grown Mu masters; minimalist gem installs will have to make do
|
|
29
33
|
# with whatever Ansible executables they can find in $PATH.
|
|
@@ -40,6 +44,10 @@ module MU
|
|
|
40
44
|
@ansible_path = node.deploy.deploy_dir+"/ansible"
|
|
41
45
|
@ansible_execs = MU::Groomer::Ansible.ansibleExecDir
|
|
42
46
|
|
|
47
|
+
if !MU::Groomer::Ansible.checkPythonDependencies(@server.windows?)
|
|
48
|
+
raise AnsibleLibrariesError, "One or more python dependencies not available"
|
|
49
|
+
end
|
|
50
|
+
|
|
43
51
|
if !@ansible_execs or @ansible_execs.empty?
|
|
44
52
|
raise NoAnsibleExecError, "No Ansible executables found in visible paths"
|
|
45
53
|
end
|
|
@@ -54,6 +62,10 @@ module MU
|
|
|
54
62
|
installRoles
|
|
55
63
|
end
|
|
56
64
|
|
|
65
|
+
# Are Ansible executables and key libraries present and accounted for?
|
|
66
|
+
def self.available?(windows = false)
|
|
67
|
+
MU::Groomer::Ansible.checkPythonDependencies(windows)
|
|
68
|
+
end
|
|
57
69
|
|
|
58
70
|
# Indicate whether our server has been bootstrapped with Ansible
|
|
59
71
|
def haveBootstrapped?
|
|
@@ -66,6 +78,7 @@ module MU
|
|
|
66
78
|
# @param permissions [Boolean]: If true, save the secret under the current active deploy (if any), rather than in the global location for this user
|
|
67
79
|
# @param deploy_dir [String]: If permissions is +true+, save the secret here
|
|
68
80
|
def self.saveSecret(vault: nil, item: nil, data: nil, permissions: false, deploy_dir: nil)
|
|
81
|
+
|
|
69
82
|
if vault.nil? or vault.empty? or item.nil? or item.empty?
|
|
70
83
|
raise MuError, "Must call saveSecret with vault and item names"
|
|
71
84
|
end
|
|
@@ -73,7 +86,6 @@ module MU
|
|
|
73
86
|
raise MuError, "Ansible vault/item names cannot include forward slashes"
|
|
74
87
|
end
|
|
75
88
|
pwfile = vaultPasswordFile
|
|
76
|
-
|
|
77
89
|
|
|
78
90
|
dir = if permissions
|
|
79
91
|
if deploy_dir
|
|
@@ -95,8 +107,9 @@ module MU
|
|
|
95
107
|
if File.exist?(path)
|
|
96
108
|
MU.log "Overwriting existing vault #{vault} item #{item}"
|
|
97
109
|
end
|
|
110
|
+
|
|
98
111
|
File.open(path, File::CREAT|File::RDWR|File::TRUNC, 0600) { |f|
|
|
99
|
-
f.write data
|
|
112
|
+
f.write data.to_yaml
|
|
100
113
|
}
|
|
101
114
|
|
|
102
115
|
cmd = %Q{#{ansibleExecDir}/ansible-vault encrypt #{path} --vault-password-file #{pwfile}}
|
|
@@ -115,14 +128,23 @@ module MU
|
|
|
115
128
|
# @param item [String]: The item within the repository to retrieve
|
|
116
129
|
# @param field [String]: OPTIONAL - A specific field within the item to return.
|
|
117
130
|
# @return [Hash]
|
|
118
|
-
def self.getSecret(vault: nil, item: nil, field: nil)
|
|
131
|
+
def self.getSecret(vault: nil, item: nil, field: nil, deploy_dir: nil)
|
|
119
132
|
if vault.nil? or vault.empty?
|
|
120
133
|
raise MuError, "Must call getSecret with at least a vault name"
|
|
121
134
|
end
|
|
122
|
-
|
|
123
135
|
pwfile = vaultPasswordFile
|
|
124
|
-
|
|
125
|
-
|
|
136
|
+
|
|
137
|
+
dir = nil
|
|
138
|
+
try = [secret_dir+"/"+vault]
|
|
139
|
+
try << deploy_dir+"/ansible/vaults/"+vault if deploy_dir
|
|
140
|
+
try << MU.mommacat.deploy_dir+"/ansible/vaults/"+vault if MU.mommacat.deploy_dir
|
|
141
|
+
try.each { |maybe_dir|
|
|
142
|
+
if Dir.exist?(maybe_dir) and (item.nil? or File.exist?(maybe_dir+"/"+item))
|
|
143
|
+
dir = maybe_dir
|
|
144
|
+
break
|
|
145
|
+
end
|
|
146
|
+
}
|
|
147
|
+
if dir.nil?
|
|
126
148
|
raise MuNoSuchSecret, "No such vault #{vault}"
|
|
127
149
|
end
|
|
128
150
|
|
|
@@ -135,17 +157,23 @@ module MU
|
|
|
135
157
|
cmd = %Q{#{ansibleExecDir}/ansible-vault view #{itempath} --vault-password-file #{pwfile}}
|
|
136
158
|
MU.log cmd
|
|
137
159
|
a = `#{cmd}`
|
|
138
|
-
# If we happen to have stored recognizeable JSON, return it
|
|
139
|
-
# which is a behavior we're used to from Chef vault.
|
|
140
|
-
# a String.
|
|
160
|
+
# If we happen to have stored recognizeable JSON or YAML, return it
|
|
161
|
+
# as parsed, which is a behavior we're used to from Chef vault.
|
|
162
|
+
# Otherwise, return a String.
|
|
141
163
|
begin
|
|
142
164
|
data = JSON.parse(a)
|
|
143
|
-
if field and data[field]
|
|
144
|
-
data = data[field]
|
|
145
|
-
end
|
|
146
165
|
rescue JSON::ParserError
|
|
147
|
-
|
|
166
|
+
begin
|
|
167
|
+
data = YAML.load(a)
|
|
168
|
+
rescue Psych::SyntaxError => e
|
|
169
|
+
data = a
|
|
170
|
+
end
|
|
148
171
|
end
|
|
172
|
+
[vault, item, field].each { |tier|
|
|
173
|
+
if data and data.is_a?(Hash) and tier and data[tier]
|
|
174
|
+
data = data[tier]
|
|
175
|
+
end
|
|
176
|
+
}
|
|
149
177
|
else
|
|
150
178
|
data = []
|
|
151
179
|
Dir.foreach(dir) { |entry|
|
|
@@ -160,7 +188,7 @@ module MU
|
|
|
160
188
|
|
|
161
189
|
# see {MU::Groomer::Ansible.getSecret}
|
|
162
190
|
def getSecret(vault: nil, item: nil, field: nil)
|
|
163
|
-
self.class.getSecret(vault: vault, item: item, field: field)
|
|
191
|
+
self.class.getSecret(vault: vault, item: item, field: field, deploy_dir: @server.deploy.deploy_dir)
|
|
164
192
|
end
|
|
165
193
|
|
|
166
194
|
# Delete a Ansible data bag / Vault
|
|
@@ -215,7 +243,9 @@ module MU
|
|
|
215
243
|
play = {
|
|
216
244
|
"hosts" => @server.config['name']
|
|
217
245
|
}
|
|
218
|
-
|
|
246
|
+
if !@server.windows? and @server.config['ssh_user'] != "root"
|
|
247
|
+
play["become"] = "yes"
|
|
248
|
+
end
|
|
219
249
|
play["roles"] = override_runlist if @server.config['run_list'] and !@server.config['run_list'].empty?
|
|
220
250
|
play["vars"] = @server.config['ansible_vars'] if @server.config['ansible_vars']
|
|
221
251
|
|
|
@@ -227,7 +257,7 @@ module MU
|
|
|
227
257
|
"#{@server.config['name']}.yml"
|
|
228
258
|
end
|
|
229
259
|
|
|
230
|
-
cmd = %Q{cd #{@ansible_path} && echo "#{purpose}" && #{@ansible_execs}/ansible-playbook -i hosts #{playbook} --limit=#{@server.mu_name} --vault-password-file #{pwfile} --timeout=30 --vault-password-file #{@ansible_path}/.vault_pw -u #{ssh_user}}
|
|
260
|
+
cmd = %Q{cd #{@ansible_path} && echo "#{purpose}" && #{@ansible_execs}/ansible-playbook -i hosts #{playbook} --limit=#{@server.windows? ? @server.canonicalIP : @server.mu_name} --vault-password-file #{pwfile} --timeout=30 --vault-password-file #{@ansible_path}/.vault_pw -u #{ssh_user}}
|
|
231
261
|
|
|
232
262
|
retries = 0
|
|
233
263
|
begin
|
|
@@ -252,6 +282,7 @@ module MU
|
|
|
252
282
|
sleep 30
|
|
253
283
|
retries += 1
|
|
254
284
|
MU.log "Failed Ansible run, will retry (#{retries.to_s}/#{max_retries.to_s})", MU::NOTICE, details: cmd
|
|
285
|
+
|
|
255
286
|
retry
|
|
256
287
|
else
|
|
257
288
|
tmpfile.unlink if tmpfile
|
|
@@ -275,12 +306,12 @@ module MU
|
|
|
275
306
|
# Bootstrap our server with Ansible- basically, just make sure this node
|
|
276
307
|
# is listed in our deployment's Ansible inventory.
|
|
277
308
|
def bootstrap
|
|
278
|
-
@inventory.add(@server.config['name'], @server.mu_name)
|
|
309
|
+
@inventory.add(@server.config['name'], @server.windows? ? @server.canonicalIP : @server.mu_name)
|
|
279
310
|
play = {
|
|
280
311
|
"hosts" => @server.config['name']
|
|
281
312
|
}
|
|
282
313
|
|
|
283
|
-
if @server.config['ssh_user'] != "root"
|
|
314
|
+
if !@server.windows? and @server.config['ssh_user'] != "root"
|
|
284
315
|
play["become"] = "yes"
|
|
285
316
|
end
|
|
286
317
|
|
|
@@ -292,9 +323,26 @@ module MU
|
|
|
292
323
|
play["vars"] = @server.config['ansible_vars']
|
|
293
324
|
end
|
|
294
325
|
|
|
326
|
+
if @server.windows?
|
|
327
|
+
play["vars"] ||= {}
|
|
328
|
+
play["vars"]["ansible_connection"] = "winrm"
|
|
329
|
+
play["vars"]["ansible_winrm_scheme"] = "https"
|
|
330
|
+
play["vars"]["ansible_winrm_transport"] = "ntlm"
|
|
331
|
+
play["vars"]["ansible_winrm_server_cert_validation"] = "ignore" # XXX this sucks; use Mu_CA.pem if we can get it to work
|
|
332
|
+
# play["vars"]["ansible_winrm_ca_trust_path"] = "#{MU.mySSLDir}/Mu_CA.pem"
|
|
333
|
+
play["vars"]["ansible_user"] = @server.config['windows_admin_username']
|
|
334
|
+
win_pw = @server.getWindowsAdminPassword
|
|
335
|
+
|
|
336
|
+
pwfile = MU::Groomer::Ansible.vaultPasswordFile
|
|
337
|
+
cmd = %Q{#{MU::Groomer::Ansible.ansibleExecDir}/ansible-vault}
|
|
338
|
+
output = %x{#{cmd} encrypt_string '#{win_pw.gsub(/'/, "\\\\'")}' --vault-password-file #{pwfile}}
|
|
339
|
+
|
|
340
|
+
play["vars"]["ansible_password"] = output
|
|
341
|
+
end
|
|
342
|
+
|
|
295
343
|
File.open(@ansible_path+"/"+@server.config['name']+".yml", File::CREAT|File::RDWR|File::TRUNC, 0600) { |f|
|
|
296
344
|
f.flock(File::LOCK_EX)
|
|
297
|
-
f.puts [play].to_yaml
|
|
345
|
+
f.puts [play].to_yaml.sub(/ansible_password: \|-?[\n\s]+/, 'ansible_password: ') # Ansible doesn't like this (legal) YAML
|
|
298
346
|
f.flock(File::LOCK_UN)
|
|
299
347
|
}
|
|
300
348
|
end
|
|
@@ -351,11 +399,18 @@ module MU
|
|
|
351
399
|
allvars['deployment']
|
|
352
400
|
end
|
|
353
401
|
|
|
402
|
+
# Nuke everything associated with a deploy. Since we're just some files
|
|
403
|
+
# in the deploy directory, this doesn't have to do anything.
|
|
404
|
+
def self.cleanup(deploy_id, noop = false)
|
|
405
|
+
# deploy = MU::MommaCat.new(MU.deploy_id)
|
|
406
|
+
# inventory = Inventory.new(deploy)
|
|
407
|
+
end
|
|
408
|
+
|
|
354
409
|
# Expunge Ansible resources associated with a node.
|
|
355
410
|
# @param node [String]: The Mu name of the node in question.
|
|
356
411
|
# @param _vaults_to_clean [Array<Hash>]: Dummy argument, part of this method's interface but not used by the Ansible layer
|
|
357
412
|
# @param noop [Boolean]: Skip actual deletion, just state what we'd do
|
|
358
|
-
def self.
|
|
413
|
+
def self.purge(node, _vaults_to_clean = [], noop = false)
|
|
359
414
|
deploy = MU::MommaCat.new(MU.deploy_id)
|
|
360
415
|
inventory = Inventory.new(deploy)
|
|
361
416
|
# ansible_path = deploy.deploy_dir+"/ansible"
|
|
@@ -388,6 +443,51 @@ module MU
|
|
|
388
443
|
if !system(cmd, "encrypt_string", string, "--name", name, "--vault-password-file", pwfile)
|
|
389
444
|
raise MuError, "Failed Ansible command: #{cmd} encrypt_string <redacted> --name #{name} --vault-password-file"
|
|
390
445
|
end
|
|
446
|
+
output
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# Hunt down and return a path for a Python executable
|
|
450
|
+
# @return [String]
|
|
451
|
+
def self.pythonExecDir
|
|
452
|
+
path = nil
|
|
453
|
+
|
|
454
|
+
if File.exist?(BINDIR+"/python")
|
|
455
|
+
path = BINDIR
|
|
456
|
+
else
|
|
457
|
+
paths = [ansibleExecDir]
|
|
458
|
+
paths.concat(ENV['PATH'].split(/:/))
|
|
459
|
+
paths << "/usr/bin" # not always in path, esp in pared-down Docker images
|
|
460
|
+
paths.reject! { |p| p.nil? }
|
|
461
|
+
paths.uniq.each { |bindir|
|
|
462
|
+
if File.exist?(bindir+"/python")
|
|
463
|
+
path = bindir
|
|
464
|
+
break
|
|
465
|
+
end
|
|
466
|
+
}
|
|
467
|
+
end
|
|
468
|
+
path
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
# Make sure what's in our Python requirements.txt is reflected in the
|
|
472
|
+
# Python we're about to run for Ansible
|
|
473
|
+
def self.checkPythonDependencies(windows = false)
|
|
474
|
+
return nil if !ansibleExecDir
|
|
475
|
+
|
|
476
|
+
execline = File.readlines(ansibleExecDir+"/ansible-playbook").first.chomp.sub(/^#!/, '')
|
|
477
|
+
if !execline
|
|
478
|
+
MU.log "Unable to extract a Python executable from #{ansibleExecDir}/ansible-playbook", MU::ERR
|
|
479
|
+
return false
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
require 'tempfile'
|
|
483
|
+
f = Tempfile.new("pythoncheck")
|
|
484
|
+
f.puts "import ansible"
|
|
485
|
+
f.puts "import winrm" if windows
|
|
486
|
+
f.close
|
|
487
|
+
|
|
488
|
+
system(%Q{#{execline} #{f.path}})
|
|
489
|
+
f.unlink
|
|
490
|
+
$?.exitstatus == 0 ? true : false
|
|
391
491
|
end
|
|
392
492
|
|
|
393
493
|
# Hunt down and return a path for Ansible executables
|
|
@@ -397,7 +497,9 @@ module MU
|
|
|
397
497
|
if File.exist?(BINDIR+"/ansible-playbook")
|
|
398
498
|
path = BINDIR
|
|
399
499
|
else
|
|
400
|
-
ENV['PATH'].split(/:/)
|
|
500
|
+
paths = ENV['PATH'].split(/:/)
|
|
501
|
+
paths << "/usr/bin"
|
|
502
|
+
paths.uniq.each { |bindir|
|
|
401
503
|
if File.exist?(bindir+"/ansible-playbook")
|
|
402
504
|
path = bindir
|
|
403
505
|
if !File.exist?(bindir+"/ansible-vault")
|
data/modules/mu/groomers/chef.rb
CHANGED
|
@@ -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
|
|
@@ -329,7 +335,7 @@ module MU
|
|
|
329
335
|
}
|
|
330
336
|
else
|
|
331
337
|
MU.log "Invoking Chef over WinRM on #{@server.mu_name}: #{purpose}"
|
|
332
|
-
winrm = @server.getWinRMSession(haveBootstrapped? ?
|
|
338
|
+
winrm = @server.getWinRMSession(haveBootstrapped? ? 2 : max_retries)
|
|
333
339
|
if @server.windows? and @server.windowsRebootPending?(winrm)
|
|
334
340
|
# Windows frequently gets stuck here
|
|
335
341
|
if retries > 5
|
|
@@ -362,7 +368,7 @@ module MU
|
|
|
362
368
|
}
|
|
363
369
|
|
|
364
370
|
if resp.exitcode == 1 and output_lines.join("\n").match(/Chef Client finished/)
|
|
365
|
-
MU.log
|
|
371
|
+
MU.log output_lines.last
|
|
366
372
|
elsif resp.exitcode != 0
|
|
367
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/)
|
|
368
374
|
raise MU::Groomer::RunError, output_lines.slice(output_lines.length-50, output_lines.length).join("")
|
|
@@ -415,9 +421,9 @@ module MU
|
|
|
415
421
|
if retries < max_retries
|
|
416
422
|
retries += 1
|
|
417
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
|
|
418
|
-
if purpose != "Base Windows configuration"
|
|
419
|
-
windows_try_ssh = !windows_try_ssh
|
|
420
|
-
end
|
|
424
|
+
# if purpose != "Base Windows configuration"
|
|
425
|
+
# windows_try_ssh = !windows_try_ssh
|
|
426
|
+
# end
|
|
421
427
|
if e.is_a?(WinRM::WinRMError)
|
|
422
428
|
if @server.windows? and retries >= 3 and retries % 3 == 0
|
|
423
429
|
# Mix in a hard reboot if WinRM isn't answering
|
|
@@ -619,13 +625,20 @@ module MU
|
|
|
619
625
|
kb.name_args = [@server.mu_name]
|
|
620
626
|
kb.config[:manual] = true
|
|
621
627
|
kb.config[:winrm_transport] = :ssl
|
|
622
|
-
kb.config[:host] = @server.mu_name
|
|
623
628
|
kb.config[:winrm_port] = 5986
|
|
624
629
|
kb.config[:session_timeout] = timeout
|
|
625
630
|
kb.config[:operation_timeout] = timeout
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
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
|
|
629
642
|
# kb.config[:ca_trust_file] = "#{MU.mySSLDir}/Mu_CA.pem"
|
|
630
643
|
# XXX ca_trust_file doesn't work for some reason, so we have to set the below for now
|
|
631
644
|
kb.config[:winrm_ssl_verify_mode] = :verify_none
|
|
@@ -675,7 +688,7 @@ module MU
|
|
|
675
688
|
preClean(false) # it's ok for this to fail
|
|
676
689
|
rescue StandardError => e
|
|
677
690
|
end
|
|
678
|
-
MU::Groomer::Chef.
|
|
691
|
+
MU::Groomer::Chef.purge(@server.mu_name, nodeonly: true)
|
|
679
692
|
@config['forced_preclean'] = true
|
|
680
693
|
@server.reboot if @server.windows? # *sigh*
|
|
681
694
|
end
|
|
@@ -792,12 +805,52 @@ retry
|
|
|
792
805
|
end
|
|
793
806
|
end
|
|
794
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
|
+
|
|
795
848
|
# Expunge Chef resources associated with a node.
|
|
796
849
|
# @param node [String]: The Mu name of the node in question.
|
|
797
850
|
# @param vaults_to_clean [Array<Hash>]: Some vaults to expunge
|
|
798
851
|
# @param noop [Boolean]: Skip actual deletion, just state what we'd do
|
|
799
852
|
# @param nodeonly [Boolean]: Just delete the node and its keys, but leave other artifacts
|
|
800
|
-
def self.
|
|
853
|
+
def self.purge(node, vaults_to_clean = [], noop = false, nodeonly: false)
|
|
801
854
|
loadChefLib
|
|
802
855
|
MU.log "Deleting Chef resources associated with #{node}"
|
|
803
856
|
if !nodeonly
|
data/modules/mu/logger.rb
CHANGED
|
@@ -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
|
|
@@ -76,59 +103,28 @@ module MU
|
|
|
76
103
|
html ||= @html
|
|
77
104
|
handle ||= @handle
|
|
78
105
|
color ||= @color
|
|
79
|
-
return if verbosity == MU::Logger::SILENT
|
|
80
|
-
return if verbosity < MU::Logger::LOUD and level == DEBUG
|
|
81
|
-
return if verbosity < MU::Logger::NORMAL and level == INFO
|
|
82
106
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
mod_root = Regexp.quote("#{ENV['MU_LIBDIR']}/modules/mu/")
|
|
87
|
-
bin_root = Regexp.quote("#{ENV['MU_INSTALLDIR']}/bin/")
|
|
88
|
-
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
|
|
89
110
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
caller_name
|
|
96
|
-
caller_name.sub!(/^modules\//, "")
|
|
111
|
+
if level == SUMMARY
|
|
112
|
+
@summary << msg
|
|
113
|
+
return
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
caller_name = extract_caller_name(caller[1])
|
|
97
117
|
|
|
98
118
|
time = Time.now.strftime("%b %d %H:%M:%S").to_s
|
|
99
119
|
|
|
100
120
|
Syslog.open("Mu/"+caller_name, Syslog::LOG_PID, Syslog::LOG_DAEMON | Syslog::LOG_LOCAL3) if !Syslog.opened?
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
details = details[:details]
|
|
104
|
-
end
|
|
105
|
-
details = PP.pp(details, '') if !details.is_a?(String)
|
|
106
|
-
end
|
|
107
|
-
details = "<pre>"+details+"</pre>" if html
|
|
108
|
-
# We get passed literal quoted newlines sometimes, fix 'em. Get Windows'
|
|
109
|
-
# ugly line feeds too.
|
|
110
|
-
if !details.nil?
|
|
111
|
-
details = details.dup # in case it's frozen or something
|
|
112
|
-
details.gsub!(/\\n/, "\n")
|
|
113
|
-
details.gsub!(/(\\r|\r)/, "")
|
|
114
|
-
end
|
|
121
|
+
|
|
122
|
+
details = format_details(details, html)
|
|
115
123
|
|
|
116
124
|
msg = msg.first if msg.is_a?(Array)
|
|
117
125
|
msg = "" if msg == nil
|
|
118
126
|
msg = msg.to_s if !msg.is_a?(String) and msg.respond_to?(:to_s)
|
|
119
127
|
|
|
120
|
-
# wrapper for writing a log entry to multiple filehandles
|
|
121
|
-
# @param handles [Array<IO>]
|
|
122
|
-
# @param msgs [Array<String>]
|
|
123
|
-
def write(handles = [], msgs = [])
|
|
124
|
-
return if handles.nil? or msgs.nil?
|
|
125
|
-
handles.each { |h|
|
|
126
|
-
msgs.each { |m|
|
|
127
|
-
h.puts m
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
end
|
|
131
|
-
|
|
132
128
|
@@log_semaphere.synchronize {
|
|
133
129
|
handles = [handle]
|
|
134
130
|
extra_logfile = if deploy and deploy.deploy_dir and Dir.exist?(deploy.deploy_dir)
|
|
@@ -137,110 +133,41 @@ module MU
|
|
|
137
133
|
handles << extra_logfile if extra_logfile
|
|
138
134
|
msgs = []
|
|
139
135
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
end
|
|
155
|
-
Syslog.log(Syslog::LOG_DEBUG, msg.gsub(/%/, ''))
|
|
156
|
-
Syslog.log(Syslog::LOG_DEBUG, details.gsub(/%/, '')) if details
|
|
157
|
-
end
|
|
158
|
-
when INFO
|
|
159
|
-
if verbosity >= MU::Logger::NORMAL
|
|
160
|
-
if html
|
|
161
|
-
html_out "#{time} - #{caller_name} - #{msg}", "green"
|
|
162
|
-
elsif color
|
|
163
|
-
msgs << "#{time} - #{caller_name} - #{msg}".green.on_black
|
|
164
|
-
else
|
|
165
|
-
msgs << "#{time} - #{caller_name} - #{msg}"
|
|
166
|
-
end
|
|
167
|
-
if verbosity >= MU::Logger::LOUD
|
|
168
|
-
if html
|
|
169
|
-
html_out " #{details}"
|
|
170
|
-
elsif color
|
|
171
|
-
msgs << "#{details}".white.on_black if details
|
|
172
|
-
else
|
|
173
|
-
msgs << "#{details}" if details
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
Syslog.log(Syslog::LOG_NOTICE, msg.gsub(/%/, ''))
|
|
177
|
-
Syslog.log(Syslog::LOG_NOTICE, details.gsub(/%/, '')) if details
|
|
178
|
-
end
|
|
179
|
-
when NOTICE
|
|
180
|
-
if html
|
|
181
|
-
html_out "#{time} - #{caller_name} - #{msg}", "yellow"
|
|
182
|
-
elsif color
|
|
183
|
-
msgs << "#{time} - #{caller_name} - #{msg}".yellow.on_black
|
|
184
|
-
else
|
|
185
|
-
msgs << "#{time} - #{caller_name} - #{msg}"
|
|
186
|
-
end
|
|
187
|
-
if verbosity >= MU::Logger::QUIET
|
|
188
|
-
if html
|
|
189
|
-
html_out "#{caller_name} - #{msg}"
|
|
190
|
-
elsif color
|
|
191
|
-
msgs << "#{details}".white.on_black if details
|
|
192
|
-
else
|
|
193
|
-
msgs << "#{details}" if details
|
|
194
|
-
end
|
|
195
|
-
end
|
|
196
|
-
Syslog.log(Syslog::LOG_NOTICE, msg.gsub(/%/, ''))
|
|
197
|
-
Syslog.log(Syslog::LOG_NOTICE, details.gsub(/%/, '')) if details
|
|
198
|
-
when WARN
|
|
199
|
-
if html
|
|
200
|
-
html_out "#{time} - #{caller_name} - #{msg}", "orange"
|
|
201
|
-
elsif color
|
|
202
|
-
msgs << "#{time} - #{caller_name} - #{msg}".light_red.on_black
|
|
203
|
-
else
|
|
204
|
-
msgs << "#{time} - #{caller_name} - #{msg}"
|
|
205
|
-
end
|
|
206
|
-
if verbosity >= MU::Logger::SILENT
|
|
207
|
-
if html
|
|
208
|
-
html_out "#{caller_name} - #{msg}"
|
|
209
|
-
elsif color
|
|
210
|
-
msgs << "#{details}".white.on_black if details
|
|
211
|
-
else
|
|
212
|
-
msgs << "#{details}" if details
|
|
213
|
-
end
|
|
214
|
-
end
|
|
215
|
-
Syslog.log(Syslog::LOG_WARNING, msg.gsub(/%/, ''))
|
|
216
|
-
Syslog.log(Syslog::LOG_WARNING, details.gsub(/%/, '')) if details
|
|
217
|
-
when ERR
|
|
218
|
-
if html
|
|
219
|
-
html_out "#{time} - #{caller_name} - #{msg}", "red"
|
|
220
|
-
html_out " #{details}" if details
|
|
221
|
-
elsif color
|
|
222
|
-
msgs << "#{time} - #{caller_name} - #{msg}".red.on_black
|
|
223
|
-
msgs << "#{details}".white.on_black if details
|
|
224
|
-
else
|
|
225
|
-
msgs << "#{time} - #{caller_name} - #{msg}"
|
|
226
|
-
msgs << "#{details}" if details
|
|
227
|
-
end
|
|
228
|
-
Syslog.log(Syslog::LOG_ERR, msg.gsub(/%/, ''))
|
|
229
|
-
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 " #{details}"
|
|
230
150
|
else
|
|
231
|
-
if
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
msgs << "#{time} - #{caller_name} - #{msg}".white.on_black
|
|
236
|
-
msgs << "#{details}".white.on_black if details
|
|
237
|
-
else
|
|
238
|
-
msgs << "#{time} - #{caller_name} - #{msg}"
|
|
239
|
-
msgs << "#{details}" if details
|
|
240
|
-
end
|
|
241
|
-
Syslog.log(Syslog::LOG_NOTICE, msg.gsub(/%/, ''))
|
|
242
|
-
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(/%/, ''))
|
|
243
155
|
end
|
|
156
|
+
|
|
157
|
+
# else
|
|
158
|
+
# if html
|
|
159
|
+
# html_out "#{time} - #{caller_name} - #{msg}"
|
|
160
|
+
# html_out " #{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
|
+
|
|
244
171
|
write(handles, msgs)
|
|
245
172
|
|
|
246
173
|
extra_logfile.close if extra_logfile
|
|
@@ -250,6 +177,43 @@ module MU
|
|
|
250
177
|
|
|
251
178
|
private
|
|
252
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
|
+
|
|
253
217
|
# Output a log message as HTML.
|
|
254
218
|
#
|
|
255
219
|
# @param msg [String]: The log message to print
|
|
@@ -259,5 +223,17 @@ module MU
|
|
|
259
223
|
@handle.puts "<span style='color:#{rgb.css_rgb};'>#{msg}</span>"
|
|
260
224
|
end
|
|
261
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
|
+
|
|
262
238
|
end #class
|
|
263
239
|
end #module
|