Project

General

Profile

« Previous | Next » 

Revision 8a1afdb6

Added by John (JJ) Jawed about 10 years ago

Add support for parallel push facts

View differences:

spec/unit/foreman_external_node_spec.rb
File.stubs(:stat).returns(stub(:mtime => Time.now.utc))
enc.stubs(:build_body).returns({'fake' => 'data'})
enc.upload_facts('fake.host.fqdn.com',"#{static_fixture_path}/fake.host.fqdn.com.yaml")
req = enc.generate_fact_request('fake.host.fqdn.com',"#{static_fixture_path}/fake.host.fqdn.com.yaml")
enc.upload_facts('fake.host.fqdn.com',req)
webstub.should have_been_requested
# test pushing facts async
http_fact_requests = []
http_fact_requests << ['fake.host.fqdn.com', req]
enc.upload_facts_parallel(http_fact_requests)
webstub.should have_been_requested.times(2)
http_fact_requests << ['fake.host.fqdn.com', req]
http_fact_requests << ['fake.host.fqdn.com', req]
enc.upload_facts_parallel(http_fact_requests)
webstub.should have_been_requested.times(4)
end
it "should have the correct certname and hostname" do
templates/external_node_v2.rb.erb
:puppetuser => "<%= @puppet_user %>", # e.g. puppet
:facts => <%= @facts %>, # true/false to upload facts
:timeout => 10,
:threads => nil,
# if CA is specified, remote Foreman host will be verified
:ssl_ca => "<%= @ssl_ca -%>", # e.g. /var/lib/puppet/ssl/certs/ca.pem
# ssl_cert and key are required if require_ssl_puppetmasters is enabled in Foreman
......
SETTINGS[:timeout] || 3
end
def thread_count
SETTINGS[:threads] || case RbConfig::CONFIG['host_os']
when /darwin9/
`hwprefs cpu_count`.to_i
when /darwin/
((`which hwprefs` != '') ? `hwprefs thread_count` : `sysctl -n hw.ncpu`).to_i
when /linux/
`cat /proc/cpuinfo | grep processor | wc -l`.to_i
when /freebsd/
`sysctl -n hw.ncpu`.to_i
when /mswin|mingw/
require 'win32ole'
wmi = WIN32OLE.connect("winmgmts://")
cpu = wmi.ExecQuery("select NumberOfCores from Win32_Processor") # TODO count hyper-threaded in this
cpu.to_enum.first.NumberOfCores
else
4
end
end
class Http_Fact_Requests
include Enumerable
def initialize
@results_array = []
end
def <<(val)
@results_array << val
end
def each(&block)
@results_array.each(&block)
end
def pop
@results_array.pop
end
end
require 'etc'
require 'net/http'
require 'net/https'
......
end
end
def upload_all_facts
def process_all_facts(http_requests)
Dir["#{puppetdir}/yaml/facts/*.yaml"].each do |f|
certname = File.basename(f, ".yaml")
# Skip empty host fact yaml files
if File.size(f) != 0
upload_facts(certname, f)
req = generate_fact_request(certname, f)
if http_requests
http_requests << [certname, req]
else
upload_facts(certname, req)
end
end
end
end
......
{'facts' => puppet_facts['values'], 'name' => hostname, 'certname' => certname}
end
def upload_facts(certname, filename)
def initialize_http(uri)
res = Net::HTTP.new(uri.host, uri.port)
res.use_ssl = uri.scheme == 'https'
if res.use_ssl?
if SETTINGS[:ssl_ca] && !SETTINGS[:ssl_ca].empty?
res.ca_file = SETTINGS[:ssl_ca]
res.verify_mode = OpenSSL::SSL::VERIFY_PEER
else
res.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
if SETTINGS[:ssl_cert] && !SETTINGS[:ssl_cert].empty? && SETTINGS[:ssl_key] && !SETTINGS[:ssl_key].empty?
res.cert = OpenSSL::X509::Certificate.new(File.read(SETTINGS[:ssl_cert]))
res.key = OpenSSL::PKey::RSA.new(File.read(SETTINGS[:ssl_key]), nil)
end
end
res
end
def generate_fact_request(certname, filename)
# Temp file keeping the last run time
stat = stat_file("#{certname}-push-facts")
last_run = File.exists?(stat) ? File.stat(stat).mtime.utc : Time.now - 365*24*60*60
......
req.add_field('Accept', 'application/json,version=2' )
req.content_type = 'application/json'
req.body = build_body(certname, filename).to_json
res = Net::HTTP.new(uri.host, uri.port)
res.use_ssl = uri.scheme == 'https'
if res.use_ssl?
if SETTINGS[:ssl_ca] && !SETTINGS[:ssl_ca].empty?
res.ca_file = SETTINGS[:ssl_ca]
res.verify_mode = OpenSSL::SSL::VERIFY_PEER
else
res.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
if SETTINGS[:ssl_cert] && !SETTINGS[:ssl_cert].empty? && SETTINGS[:ssl_key] && !SETTINGS[:ssl_key].empty?
res.cert = OpenSSL::X509::Certificate.new(File.read(SETTINGS[:ssl_cert]))
res.key = OpenSSL::PKey::RSA.new(File.read(SETTINGS[:ssl_key]), nil)
end
end
res.start { |http| http.request(req) }
cache("#{certname}-push-facts", "Facts from this host were last pushed to #{uri} at #{Time.now}\n")
req
rescue => e
raise "Could not send facts to Foreman: #{e}"
raise "Could not generate facts for Foreman: #{e}"
end
end
end
......
res.body
end
def upload_facts(certname, req)
uri = URI.parse("#{url}/api/hosts/facts")
begin
res = initialize_http(uri)
res.start { |http| http.request(req) }
cache("#{certname}-push-facts", "Facts from this host were last pushed to #{uri} at #{Time.now}\n")
rescue => e
raise "Could not send facts to Foreman: #{e}"
end
end
def upload_facts_parallel(http_fact_requests)
thread_count.times.map {
Thread.new(http_fact_requests) do |fact_requests|
while factref = fact_requests.pop
certname = factref[0]
httpobj = factref[1]
if httpobj
upload_facts(certname, httpobj)
end
end
end
}.each(&:join)
end
# Actual code starts here
if __FILE__ == $0 then
......
no_env = ARGV.delete("--no-environment")
if ARGV.delete("--push-facts")
# push all facts files to Foreman and don't act as an ENC
upload_all_facts
process_all_facts(false)
elsif ARGV.delete("--push-facts-parallel")
http_fact_requests = Http_Fact_Requests.new
process_all_facts(http_fact_requests)
upload_facts_parallel(http_fact_requests)
else
certname = ARGV[0] || raise("Must provide certname as an argument")
# send facts to Foreman - enable 'facts' setting to activate
# if you use this option below, make sure that you don't send facts to foreman via the rake task or push facts alternatives.
#
if SETTINGS[:facts]
upload_facts certname, "#{puppetdir}/yaml/facts/#{certname}.yaml"
req = generate_fact_request certname, "#{puppetdir}/yaml/facts/#{certname}.yaml"
upload_facts(certname, req)
end
#
# query External node

Also available in: Unified diff