smart_proxy_abrt 0.0.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 +7 -0
- data/LICENSE +674 -0
- data/README +4 -0
- data/Rakefile +25 -0
- data/bin/smart-proxy-abrt-send +92 -0
- data/bundler.d/abrt.rb +6 -0
- data/extra/foreman-proxy-abrt-send.cron +2 -0
- data/extra/rubygem-smart_proxy_abrt.spec +110 -0
- data/lib/smart_proxy_abrt.rb +3 -0
- data/lib/smart_proxy_abrt/abrt_api.rb +76 -0
- data/lib/smart_proxy_abrt/abrt_lib.rb +244 -0
- data/lib/smart_proxy_abrt/abrt_plugin.rb +12 -0
- data/lib/smart_proxy_abrt/abrt_version.rb +5 -0
- data/lib/smart_proxy_abrt/http_config.ru +5 -0
- data/settings.d/abrt.yml.example +26 -0
- data/test/abrt_api_test.rb +90 -0
- data/test/abrt_test.rb +125 -0
- data/test/fixtures/ureport-ondisk-host1-01 +1 -0
- data/test/fixtures/ureport-ondisk-host1-02 +1 -0
- data/test/fixtures/ureport-ondisk-host1-03 +1 -0
- data/test/fixtures/ureport-ondisk-host2-01 +1 -0
- data/test/fixtures/ureport1.json +45 -0
- data/test/test_helper.rb +10 -0
- metadata +67 -0
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
# Test for 1.9
|
5
|
+
if (RUBY_VERSION.split('.').map{|s|s.to_i} <=> [1,9,0]) > 0 then
|
6
|
+
PLATFORM = RUBY_PLATFORM
|
7
|
+
end
|
8
|
+
|
9
|
+
desc 'Default: run unit tests.'
|
10
|
+
task :default => :test
|
11
|
+
|
12
|
+
desc 'Test the Foreman Proxy plugin.'
|
13
|
+
Rake::TestTask.new(:test) do |t|
|
14
|
+
t.libs << '.'
|
15
|
+
t.libs << 'lib'
|
16
|
+
t.libs << 'test'
|
17
|
+
files = FileList['test/**/*_test.rb']
|
18
|
+
if PLATFORM =~ /mingw/
|
19
|
+
files = FileList['test/**/server_ms_test*']
|
20
|
+
else
|
21
|
+
files = FileList['test/**/*_test.rb'].delete_if{|f| f =~ /_ms_/}
|
22
|
+
end
|
23
|
+
t.test_files = files
|
24
|
+
t.verbose = true
|
25
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift '/usr/share/foreman-proxy/lib' #XXX!
|
4
|
+
$LOAD_PATH.unshift '/usr/share/foreman-proxy/modules' #XXX!
|
5
|
+
|
6
|
+
# We rely on the main smart_proxy module to initialize the plugins so that we
|
7
|
+
# can access our module's settings. Also used from smart-proxy code: global
|
8
|
+
# settings, logging, foreman requests.
|
9
|
+
require 'smart_proxy'
|
10
|
+
|
11
|
+
require 'smart_proxy_abrt'
|
12
|
+
require 'smart_proxy_abrt/abrt_lib'
|
13
|
+
|
14
|
+
# Substitute our own logger so that we don't log to the main smart-proxy logfile.
|
15
|
+
module Proxy
|
16
|
+
module Log
|
17
|
+
@@logger = ::Logger.new(Proxy::Abrt::Plugin.settings.abrt_send_log_file, 6, 1024*1024*10)
|
18
|
+
@@logger.level = ::Logger.const_get(Proxy::SETTINGS.log_level.upcase)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
include Proxy::Log
|
22
|
+
|
23
|
+
# Don't run if ABRT plugin is disabled.
|
24
|
+
exit unless Proxy::Abrt::Plugin.settings.enabled == true
|
25
|
+
|
26
|
+
if !Proxy::SETTINGS.foreman_url
|
27
|
+
logger.error "Foreman URL not configured"
|
28
|
+
exit false
|
29
|
+
end
|
30
|
+
|
31
|
+
begin
|
32
|
+
require 'satyr' if Proxy::Abrt::Plugin.settings.aggregate_reports
|
33
|
+
rescue LoadError
|
34
|
+
logger.error "The satyr gem required for report aggregation was not found. "\
|
35
|
+
"You need to either install it or disable the aggregation."
|
36
|
+
end
|
37
|
+
|
38
|
+
def send_reports_from_spool
|
39
|
+
reports_by_host = {}
|
40
|
+
|
41
|
+
# load reports from disk
|
42
|
+
reports = Proxy::Abrt::HostReport.load_from_spool
|
43
|
+
|
44
|
+
# aggregate reports by host and by duplication hash if possible
|
45
|
+
reports.each do |report|
|
46
|
+
if reports_by_host.has_key? report.host
|
47
|
+
begin
|
48
|
+
reports_by_host[report.host].merge report
|
49
|
+
rescue StandardError => e
|
50
|
+
logger.error "Failed to merge #{report.files[0]} " \
|
51
|
+
"into #{reports_by_host[report.host].files}: #{e}"
|
52
|
+
end
|
53
|
+
else
|
54
|
+
reports_by_host[report.host] = report
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# send reports
|
59
|
+
reports_by_host.each_value do |hr|
|
60
|
+
begin
|
61
|
+
result = hr.send_to_foreman
|
62
|
+
rescue StandardError => e
|
63
|
+
logger.error "Unable to forward to Foreman server: #{e}"
|
64
|
+
next
|
65
|
+
end
|
66
|
+
unless result.is_a? Net::HTTPSuccess
|
67
|
+
logger.error "Foreman server rejected report (status #{result.code}): #{result.body}"
|
68
|
+
end
|
69
|
+
begin
|
70
|
+
hr.unlink
|
71
|
+
rescue StandardError => e
|
72
|
+
logger.error "Cannot delete #{hr.files}: #{e}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
if ARGV[0] == "--daemon"
|
78
|
+
if PLATFORM =~ /mingw/
|
79
|
+
puts "Daemon mode is not supported on Windows."
|
80
|
+
exit false
|
81
|
+
end
|
82
|
+
require 'daemon'
|
83
|
+
Process.daemon true
|
84
|
+
|
85
|
+
sleep_interval = Proxy::Abrt::Plugin.settings.abrt_send_period || 600
|
86
|
+
loop do
|
87
|
+
send_reports_from_spool
|
88
|
+
sleep sleep_interval
|
89
|
+
end
|
90
|
+
else
|
91
|
+
send_reports_from_spool
|
92
|
+
end
|
data/bundler.d/abrt.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
%global gem_name smart_proxy_abrt
|
2
|
+
|
3
|
+
%global foreman_proxy_bundlerd_dir /usr/share/foreman-proxy/bundler.d
|
4
|
+
%global foreman_proxy_pluginconf_dir /etc/foreman-proxy/settings.d
|
5
|
+
%global spool_dir /var/spool/foreman-proxy-abrt
|
6
|
+
|
7
|
+
Name: rubygem-%{gem_name}
|
8
|
+
Version: 0.0.1
|
9
|
+
Release: 1%{?dist}
|
10
|
+
Summary: Automatic Bug Reporting Tool plugin for Foreman's smart proxy
|
11
|
+
Group: Applications/Internet
|
12
|
+
License: GPLv3
|
13
|
+
URL: http://github.com/abrt/smart-proxy-abrt
|
14
|
+
Source0: https://fedorahosted.org/released/abrt/%{gem_name}-%{version}.gem
|
15
|
+
Requires: ruby(release)
|
16
|
+
Requires: ruby(rubygems)
|
17
|
+
Requires: rubygem(ffi)
|
18
|
+
Requires: foreman-proxy >= 1.6.0
|
19
|
+
Requires: crontabs
|
20
|
+
## does not exist in repository yet
|
21
|
+
#Requires: rubygem-satyr
|
22
|
+
BuildRequires: ruby(release)
|
23
|
+
BuildRequires: rubygems-devel
|
24
|
+
BuildRequires: ruby
|
25
|
+
BuildRequires: rubygem(ffi)
|
26
|
+
BuildRequires: rubygem(minitest)
|
27
|
+
BuildArch: noarch
|
28
|
+
Provides: rubygem(%{gem_name}) = %{version}
|
29
|
+
|
30
|
+
%description
|
31
|
+
This smart proxy plugin, together with a Foreman plugin, add the capability to
|
32
|
+
send ABRT micro-reports from your managed hosts to Foreman.
|
33
|
+
|
34
|
+
%package doc
|
35
|
+
Summary: Documentation for %{name}
|
36
|
+
Group: Documentation
|
37
|
+
Requires:%{name} = %{version}-%{release}
|
38
|
+
|
39
|
+
%description doc
|
40
|
+
Documentation for %{name}
|
41
|
+
|
42
|
+
%prep
|
43
|
+
gem unpack %{SOURCE0}
|
44
|
+
%setup -q -D -T -n %{gem_name}-%{version}
|
45
|
+
gem spec %{SOURCE0} -l --ruby > %{gem_name}.gemspec
|
46
|
+
|
47
|
+
%build
|
48
|
+
# Create the gem as gem install only works on a gem file
|
49
|
+
gem build %{gem_name}.gemspec
|
50
|
+
|
51
|
+
# %%gem_install compiles any C extensions and installs the gem into ./%gem_dir
|
52
|
+
# by default, so that we can move it into the buildroot in %%install
|
53
|
+
%gem_install
|
54
|
+
|
55
|
+
%install
|
56
|
+
# Packaging guidelines say: Do not ship tests
|
57
|
+
rm -r .%{gem_instdir}/test .%{gem_instdir}/Rakefile
|
58
|
+
rm .%{gem_instdir}/extra/*.spec
|
59
|
+
|
60
|
+
mkdir -p %{buildroot}%{gem_dir}
|
61
|
+
cp -a .%{gem_dir}/* \
|
62
|
+
%{buildroot}%{gem_dir}/
|
63
|
+
|
64
|
+
# executables
|
65
|
+
mkdir -p %{buildroot}%{_bindir}
|
66
|
+
cp -a .%{_bindir}/* \
|
67
|
+
%{buildroot}%{_bindir}
|
68
|
+
|
69
|
+
# bundler file
|
70
|
+
mkdir -p %{buildroot}%{foreman_proxy_bundlerd_dir}
|
71
|
+
mv %{buildroot}%{gem_instdir}/bundler.d/abrt.rb \
|
72
|
+
%{buildroot}%{foreman_proxy_bundlerd_dir}
|
73
|
+
|
74
|
+
# sample config
|
75
|
+
mkdir -p %{buildroot}%{foreman_proxy_pluginconf_dir}
|
76
|
+
mv %{buildroot}%{gem_instdir}/settings.d/abrt.yml.example \
|
77
|
+
%{buildroot}%{foreman_proxy_pluginconf_dir}/
|
78
|
+
|
79
|
+
# crontab
|
80
|
+
mkdir -p %{buildroot}%{_sysconfdir}/cron.d/
|
81
|
+
mv %{buildroot}%{gem_instdir}/extra/foreman-proxy-abrt-send.cron \
|
82
|
+
%{buildroot}%{_sysconfdir}/cron.d/%{name}
|
83
|
+
|
84
|
+
# create spool directory
|
85
|
+
mkdir -p %{buildroot}%{spool_dir}
|
86
|
+
|
87
|
+
#%check
|
88
|
+
#testrb -Ilib test
|
89
|
+
|
90
|
+
%files
|
91
|
+
%dir %{gem_instdir}
|
92
|
+
%{gem_libdir}
|
93
|
+
%exclude %{gem_cache}
|
94
|
+
%{gem_spec}
|
95
|
+
%{gem_instdir}/bin
|
96
|
+
|
97
|
+
%dir %attr(0755, foreman-proxy, foreman-proxy) %{spool_dir}
|
98
|
+
%{foreman_proxy_bundlerd_dir}/abrt.rb
|
99
|
+
%{_bindir}/smart-proxy-abrt-send
|
100
|
+
%doc %{foreman_proxy_pluginconf_dir}/abrt.yml.example
|
101
|
+
%config(noreplace) %{_sysconfdir}/cron.d/%{name}
|
102
|
+
|
103
|
+
%files doc
|
104
|
+
%{gem_docdir}
|
105
|
+
%{gem_instdir}/README
|
106
|
+
%{gem_instdir}/LICENSE
|
107
|
+
|
108
|
+
%changelog
|
109
|
+
* Tue Jul 15 2014 Martin Milata <mmilata@redhat.com> - 0.0.1-1
|
110
|
+
- Initial package
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
require 'smart_proxy_abrt/abrt_lib'
|
5
|
+
|
6
|
+
STATUS_ACCEPTED = 202
|
7
|
+
|
8
|
+
module Proxy::Abrt
|
9
|
+
class Api < ::Sinatra::Base
|
10
|
+
include ::Proxy::Log
|
11
|
+
helpers ::Proxy::Helpers
|
12
|
+
|
13
|
+
post '/reports/new/' do
|
14
|
+
begin
|
15
|
+
cn = Proxy::Abrt::common_name request
|
16
|
+
rescue Proxy::Error::Unauthorized => e
|
17
|
+
log_halt 403, "Client authentication failed: #{e.message}"
|
18
|
+
end
|
19
|
+
|
20
|
+
ureport_json = request['file'][:tempfile].read
|
21
|
+
ureport = JSON.parse(ureport_json)
|
22
|
+
|
23
|
+
#forward to FAF
|
24
|
+
response = nil
|
25
|
+
if Proxy::Abrt::Plugin.settings.server_url
|
26
|
+
begin
|
27
|
+
result = Proxy::Abrt::faf_request "/reports/new/", ureport_json
|
28
|
+
response = result.body if result.code.to_s == STATUS_ACCEPTED.to_s
|
29
|
+
rescue StandardError => e
|
30
|
+
logger.error "Unable to forward to ABRT server: #{e}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
unless response
|
34
|
+
# forwarding is not configured or failed
|
35
|
+
# FAF source that generates replies is in src/webfaf/reports/views.py
|
36
|
+
response = { "result" => false,
|
37
|
+
"message" => "Report queued" }
|
38
|
+
if Proxy::SETTINGS.foreman_url
|
39
|
+
foreman_url = Proxy::SETTINGS.foreman_url
|
40
|
+
foreman_url += "/" if url[-1] != "/"
|
41
|
+
foreman_url += "hosts/#{cn}/abrt_reports"
|
42
|
+
response["reported_to"] = [{ "reporter" => "Foreman",
|
43
|
+
"type" => "url",
|
44
|
+
"value" => foreman_url }]
|
45
|
+
end
|
46
|
+
response = response.to_json
|
47
|
+
end
|
48
|
+
|
49
|
+
#save report to disk
|
50
|
+
begin
|
51
|
+
Proxy::Abrt::HostReport.save cn, ureport
|
52
|
+
rescue StandardError => e
|
53
|
+
log_halt 500, "Failed to save the report: #{e}"
|
54
|
+
end
|
55
|
+
|
56
|
+
status STATUS_ACCEPTED
|
57
|
+
response
|
58
|
+
end
|
59
|
+
|
60
|
+
post '/reports/:action/' do
|
61
|
+
# pass through to real FAF if configured
|
62
|
+
if Proxy::Abrt::Plugin.settings.server_url
|
63
|
+
body = request['file'][:tempfile].read
|
64
|
+
begin
|
65
|
+
result = Proxy::Abrt::faf_request "/reports/#{params[:action]}/", body
|
66
|
+
rescue StandardError => e
|
67
|
+
log_halt 503, "ABRT server unavailable: #{e}"
|
68
|
+
end
|
69
|
+
status result.code
|
70
|
+
result.body
|
71
|
+
else
|
72
|
+
log_halt 501, "foreman-proxy does not implement /reports/#{params[:action]}/"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
require 'uri'
|
4
|
+
require 'time'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
require 'proxy/log'
|
8
|
+
require 'proxy/request'
|
9
|
+
|
10
|
+
module Proxy::Abrt
|
11
|
+
def self.random_alpha_string(length)
|
12
|
+
base = ('a'..'z').to_a
|
13
|
+
result = ""
|
14
|
+
length.times { result << base[rand(base.size)] }
|
15
|
+
result
|
16
|
+
end
|
17
|
+
|
18
|
+
# Generate multipart boundary separator
|
19
|
+
def self.suggest_separator
|
20
|
+
separator = "-"*28
|
21
|
+
separator + self.random_alpha_string(16)
|
22
|
+
end
|
23
|
+
|
24
|
+
# It seems that Net::HTTP does not support multipart/form-data - this function
|
25
|
+
# is adapted from http://stackoverflow.com/a/213276 and lib/proxy/request.rb
|
26
|
+
def self.form_data_file(content, file_content_type)
|
27
|
+
# Assemble the request body using the special multipart format
|
28
|
+
thepart = "Content-Disposition: form-data; name=\"file\"; filename=\"*buffer*\"\r\n" +
|
29
|
+
"Content-Type: #{ file_content_type }\r\n\r\n#{ content }\r\n"
|
30
|
+
|
31
|
+
boundary = self.suggest_separator
|
32
|
+
while thepart.include? boundary
|
33
|
+
boundary = self.suggest_separator
|
34
|
+
end
|
35
|
+
|
36
|
+
body = "--" + boundary + "\r\n" + thepart + "--" + boundary + "--\r\n"
|
37
|
+
headers = {
|
38
|
+
"User-Agent" => "foreman-proxy/#{Proxy::VERSION}",
|
39
|
+
"Content-Type" => "multipart/form-data; boundary=#{ boundary }",
|
40
|
+
"Content-Length" => body.length.to_s
|
41
|
+
}
|
42
|
+
|
43
|
+
return headers, body
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.faf_request(path, content, content_type="application/json")
|
47
|
+
uri = URI.parse(Proxy::Abrt::Plugin.settings.server_url.to_s)
|
48
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
49
|
+
http.use_ssl = uri.scheme == 'https'
|
50
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
51
|
+
|
52
|
+
if Proxy::Abrt::Plugin.settings.server_ssl_noverify
|
53
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
54
|
+
end
|
55
|
+
|
56
|
+
if Proxy::Abrt::Plugin.settings.server_ssl_cert && !Proxy::Abrt::Plugin.settings.server_ssl_cert.to_s.empty? \
|
57
|
+
&& Proxy::Abrt::Plugin.settings.server_ssl_key && !Proxy::Abrt::Plugin.settings.server_ssl_key.to_s.empty?
|
58
|
+
http.cert = OpenSSL::X509::Certificate.new(File.read(Proxy::Abrt::Plugin.settings.server_ssl_cert))
|
59
|
+
http.key = OpenSSL::PKey::RSA.new(File.read(Proxy::Abrt::Plugin.settings.server_ssl_key), nil)
|
60
|
+
end
|
61
|
+
|
62
|
+
headers, body = self.form_data_file content, content_type
|
63
|
+
|
64
|
+
path = [uri.path, path].join unless uri.path.empty?
|
65
|
+
response = http.start { |con| con.post(path, body, headers) }
|
66
|
+
|
67
|
+
response
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.common_name(request)
|
71
|
+
client_cert = request.env['SSL_CLIENT_CERT']
|
72
|
+
raise Proxy::Error::Unauthorized, "Client certificate required" if client_cert.to_s.empty?
|
73
|
+
|
74
|
+
begin
|
75
|
+
client_cert = OpenSSL::X509::Certificate.new(client_cert)
|
76
|
+
rescue OpenSSL::OpenSSLError => e
|
77
|
+
raise Proxy::Error::Unauthorized, e.message
|
78
|
+
end
|
79
|
+
|
80
|
+
cn = client_cert.subject.to_a.detect { |name, value| name == 'CN' }
|
81
|
+
cn = cn[1] unless cn.nil?
|
82
|
+
raise Proxy::Error::Unauthorized, "Common Name not found in the certificate" unless cn
|
83
|
+
|
84
|
+
return cn
|
85
|
+
end
|
86
|
+
|
87
|
+
class HostReport
|
88
|
+
include Proxy::Log
|
89
|
+
|
90
|
+
class AggregatedReport
|
91
|
+
attr_accessor :report, :count, :hash, :reported_at
|
92
|
+
def initialize(report, count, hash, reported_at)
|
93
|
+
@report = report
|
94
|
+
@count = count
|
95
|
+
@hash = hash
|
96
|
+
@reported_at = Time.parse reported_at
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class Error < RuntimeError; end
|
101
|
+
|
102
|
+
attr_reader :host, :reports, :files, :by_hash
|
103
|
+
|
104
|
+
def initialize(fname)
|
105
|
+
contents = IO.read(fname)
|
106
|
+
json = JSON.parse(contents)
|
107
|
+
|
108
|
+
report = json["report"]
|
109
|
+
hash = HostReport.duphash report
|
110
|
+
ar = AggregatedReport.new(json["report"], 1, hash, json["reported_at"])
|
111
|
+
@reports = [ar]
|
112
|
+
# index the array elements by duphash, if they have one
|
113
|
+
@by_hash = {}
|
114
|
+
@by_hash[hash] = ar unless hash.nil?
|
115
|
+
@files = [fname]
|
116
|
+
@host = json["host"]
|
117
|
+
end
|
118
|
+
|
119
|
+
def merge(other)
|
120
|
+
raise HostReport::Error, "Host names do not match" unless @host == other.host
|
121
|
+
|
122
|
+
other.reports.each do |ar|
|
123
|
+
if !ar.hash.nil? && @by_hash.has_key?(ar.hash)
|
124
|
+
# we already have this report, just increment the counter
|
125
|
+
found_report = @by_hash[ar.hash]
|
126
|
+
found_report.count += ar.count
|
127
|
+
found_report.reported_at = [found_report.reported_at, ar.reported_at].min
|
128
|
+
else
|
129
|
+
# we either don't have this report or it has no hash
|
130
|
+
@reports << ar
|
131
|
+
@by_hash[ar.hash] = ar unless ar.hash.nil?
|
132
|
+
end
|
133
|
+
end
|
134
|
+
@files += other.files
|
135
|
+
end
|
136
|
+
|
137
|
+
def send_to_foreman
|
138
|
+
foreman_report = create_foreman_report
|
139
|
+
logger.debug "Sending #{foreman_report}"
|
140
|
+
Proxy::HttpRequest::ForemanRequest.new.send_request("/api/abrt_reports", foreman_report.to_json)
|
141
|
+
end
|
142
|
+
|
143
|
+
def unlink
|
144
|
+
@files.each do |fname|
|
145
|
+
logger.debug "Deleting #{fname}"
|
146
|
+
File.unlink(fname)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.save(host, report, reported_at=nil)
|
151
|
+
# create the spool dir if it does not exist
|
152
|
+
FileUtils.mkdir_p HostReport.spooldir
|
153
|
+
|
154
|
+
reported_at ||= Time.now.utc
|
155
|
+
on_disk_report = { "host" => host, "report" => report , "reported_at" => reported_at.to_s }
|
156
|
+
|
157
|
+
# write report to temporary file
|
158
|
+
temp_fname = with_unique_filename "new-" do |temp_fname|
|
159
|
+
File.open temp_fname, File::WRONLY|File::CREAT|File::EXCL do |tmpfile|
|
160
|
+
tmpfile.write(on_disk_report.to_json)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# rename it
|
165
|
+
with_unique_filename ("ureport-" + DateTime.now.iso8601 + "-") do |final_fname|
|
166
|
+
File.link temp_fname, final_fname
|
167
|
+
File.unlink temp_fname
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def self.load_from_spool
|
172
|
+
reports = []
|
173
|
+
report_files = Dir[File.join(HostReport.spooldir, "ureport-*")]
|
174
|
+
report_files.each do |fname|
|
175
|
+
begin
|
176
|
+
reports << new(fname)
|
177
|
+
rescue StandardError => e
|
178
|
+
logger.error "Failed to parse report #{fname}: #{e}"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
reports
|
182
|
+
end
|
183
|
+
|
184
|
+
private
|
185
|
+
|
186
|
+
def format_reports
|
187
|
+
@reports.collect do |ar|
|
188
|
+
r = {
|
189
|
+
"count" => ar.count,
|
190
|
+
"reported_at" => ar.reported_at.utc.to_s,
|
191
|
+
"full" => ar.report
|
192
|
+
}
|
193
|
+
r["duphash"] = ar.hash unless ar.hash.nil?
|
194
|
+
r
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# http://projects.theforeman.org/projects/foreman/wiki/Json-report-format
|
199
|
+
# To be replaced once Foreman understands other report types than from Puppet.
|
200
|
+
def create_foreman_report
|
201
|
+
{ "abrt_report" => {
|
202
|
+
"host" => @host,
|
203
|
+
"reports" => format_reports
|
204
|
+
}
|
205
|
+
}
|
206
|
+
end
|
207
|
+
|
208
|
+
def self.duphash(report)
|
209
|
+
return nil if !Proxy::Abrt::Plugin.settings.aggregate_reports
|
210
|
+
|
211
|
+
begin
|
212
|
+
satyr_report = Satyr::Report.new report.to_json
|
213
|
+
stacktrace = satyr_report.stacktrace
|
214
|
+
thread = stacktrace.find_crash_thread
|
215
|
+
thread.duphash
|
216
|
+
rescue StandardError => e
|
217
|
+
logger.error "Error computing duphash: #{e}"
|
218
|
+
nil
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def self.unique_filename(prefix)
|
223
|
+
File.join(HostReport.spooldir, prefix + Proxy::Abrt::random_alpha_string(8))
|
224
|
+
end
|
225
|
+
|
226
|
+
def self.with_unique_filename(prefix)
|
227
|
+
filename = unique_filename prefix
|
228
|
+
tries_left = 5
|
229
|
+
begin
|
230
|
+
yield filename
|
231
|
+
rescue Errno::EEXIST => e
|
232
|
+
filename = unique_filename prefix
|
233
|
+
tries_left -= 1
|
234
|
+
retry if tries_left > 0
|
235
|
+
raise HostReport::Error, "Unable to create unique file"
|
236
|
+
end
|
237
|
+
filename
|
238
|
+
end
|
239
|
+
|
240
|
+
def self.spooldir
|
241
|
+
Proxy::Abrt::Plugin.settings.spooldir || File.join(APP_ROOT, "spool/foreman-proxy-abrt")
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|