ruby-grafana-reporter 0.1.7 → 0.4.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.
- checksums.yaml +4 -4
- data/README.md +86 -245
- data/bin/ruby-grafana-reporter +3 -2
- data/lib/VERSION.rb +6 -3
- data/lib/grafana/abstract_datasource.rb +116 -0
- data/lib/grafana/dashboard.rb +75 -66
- data/lib/grafana/errors.rb +81 -61
- data/lib/grafana/grafana.rb +130 -131
- data/lib/grafana/grafana_alerts_datasource.rb +57 -0
- data/lib/grafana/grafana_annotations_datasource.rb +56 -0
- data/lib/grafana/grafana_property_datasource.rb +25 -0
- data/lib/grafana/graphite_datasource.rb +44 -0
- data/lib/grafana/image_rendering_datasource.rb +44 -0
- data/lib/grafana/panel.rb +47 -39
- data/lib/grafana/prometheus_datasource.rb +39 -0
- data/lib/grafana/sql_datasource.rb +65 -0
- data/lib/grafana/variable.rb +218 -259
- data/lib/grafana/webrequest.rb +71 -0
- data/lib/grafana_reporter/abstract_query.rb +401 -0
- data/lib/grafana_reporter/abstract_report.rb +163 -109
- data/lib/grafana_reporter/alerts_table_query.rb +44 -0
- data/lib/grafana_reporter/annotations_table_query.rb +43 -0
- data/lib/grafana_reporter/application/application.rb +162 -229
- data/lib/grafana_reporter/application/errors.rb +33 -30
- data/lib/grafana_reporter/application/webservice.rb +242 -0
- data/lib/grafana_reporter/asciidoctor/alerts_table_include_processor.rb +90 -0
- data/lib/grafana_reporter/asciidoctor/annotations_table_include_processor.rb +89 -0
- data/lib/grafana_reporter/asciidoctor/panel_image_block_macro.rb +76 -0
- data/lib/grafana_reporter/asciidoctor/panel_image_inline_macro.rb +77 -0
- data/lib/grafana_reporter/asciidoctor/panel_property_inline_macro.rb +72 -0
- data/lib/grafana_reporter/asciidoctor/panel_query_table_include_processor.rb +98 -0
- data/lib/grafana_reporter/asciidoctor/panel_query_value_inline_macro.rb +93 -0
- data/lib/grafana_reporter/asciidoctor/processor_mixin.rb +23 -0
- data/lib/grafana_reporter/asciidoctor/report.rb +172 -159
- data/lib/grafana_reporter/asciidoctor/show_environment_include_processor.rb +46 -0
- data/lib/grafana_reporter/asciidoctor/show_help_include_processor.rb +35 -0
- data/lib/grafana_reporter/asciidoctor/sql_table_include_processor.rb +92 -0
- data/lib/grafana_reporter/asciidoctor/sql_value_inline_macro.rb +88 -0
- data/lib/grafana_reporter/asciidoctor/value_as_variable_include_processor.rb +90 -0
- data/lib/grafana_reporter/configuration.rb +310 -326
- data/lib/grafana_reporter/console_configuration_wizard.rb +319 -0
- data/lib/grafana_reporter/demo_report_wizard.rb +87 -0
- data/lib/grafana_reporter/errors.rb +81 -38
- data/lib/grafana_reporter/help.rb +447 -0
- data/lib/grafana_reporter/logger/two_way_logger.rb +58 -52
- data/lib/grafana_reporter/panel_image_query.rb +29 -0
- data/lib/grafana_reporter/panel_property_query.rb +22 -0
- data/lib/grafana_reporter/query_value_query.rb +79 -0
- data/lib/grafana_reporter/report_webhook.rb +35 -0
- data/lib/{ruby-grafana-reporter.rb → ruby_grafana_reporter.rb} +29 -27
- metadata +48 -60
- data/lib/grafana/abstract_panel_query.rb +0 -20
- data/lib/grafana/abstract_query.rb +0 -127
- data/lib/grafana/abstract_sql_query.rb +0 -42
- data/lib/grafana/panel_image_query.rb +0 -49
- data/lib/grafana_reporter/asciidoctor/alerts_table_query.rb +0 -99
- data/lib/grafana_reporter/asciidoctor/annotations_table_query.rb +0 -96
- data/lib/grafana_reporter/asciidoctor/errors.rb +0 -37
- data/lib/grafana_reporter/asciidoctor/extensions/alerts_table_include_processor.rb +0 -86
- data/lib/grafana_reporter/asciidoctor/extensions/annotations_table_include_processor.rb +0 -86
- data/lib/grafana_reporter/asciidoctor/extensions/panel_image_block_macro.rb +0 -67
- data/lib/grafana_reporter/asciidoctor/extensions/panel_image_inline_macro.rb +0 -65
- data/lib/grafana_reporter/asciidoctor/extensions/panel_property_inline_macro.rb +0 -58
- data/lib/grafana_reporter/asciidoctor/extensions/panel_query_table_include_processor.rb +0 -75
- data/lib/grafana_reporter/asciidoctor/extensions/panel_query_value_inline_macro.rb +0 -70
- data/lib/grafana_reporter/asciidoctor/extensions/processor_mixin.rb +0 -18
- data/lib/grafana_reporter/asciidoctor/extensions/show_environment_include_processor.rb +0 -41
- data/lib/grafana_reporter/asciidoctor/extensions/show_help_include_processor.rb +0 -202
- data/lib/grafana_reporter/asciidoctor/extensions/sql_table_include_processor.rb +0 -67
- data/lib/grafana_reporter/asciidoctor/extensions/sql_value_inline_macro.rb +0 -65
- data/lib/grafana_reporter/asciidoctor/extensions/value_as_variable_include_processor.rb +0 -57
- data/lib/grafana_reporter/asciidoctor/panel_first_value_query.rb +0 -32
- data/lib/grafana_reporter/asciidoctor/panel_image_query.rb +0 -23
- data/lib/grafana_reporter/asciidoctor/panel_property_query.rb +0 -43
- data/lib/grafana_reporter/asciidoctor/panel_table_query.rb +0 -36
- data/lib/grafana_reporter/asciidoctor/query_mixin.rb +0 -309
- data/lib/grafana_reporter/asciidoctor/sql_first_value_query.rb +0 -34
- data/lib/grafana_reporter/asciidoctor/sql_table_query.rb +0 -32
@@ -1,30 +1,33 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GrafanaReporter
|
4
|
+
module Application
|
5
|
+
# General grafana application error, from which the specific errors
|
6
|
+
# inherit.
|
7
|
+
class ApplicationError < GrafanaReporterError
|
8
|
+
end
|
9
|
+
|
10
|
+
# Thrown, if the '-s' parameter is not configured with exactly one variable
|
11
|
+
# name and one value.
|
12
|
+
class ParameterValueError < ApplicationError
|
13
|
+
def initialize(length)
|
14
|
+
super("Parameter '-s' needs exactly two values separated by comma, received #{length}.")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Thrown, if a webservice request has been requested, which could not be
|
19
|
+
# handled.
|
20
|
+
class WebserviceUnknownPathError < ApplicationError
|
21
|
+
def initialize(request)
|
22
|
+
super("Request '#{request}' calls an unknown path for this webservice.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Thrown, if an internal error appeared during creation of the report.
|
27
|
+
class WebserviceGeneralRenderingError < ApplicationError
|
28
|
+
def initialize(error)
|
29
|
+
super("Could not render report because of internal error: #{error}")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,242 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GrafanaReporter
|
4
|
+
module Application
|
5
|
+
# This class provides the webservice for the reporter application. It does not
|
6
|
+
# make use of `webrick` or similar, so that it can be used without futher dependencies
|
7
|
+
# in conjunction with the standard asciidoctor docker container.
|
8
|
+
class Webservice
|
9
|
+
def initialize
|
10
|
+
@reports = []
|
11
|
+
@running = false
|
12
|
+
end
|
13
|
+
|
14
|
+
# Runs the webservice with the given {Configuration} object.
|
15
|
+
def run(config)
|
16
|
+
@config = config
|
17
|
+
@logger = config.logger
|
18
|
+
|
19
|
+
# start webserver
|
20
|
+
@server = TCPServer.new(@config.webserver_port)
|
21
|
+
@logger.info("Server listening on port #{@config.webserver_port}...")
|
22
|
+
@running = true
|
23
|
+
|
24
|
+
@progress_reporter = Thread.new {}
|
25
|
+
|
26
|
+
accept_requests_loop
|
27
|
+
@running = false
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return True, if webservice is up and running, false otherwise
|
31
|
+
def running?
|
32
|
+
@running
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def accept_requests_loop
|
38
|
+
loop do
|
39
|
+
# step 1) accept incoming connection
|
40
|
+
socket = @server.accept
|
41
|
+
|
42
|
+
# step 2) print the request headers (separated by a blank line e.g. \r\n)
|
43
|
+
request = ''
|
44
|
+
line = ''
|
45
|
+
begin
|
46
|
+
until line == "\r\n"
|
47
|
+
line = socket.readline
|
48
|
+
request += line
|
49
|
+
end
|
50
|
+
rescue EOFError => e
|
51
|
+
@logger.debug("Webserver EOFError: #{e.message}")
|
52
|
+
end
|
53
|
+
|
54
|
+
begin
|
55
|
+
response = handle_request(request)
|
56
|
+
socket.write response
|
57
|
+
rescue WebserviceUnknownPathError => e
|
58
|
+
@logger.debug(e.message)
|
59
|
+
socket.write http_response(404, '', e.message)
|
60
|
+
rescue MissingTemplateError => e
|
61
|
+
@logger.error(e.message)
|
62
|
+
socket.write http_response(400, 'Bad Request', e.message)
|
63
|
+
rescue WebserviceGeneralRenderingError => e
|
64
|
+
@logger.fatal(e.message)
|
65
|
+
socket.write http_response(400, 'Bad Request', e.message)
|
66
|
+
rescue StandardError => e
|
67
|
+
@logger.fatal(e.message)
|
68
|
+
socket.write http_response(400, 'Bad Request', e.message)
|
69
|
+
ensure
|
70
|
+
socket.close
|
71
|
+
end
|
72
|
+
|
73
|
+
log_report_progress
|
74
|
+
clean_outdated_temporary_reports
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def log_report_progress
|
79
|
+
return if @progress_reporter.alive?
|
80
|
+
|
81
|
+
@progress_reporter = Thread.new do
|
82
|
+
running_reports = @reports.reject(&:done)
|
83
|
+
until running_reports.empty?
|
84
|
+
unless running_reports.empty?
|
85
|
+
@logger.info("#{running_reports.length} report(s) in progress: "\
|
86
|
+
"#{running_reports.map do |report|
|
87
|
+
"#{(report.progress * 100).to_i}% (running #{report.execution_time.to_i} secs)"
|
88
|
+
end.join(', ')}")
|
89
|
+
end
|
90
|
+
sleep 5
|
91
|
+
running_reports = @reports.reject(&:done)
|
92
|
+
end
|
93
|
+
# puts "no more running reports - stopping to report progress"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def clean_outdated_temporary_reports
|
98
|
+
clean_time = Time.now - 60 * 60 * @config.report_retention
|
99
|
+
@reports.select { |report| report.done && clean_time > report.end_time }.each do |report|
|
100
|
+
@reports.delete(report).delete_file
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def handle_request(request)
|
105
|
+
raise WebserviceUnknownPathError, request.split("\r\n")[0] if request.nil?
|
106
|
+
raise WebserviceUnknownPathError, request.split("\r\n")[0] if request.split("\r\n")[0].nil?
|
107
|
+
|
108
|
+
query_string = request.split("\r\n")[0].gsub(%r{(?:[^?]+\?)(.*)(?: HTTP/.*)$}, '\1')
|
109
|
+
query_parameters = CGI.parse(query_string)
|
110
|
+
|
111
|
+
@logger.debug("Received request: #{request.split("\r\n")[0]}")
|
112
|
+
@logger.debug("query_parameters: #{query_parameters}")
|
113
|
+
|
114
|
+
# read URL parameters
|
115
|
+
attrs = {}
|
116
|
+
query_parameters.each do |k, v|
|
117
|
+
attrs[k] = v.length == 1 ? v[0] : v
|
118
|
+
end
|
119
|
+
|
120
|
+
case request.split("\r\n")[0]
|
121
|
+
when %r{^GET /render[? ]}
|
122
|
+
return render_report(attrs)
|
123
|
+
|
124
|
+
when %r{^GET /overview[? ]}
|
125
|
+
# show overview for current reports
|
126
|
+
return get_reports_status_as_html(@reports)
|
127
|
+
|
128
|
+
when %r{^GET /view_report[? ]}
|
129
|
+
return view_report(attrs)
|
130
|
+
|
131
|
+
when %r{^GET /cancel_report[? ]}
|
132
|
+
return cancel_report(attrs)
|
133
|
+
|
134
|
+
when %r{^GET /view_log[? ]}
|
135
|
+
return view_log(attrs)
|
136
|
+
end
|
137
|
+
|
138
|
+
raise WebserviceUnknownPathError, request.split("\r\n")[0]
|
139
|
+
end
|
140
|
+
|
141
|
+
def view_log(attrs)
|
142
|
+
# view report if already available, or show status view
|
143
|
+
report = @reports.select { |r| r.object_id.to_s == attrs['report_id'].to_s }.first
|
144
|
+
raise WebserviceGeneralRenderingError, 'view_log has been called without valid id' if report.nil?
|
145
|
+
|
146
|
+
content = report.full_log
|
147
|
+
|
148
|
+
http_response(200, 'OK', content, "Content-Type": 'text/plain')
|
149
|
+
end
|
150
|
+
|
151
|
+
def cancel_report(attrs)
|
152
|
+
# view report if already available, or show status view
|
153
|
+
report = @reports.select { |r| r.object_id.to_s == attrs['report_id'].to_s }.first
|
154
|
+
raise WebserviceGeneralRenderingError, 'cancel_report has been called without valid id' if report.nil?
|
155
|
+
|
156
|
+
report.cancel! unless report.done
|
157
|
+
|
158
|
+
# redirect to view_report page
|
159
|
+
http_response(302, 'Found', nil, Location: "/view_report?report_id=#{report.object_id}")
|
160
|
+
end
|
161
|
+
|
162
|
+
def view_report(attrs)
|
163
|
+
# view report if already available, or show status view
|
164
|
+
report = @reports.select { |r| r.object_id.to_s == attrs['report_id'].to_s }.first
|
165
|
+
raise WebserviceGeneralRenderingError, 'view_report has been called without valid id' if report.nil?
|
166
|
+
|
167
|
+
# show report status
|
168
|
+
return get_reports_status_as_html([report]) if !report.done || !report.error.empty?
|
169
|
+
|
170
|
+
# provide report
|
171
|
+
@logger.debug("Returning PDF report at #{report.path}")
|
172
|
+
content = File.read(report.path, mode: 'rb')
|
173
|
+
return http_response(200, 'OK', content, "Content-Type": 'application/pdf') if content.start_with?('%PDF')
|
174
|
+
|
175
|
+
http_response(200, 'OK', content, "Content-Type": 'application/octet-stream',
|
176
|
+
"Content-Disposition": 'attachment; '\
|
177
|
+
"filename=report_#{attrs['report_id']}.zip")
|
178
|
+
end
|
179
|
+
|
180
|
+
def render_report(attrs)
|
181
|
+
# build report
|
182
|
+
template_file = "#{@config.templates_folder}#{attrs['var-template']}.adoc"
|
183
|
+
|
184
|
+
file = Tempfile.new('gf_pdf_', @config.reports_folder)
|
185
|
+
begin
|
186
|
+
FileUtils.chmod('+r', file.path)
|
187
|
+
rescue StandardError => e
|
188
|
+
@logger.debug("File permissions could not be set for #{file.path}: #{e.message}")
|
189
|
+
end
|
190
|
+
|
191
|
+
report = @config.report_class.new(@config, template_file, file, attrs)
|
192
|
+
Thread.new do
|
193
|
+
report.create_report
|
194
|
+
end
|
195
|
+
@reports << report
|
196
|
+
|
197
|
+
http_response(302, 'Found', nil, Location: "/view_report?report_id=#{report.object_id}")
|
198
|
+
end
|
199
|
+
|
200
|
+
def get_reports_status_as_html(reports)
|
201
|
+
i = reports.length
|
202
|
+
|
203
|
+
content = '<html>'\
|
204
|
+
'<head></head>'\
|
205
|
+
'<body>'\
|
206
|
+
'<table>'\
|
207
|
+
'<thead>'\
|
208
|
+
'<th>#</th>'\
|
209
|
+
'<th>Start Time</th>'\
|
210
|
+
'<th>End Time</th>'\
|
211
|
+
'<th>Template</th>'\
|
212
|
+
'<th>Execution time</th>'\
|
213
|
+
'<th>Status</th>'\
|
214
|
+
'<th>Error</th>'\
|
215
|
+
'<th>Action</th>'\
|
216
|
+
'</thead>' +
|
217
|
+
reports.reverse.map do |report|
|
218
|
+
'<tr>'\
|
219
|
+
"<td>#{i -= 1}</td>"\
|
220
|
+
"<td>#{report.start_time}</td>"\
|
221
|
+
"<td>#{report.end_time}</td>"\
|
222
|
+
"<td>#{report.template}</td>"\
|
223
|
+
"<td>#{report.execution_time.to_i} secs</td>"\
|
224
|
+
"<td>#{report.status} (#{(report.progress * 100).to_i}%)</td>"\
|
225
|
+
"<td>#{report.error.join('<br>')}</td>"\
|
226
|
+
"<td>#{!report.done && !report.cancel ? "<a href=\"/cancel_report?report_id=#{report.object_id}\">Cancel</a> " : ''}"\
|
227
|
+
"#{(report.status == 'finished') || (report.status == 'cancelled') ? "<a href=\"/view_report?report_id=#{report.object_id}\">View</a> " : ' '}"\
|
228
|
+
"<a href=\"/view_log?report_id=#{report.object_id}\">Log</a></td>"\
|
229
|
+
'</tr>'
|
230
|
+
end.join('') +
|
231
|
+
'</table></body></html>'
|
232
|
+
|
233
|
+
http_response(200, 'OK', content, "Content-Type": 'text/html')
|
234
|
+
end
|
235
|
+
|
236
|
+
def http_response(code, text, body, opts = {})
|
237
|
+
"HTTP/1.1 #{code} #{text}\r\n#{opts.map { |k, v| "#{k}: #{v}" }.join("\r\n")}"\
|
238
|
+
"#{body ? "\r\nContent-Length: #{body.to_s.bytesize}" : ''}\r\n\r\n#{body}"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'processor_mixin'
|
4
|
+
|
5
|
+
module GrafanaReporter
|
6
|
+
module Asciidoctor
|
7
|
+
# Implements the hook
|
8
|
+
# include::grafana_alerts[<options>]
|
9
|
+
#
|
10
|
+
# Returns the results of alerts query as a asciidoctor table.
|
11
|
+
#
|
12
|
+
# == Used document parameters
|
13
|
+
# +grafana_default_instance+ - name of grafana instance, 'default' if not specified
|
14
|
+
#
|
15
|
+
# +grafana_default_dashboard+ - uid of grafana default dashboard to use
|
16
|
+
#
|
17
|
+
# +from+ - 'from' time for the sql query
|
18
|
+
#
|
19
|
+
# +to+ - 'to' time for the sql query
|
20
|
+
#
|
21
|
+
# == Supported options
|
22
|
+
# +columns+ - see {AlertsTableQuery#pre_process} (*mandatory*)
|
23
|
+
#
|
24
|
+
# +instance+ - name of grafana instance, 'default' if not specified
|
25
|
+
#
|
26
|
+
# +dashboard+ - uid of grafana dashboard to query for, empty string if no filter is wanted
|
27
|
+
#
|
28
|
+
# +panel+ - id of the panel to query for
|
29
|
+
#
|
30
|
+
# +from+ - 'from' time for the sql query
|
31
|
+
#
|
32
|
+
# +to+ - 'to' time for the sql query
|
33
|
+
#
|
34
|
+
# +format+ - see {QueryMixin#format_columns}
|
35
|
+
#
|
36
|
+
# +replace_values+ - see {QueryMixin#replace_values}
|
37
|
+
#
|
38
|
+
# +filter_columns+ - see {QueryMixin#filter_columns}
|
39
|
+
class AlertsTableIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
|
40
|
+
include ProcessorMixin
|
41
|
+
|
42
|
+
# :nodoc:
|
43
|
+
def handles?(target)
|
44
|
+
target.start_with? 'grafana_alerts'
|
45
|
+
end
|
46
|
+
|
47
|
+
# :nodoc:
|
48
|
+
def process(doc, reader, _target, attrs)
|
49
|
+
return if @report.cancel
|
50
|
+
|
51
|
+
@report.next_step
|
52
|
+
instance = attrs['instance'] || doc.attr('grafana_default_instance') || 'default'
|
53
|
+
dashboard_id = attrs['dashboard'] || doc.attr('grafana_default_dashboard')
|
54
|
+
panel_id = attrs['panel']
|
55
|
+
@report.logger.debug("Processing AlertsTableIncludeProcessor (instance: #{instance},"\
|
56
|
+
" dashboard: #{dashboard_id}, panel: #{panel_id})")
|
57
|
+
|
58
|
+
query = AlertsTableQuery.new(@report.grafana(instance))
|
59
|
+
query.set_defaults_from_dashboard(@report.grafana(instance).dashboard(dashboard_id)) if dashboard_id
|
60
|
+
defaults = {}
|
61
|
+
defaults['dashboardId'] = dashboard_id if dashboard_id
|
62
|
+
defaults['panelId'] = panel_id if panel_id
|
63
|
+
|
64
|
+
query.merge_hash_variables(doc.attributes, attrs)
|
65
|
+
selected_attrs = attrs.select do |k, _v|
|
66
|
+
k =~ /(?:columns|limit|folderId|dashboardId|panelId|dahboardTag|dashboardQuery|state|query)/x
|
67
|
+
end
|
68
|
+
query.raw_query = defaults.merge(selected_attrs.each_with_object({}) { |(k, v), h| h[k] = v })
|
69
|
+
@report.logger.debug("from: #{query.from}, to: #{query.to}")
|
70
|
+
|
71
|
+
begin
|
72
|
+
reader.unshift_lines query.execute
|
73
|
+
rescue GrafanaReporterError => e
|
74
|
+
@report.logger.error(e.message)
|
75
|
+
reader.unshift_line "|#{e.message}"
|
76
|
+
rescue StandardError => e
|
77
|
+
@report.logger.fatal(e.message)
|
78
|
+
reader.unshift_line "|#{e.message}"
|
79
|
+
end
|
80
|
+
|
81
|
+
reader
|
82
|
+
end
|
83
|
+
|
84
|
+
# @see ProcessorMixin#build_demo_entry
|
85
|
+
def build_demo_entry(_panel)
|
86
|
+
"|===\ninclude::grafana_alerts[columns=\"panelId,name,state\"]\n|==="
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'processor_mixin'
|
4
|
+
|
5
|
+
module GrafanaReporter
|
6
|
+
module Asciidoctor
|
7
|
+
# Implements the hook
|
8
|
+
# include::grafana_annotations[<options>]
|
9
|
+
#
|
10
|
+
# Returns the results of alerts query as a asciidoctor table.
|
11
|
+
#
|
12
|
+
# == Used document parameters
|
13
|
+
# +grafana_default_instance+ - name of grafana instance, 'default' if not specified
|
14
|
+
#
|
15
|
+
# +grafana_default_dashboard+ - uid of grafana default dashboard to use
|
16
|
+
#
|
17
|
+
# +from+ - 'from' time for the sql query
|
18
|
+
#
|
19
|
+
# +to+ - 'to' time for the sql query
|
20
|
+
#
|
21
|
+
# == Supported options
|
22
|
+
# +columns+ - see {AnnotationsTableQuery#pre_process} (*mandatory*)
|
23
|
+
#
|
24
|
+
# +instance+ - name of grafana instance, 'default' if not specified
|
25
|
+
#
|
26
|
+
# +dashboard+ - uid of grafana dashboard to query for, empty string if no filter is wanted
|
27
|
+
#
|
28
|
+
# +panel+ - id of the panel to query for
|
29
|
+
#
|
30
|
+
# +from+ - 'from' time for the sql query
|
31
|
+
#
|
32
|
+
# +to+ - 'to' time for the sql query
|
33
|
+
#
|
34
|
+
# +format+ - see {QueryMixin#format_columns}
|
35
|
+
#
|
36
|
+
# +replace_values+ - see {QueryMixin#replace_values}
|
37
|
+
#
|
38
|
+
# +filter_columns+ - see {QueryMixin#filter_columns}
|
39
|
+
class AnnotationsTableIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
|
40
|
+
include ProcessorMixin
|
41
|
+
|
42
|
+
# :nodoc:
|
43
|
+
def handles?(target)
|
44
|
+
target.start_with? 'grafana_annotations'
|
45
|
+
end
|
46
|
+
|
47
|
+
# :nodoc:
|
48
|
+
def process(doc, reader, _target, attrs)
|
49
|
+
return if @report.cancel
|
50
|
+
|
51
|
+
@report.next_step
|
52
|
+
instance = attrs['instance'] || doc.attr('grafana_default_instance') || 'default'
|
53
|
+
dashboard_id = attrs['dashboard'] || doc.attr('grafana_default_dashboard')
|
54
|
+
panel_id = attrs['panel']
|
55
|
+
@report.logger.debug("Processing AnnotationsTableIncludeProcessor (instance: #{instance})")
|
56
|
+
|
57
|
+
query = AnnotationsTableQuery.new(@report.grafana(instance))
|
58
|
+
query.set_defaults_from_dashboard(@report.grafana(instance).dashboard(dashboard_id)) if dashboard_id
|
59
|
+
defaults = {}
|
60
|
+
defaults['dashboardId'] = dashboard_id if dashboard_id
|
61
|
+
defaults['panelId'] = panel_id if panel_id
|
62
|
+
|
63
|
+
query.merge_hash_variables(doc.attributes, attrs)
|
64
|
+
selected_attrs = attrs.select do |k, _v|
|
65
|
+
k =~ /(?:columns|limit|alertId|dashboardId|panelId|userId|type|tags)/
|
66
|
+
end
|
67
|
+
query.raw_query = defaults.merge(selected_attrs.each_with_object({}) { |(k, v), h| h[k] = v })
|
68
|
+
@report.logger.debug("from: #{query.from}, to: #{query.to}")
|
69
|
+
|
70
|
+
begin
|
71
|
+
reader.unshift_lines query.execute
|
72
|
+
rescue GrafanaReporterError => e
|
73
|
+
@report.logger.error(e.message)
|
74
|
+
reader.unshift_line "|#{e.message}"
|
75
|
+
rescue StandardError => e
|
76
|
+
@report.logger.fatal(e.message)
|
77
|
+
reader.unshift_line "|#{e.message}"
|
78
|
+
end
|
79
|
+
|
80
|
+
reader
|
81
|
+
end
|
82
|
+
|
83
|
+
# @see ProcessorMixin#build_demo_entry
|
84
|
+
def build_demo_entry(_panel)
|
85
|
+
"|===\ninclude::grafana_annotations[columns=\"time,panelId,newState,prevState,text\"]\n|==="
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|