smart_proxy_salt 0.0.1 → 0.0.2
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/bin/foreman-node +195 -0
- data/etc/foreman.yaml.example +10 -0
- data/lib/smart_proxy_salt/salt_http_config.ru +1 -1
- data/lib/smart_proxy_salt/salt_main.rb +2 -1
- data/lib/smart_proxy_salt/version.rb +1 -1
- metadata +10 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: acacbc60a4623c98351222437e7dc95180ffe154
|
4
|
+
data.tar.gz: 1eadaeb629d7defaeb6112606bfdd25566f47866
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb6e7a820f206431e75d88010840e8d6f9a2ff4068680bcab3dd9ec148931f8b355da8fc66f0f55e5e8996bf20f80e55763890343fc2126c191160f4b253773f
|
7
|
+
data.tar.gz: 6dd63d90454b6becf490e615ca7dcd9bd58fc30a430af9651d9f02569e8103f43242f6636521fef4a61ca8f55faababeb80d03747398444c39099ed712baeffb
|
data/bin/foreman-node
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This is the external nodes script to allow Salt to retrieve info about a host
|
3
|
+
# from Foreman. It also uploads a node's grains to Foreman, if the setting is
|
4
|
+
# enabled.
|
5
|
+
|
6
|
+
require 'yaml'
|
7
|
+
|
8
|
+
$settings_file = "/etc/salt/foreman.yaml"
|
9
|
+
SETTINGS = YAML.load_file($settings_file)
|
10
|
+
|
11
|
+
require 'net/http'
|
12
|
+
require 'net/https'
|
13
|
+
require 'etc'
|
14
|
+
require 'timeout'
|
15
|
+
|
16
|
+
begin
|
17
|
+
require 'json'
|
18
|
+
rescue LoadError
|
19
|
+
# Debian packaging guidelines state to avoid needing rubygems, so
|
20
|
+
# we only try to load it if the first require fails (for RPMs)
|
21
|
+
begin
|
22
|
+
require 'rubygems' rescue nil
|
23
|
+
require 'json'
|
24
|
+
rescue LoadError => e
|
25
|
+
puts "You need the `json` gem to use the Foreman ENC script"
|
26
|
+
# code 1 is already used below
|
27
|
+
exit 2
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def foreman_url
|
32
|
+
"#{SETTINGS[:proto]}://#{SETTINGS[:host]}:#{SETTINGS[:port]}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def valid_hostname? hostname
|
36
|
+
hostname =~ /\A(([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)*([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\z/
|
37
|
+
end
|
38
|
+
|
39
|
+
def get_grains(minion)
|
40
|
+
begin
|
41
|
+
grains = {
|
42
|
+
:name => minion,
|
43
|
+
:facts => plain_grains(minion).merge({:_timestamp => Time.now, :_type => 'foreman_salt'})
|
44
|
+
}
|
45
|
+
|
46
|
+
grains[:facts][:operatingsystem] = grains[:facts]['os']
|
47
|
+
grains[:facts][:operatingsystemrelease] = grains[:facts]['osrelease']
|
48
|
+
|
49
|
+
JSON.pretty_generate(grains)
|
50
|
+
rescue => e
|
51
|
+
puts "Could not get grains: #{e}"
|
52
|
+
exit 1
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def plain_grains minion
|
57
|
+
# We have to get the grains from the cache, because the client
|
58
|
+
# is probably running 'state.highstate' right now.
|
59
|
+
#
|
60
|
+
# salt-run doesn't support directly outputting to json,
|
61
|
+
# so we have resort to python to extract the grains.
|
62
|
+
# Based on https://github.com/saltstack/salt/issues/9444
|
63
|
+
|
64
|
+
script = <<-EOF
|
65
|
+
#!/usr/bin/env python
|
66
|
+
import json
|
67
|
+
import os
|
68
|
+
import sys
|
69
|
+
|
70
|
+
import salt.config
|
71
|
+
import salt.runner
|
72
|
+
|
73
|
+
if __name__ == '__main__':
|
74
|
+
__opts__ = salt.config.master_config(
|
75
|
+
os.environ.get('SALT_MASTER_CONFIG', '/etc/salt/minion'))
|
76
|
+
runner = salt.runner.Runner(__opts__)
|
77
|
+
|
78
|
+
stdout_bak = sys.stdout
|
79
|
+
with open(os.devnull, 'wb') as f:
|
80
|
+
sys.stdout = f
|
81
|
+
ret = runner.cmd('cache.grains', ['#{minion}'])
|
82
|
+
sys.stdout = stdout_bak
|
83
|
+
|
84
|
+
print json.dumps(ret)
|
85
|
+
EOF
|
86
|
+
|
87
|
+
result = IO.popen("python", mode='r+') do |python|
|
88
|
+
python.write script
|
89
|
+
python.close_write
|
90
|
+
result = python.read
|
91
|
+
end
|
92
|
+
|
93
|
+
grains = JSON.load(result)
|
94
|
+
plainify(grains[minion]).flatten.inject(&:merge)
|
95
|
+
end
|
96
|
+
|
97
|
+
def plainify(hash, prefix = nil)
|
98
|
+
result = []
|
99
|
+
hash.each_pair do |key, value|
|
100
|
+
if value.is_a?(Hash)
|
101
|
+
result.push plainify(value, get_key(key, prefix))
|
102
|
+
elsif value.is_a?(Array)
|
103
|
+
result.push plainify(array_to_hash(value), get_key(key, prefix))
|
104
|
+
else
|
105
|
+
new = {}
|
106
|
+
new[get_key(key, prefix)] = value
|
107
|
+
result.push new
|
108
|
+
end
|
109
|
+
end
|
110
|
+
result
|
111
|
+
end
|
112
|
+
|
113
|
+
def array_to_hash(array)
|
114
|
+
new = {}
|
115
|
+
array.each_with_index { |v, index| new[index.to_s] = v }
|
116
|
+
new
|
117
|
+
end
|
118
|
+
|
119
|
+
def get_key(key, prefix)
|
120
|
+
[prefix, key].compact.join('::')
|
121
|
+
end
|
122
|
+
|
123
|
+
def upload_grains(minion)
|
124
|
+
begin
|
125
|
+
grains = get_grains(minion)
|
126
|
+
uri = URI.parse("#{foreman_url}/api/hosts/facts")
|
127
|
+
req = Net::HTTP::Post.new(uri.request_uri)
|
128
|
+
req.add_field('Accept', 'application/json,version=2' )
|
129
|
+
req.content_type = 'application/json'
|
130
|
+
req.body = grains
|
131
|
+
res = Net::HTTP.new(uri.host, uri.port)
|
132
|
+
res.use_ssl = uri.scheme == 'https'
|
133
|
+
if res.use_ssl?
|
134
|
+
if SETTINGS[:ssl_ca] && !SETTINGS[:ssl_ca].empty?
|
135
|
+
res.ca_file = SETTINGS[:ssl_ca]
|
136
|
+
res.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
137
|
+
else
|
138
|
+
res.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
139
|
+
end
|
140
|
+
if SETTINGS[:ssl_cert] && !SETTINGS[:ssl_cert].empty? && SETTINGS[:ssl_key] && !SETTINGS[:ssl_key].empty?
|
141
|
+
res.cert = OpenSSL::X509::Certificate.new(File.read(SETTINGS[:ssl_cert]))
|
142
|
+
res.key = OpenSSL::PKey::RSA.new(File.read(SETTINGS[:ssl_key]), nil)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
res.start { |http| http.request(req) }
|
146
|
+
rescue => e
|
147
|
+
raise "Could not send facts to Foreman: #{e}"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def enc(minion)
|
152
|
+
url = "#{foreman_url}/salt/node/#{minion}?format=yml"
|
153
|
+
uri = URI.parse(url)
|
154
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
155
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
156
|
+
http.use_ssl = uri.scheme == 'https'
|
157
|
+
if http.use_ssl?
|
158
|
+
if SETTINGS[:ssl_ca] && !SETTINGS[:ssl_ca].empty?
|
159
|
+
http.ca_file = SETTINGS[:ssl_ca]
|
160
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
161
|
+
else
|
162
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
163
|
+
end
|
164
|
+
if SETTINGS[:ssl_cert] && !SETTINGS[:ssl_cert].empty? && SETTINGS[:ssl_key] && !SETTINGS[:ssl_key].empty?
|
165
|
+
http.cert = OpenSSL::X509::Certificate.new(File.read(SETTINGS[:ssl_cert]))
|
166
|
+
http.key = OpenSSL::PKey::RSA.new(File.read(SETTINGS[:ssl_key]), nil)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
res = http.start { |http| http.request(req) }
|
171
|
+
|
172
|
+
raise "Error retrieving node #{minion}: #{res.class}\nCheck Foreman's /var/log/foreman/production.log for more information." unless res.code == "200"
|
173
|
+
res.body
|
174
|
+
end
|
175
|
+
|
176
|
+
minion = ARGV[0] || raise("Must provide minion as an argument")
|
177
|
+
|
178
|
+
raise "Invalid hostname" unless valid_hostname? minion
|
179
|
+
begin
|
180
|
+
result = ""
|
181
|
+
|
182
|
+
if SETTINGS[:upload_grains]
|
183
|
+
timeout(SETTINGS[:timeout]) do
|
184
|
+
upload_grains(minion)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
timeout(SETTINGS[:timeout]) do
|
189
|
+
result = enc(minion)
|
190
|
+
end
|
191
|
+
puts result
|
192
|
+
rescue => e
|
193
|
+
puts "Couldn't retrieve ENC data: #{e}"
|
194
|
+
exit 1
|
195
|
+
end
|
@@ -13,6 +13,7 @@ module Proxy::Salt
|
|
13
13
|
return 0
|
14
14
|
end
|
15
15
|
Process.wait(c.pid)
|
16
|
+
logger.info("Result: #{c.read}")
|
16
17
|
rescue Exception => e
|
17
18
|
logger.error("Exception '#{e}' when executing '#{cmd}'")
|
18
19
|
return false
|
@@ -84,7 +85,7 @@ module Proxy::Salt
|
|
84
85
|
|
85
86
|
def highstate host
|
86
87
|
find_salt_binaries
|
87
|
-
cmd = [@sudo, '-u', Proxy::Salt::Plugin.settings.salt_command_user, @salt, "--async",
|
88
|
+
cmd = [@sudo, '-u', Proxy::Salt::Plugin.settings.salt_command_user, @salt, "--async", escape_for_shell(host), "state.highstate"]
|
88
89
|
logger.info "Will run state.highstate for #{host}. Full command: #{cmd.join(" ")}"
|
89
90
|
shell_command(cmd)
|
90
91
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_proxy_salt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Moll
|
@@ -9,17 +9,22 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-08-
|
12
|
+
date: 2014-08-31 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: SaltStack Plug-In for Foreman's Smart Proxy
|
15
15
|
email: foreman-dev@googlegroups.com
|
16
|
-
executables:
|
16
|
+
executables:
|
17
|
+
- foreman-node
|
17
18
|
extensions: []
|
18
|
-
extra_rdoc_files:
|
19
|
+
extra_rdoc_files:
|
20
|
+
- README.md
|
21
|
+
- LICENSE
|
19
22
|
files:
|
20
23
|
- LICENSE
|
21
24
|
- README.md
|
25
|
+
- bin/foreman-node
|
22
26
|
- bundler.d/salt.rb
|
27
|
+
- etc/foreman.yaml.example
|
23
28
|
- lib/smart_proxy_salt.rb
|
24
29
|
- lib/smart_proxy_salt/salt.rb
|
25
30
|
- lib/smart_proxy_salt/salt_api.rb
|
@@ -27,7 +32,7 @@ files:
|
|
27
32
|
- lib/smart_proxy_salt/salt_main.rb
|
28
33
|
- lib/smart_proxy_salt/version.rb
|
29
34
|
- settings.d/salt.yml.example
|
30
|
-
homepage:
|
35
|
+
homepage: https://github.com/theforeman/smart_proxy_salt
|
31
36
|
licenses:
|
32
37
|
- GPLv3
|
33
38
|
metadata: {}
|