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,109 +1,163 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
class
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
# @return [
|
20
|
-
attr_reader :
|
21
|
-
|
22
|
-
# @return [
|
23
|
-
attr_reader :
|
24
|
-
|
25
|
-
# @return [
|
26
|
-
attr_reader :
|
27
|
-
|
28
|
-
# @
|
29
|
-
|
30
|
-
|
31
|
-
# @
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
@
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
@
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
68
|
-
|
69
|
-
#
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
end
|
76
|
-
|
77
|
-
# @return [
|
78
|
-
def
|
79
|
-
@
|
80
|
-
end
|
81
|
-
|
82
|
-
#
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GrafanaReporter
|
4
|
+
# @abstract Override {#create_report} and {#progress}.
|
5
|
+
#
|
6
|
+
# This class is used to build a report on basis of a given configuration and
|
7
|
+
# template.
|
8
|
+
#
|
9
|
+
# Objects of this class are also stored in {Application::Application}, unless
|
10
|
+
# the retention time is over.
|
11
|
+
class AbstractReport
|
12
|
+
# Array of supported event callback symbols
|
13
|
+
EVENT_CALLBACKS = %i[all on_before_create on_after_cancel on_after_finish].freeze
|
14
|
+
|
15
|
+
# Class variable for storing event listeners
|
16
|
+
@@event_listeners = {}
|
17
|
+
@@event_listeners.default = []
|
18
|
+
|
19
|
+
# @return [String] path to the template
|
20
|
+
attr_reader :template
|
21
|
+
|
22
|
+
# @return [Time] time, when the report generation started
|
23
|
+
attr_reader :start_time
|
24
|
+
|
25
|
+
# @return [Time] time, when the report generation ended
|
26
|
+
attr_reader :end_time
|
27
|
+
|
28
|
+
# @return [Logger] logger object used during report generation
|
29
|
+
attr_reader :logger
|
30
|
+
|
31
|
+
# @return [Boolean] true, if the report is or shall be cancelled
|
32
|
+
attr_reader :cancel
|
33
|
+
|
34
|
+
# @return [Boolen] true, if the report generation is finished (successfull or not)
|
35
|
+
attr_reader :done
|
36
|
+
|
37
|
+
# @param config [Configuration] configuration object
|
38
|
+
# @param template [String] path to the template to be used
|
39
|
+
# @param destination_file_or_path [String or File] path to the destination report or file object to use
|
40
|
+
# @param custom_attributes [Hash] custom attributes, which shall be merged with priority over the configuration
|
41
|
+
def initialize(config, template, destination_file_or_path = nil, custom_attributes = {})
|
42
|
+
@config = config
|
43
|
+
@logger = Logger::TwoWayDelegateLogger.new
|
44
|
+
@logger.additional_logger = @config.logger
|
45
|
+
@done = false
|
46
|
+
@template = template
|
47
|
+
@destination_file_or_path = destination_file_or_path
|
48
|
+
@custom_attributes = custom_attributes
|
49
|
+
@start_time = nil
|
50
|
+
@end_time = nil
|
51
|
+
@cancel = false
|
52
|
+
raise MissingTemplateError, @template.to_s unless File.exist?(@template.to_s)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Registers a new event listener object.
|
56
|
+
# @param event [Symbol] one of EVENT_CALLBACKS
|
57
|
+
# @param listener [Object] object responding to #callback(event_symbol, object)
|
58
|
+
def self.add_event_listener(event, listener)
|
59
|
+
@@event_listeners[event] = [] if @@event_listeners[event] == []
|
60
|
+
@@event_listeners[event].push(listener)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Removes all registeres event listener objects
|
64
|
+
def self.clear_event_listeners
|
65
|
+
@@event_listeners = {}
|
66
|
+
@@event_listeners.default = []
|
67
|
+
end
|
68
|
+
|
69
|
+
# Call to request cancelling the report generation.
|
70
|
+
# @return [void]
|
71
|
+
def cancel!
|
72
|
+
@cancel = true
|
73
|
+
logger.info('Cancelling report generation invoked.')
|
74
|
+
notify(:on_after_cancel)
|
75
|
+
end
|
76
|
+
|
77
|
+
# @return [String] path to the report destination file
|
78
|
+
def path
|
79
|
+
@destination_file_or_path.respond_to?(:path) ? @destination_file_or_path.path : @destination_file_or_path
|
80
|
+
end
|
81
|
+
|
82
|
+
# Deletes the report file object.
|
83
|
+
# @return [void]
|
84
|
+
def delete_file
|
85
|
+
if @destination_file_or_path.is_a?(Tempfile)
|
86
|
+
@destination_file_or_path.unlink
|
87
|
+
elsif @destination_file_or_path.is_a?(File)
|
88
|
+
@destination_file_or_path.delete
|
89
|
+
end
|
90
|
+
@destination_file_or_path = nil
|
91
|
+
end
|
92
|
+
|
93
|
+
# @return [Float] time in seconds, that the report generation took
|
94
|
+
def execution_time
|
95
|
+
return nil if start_time.nil?
|
96
|
+
return end_time - start_time unless end_time.nil?
|
97
|
+
|
98
|
+
Time.now - start_time
|
99
|
+
end
|
100
|
+
|
101
|
+
# @return [Array] error messages during report generation.
|
102
|
+
def error
|
103
|
+
@error || []
|
104
|
+
end
|
105
|
+
|
106
|
+
# @return [String] status of the report as string, either 'not started', 'in progress', 'cancelling',
|
107
|
+
# 'cancelled', 'died' or 'finished'.
|
108
|
+
def status
|
109
|
+
return 'not started' unless @start_time
|
110
|
+
return 'cancelled' if done && cancel
|
111
|
+
return 'cancelling' if !done && cancel
|
112
|
+
return 'finished' if done && error.empty?
|
113
|
+
return 'died' if done && !error.empty?
|
114
|
+
|
115
|
+
'in progress'
|
116
|
+
end
|
117
|
+
|
118
|
+
# @return [String] string containing all messages ([Logger::Severity::DEBUG]) of the logger during report
|
119
|
+
# generation.
|
120
|
+
def full_log
|
121
|
+
logger.internal_messages
|
122
|
+
end
|
123
|
+
|
124
|
+
# Is being called to start the report generation.
|
125
|
+
# @return [void]
|
126
|
+
def create_report
|
127
|
+
notify(:on_before_create)
|
128
|
+
@start_time = Time.new
|
129
|
+
logger.info("Report started at #{@start_time}")
|
130
|
+
end
|
131
|
+
|
132
|
+
# @abstract
|
133
|
+
# @return [Integer] number between 0 and 100, representing the current progress of the report creation.
|
134
|
+
def progress
|
135
|
+
raise NotImplementedError
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def done!
|
141
|
+
@done = true
|
142
|
+
@end_time = Time.new
|
143
|
+
logger.info("Report creation ended after #{@end_time - @start_time} seconds with status '#{status}'")
|
144
|
+
notify(:on_after_finish)
|
145
|
+
end
|
146
|
+
|
147
|
+
def notify(event)
|
148
|
+
(@@event_listeners[:all] + @@event_listeners[event]).each do |listener|
|
149
|
+
logger.debug("Informing event listener '#{listener.class}' about event '#{event}' for report '#{object_id}'.")
|
150
|
+
begin
|
151
|
+
res = listener.callback(event, self)
|
152
|
+
logger.debug("Event listener '#{listener.class}' for event '#{event}' and report '#{object_id}' returned "\
|
153
|
+
"with result '#{res}'.")
|
154
|
+
rescue StandardError => e
|
155
|
+
msg = "Event listener '#{listener.class}' for event '#{event}' and report '#{object_id}' returned with "\
|
156
|
+
"error: #{e.message} - #{e.backtrace}."
|
157
|
+
puts msg
|
158
|
+
logger.error(msg)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GrafanaReporter
|
4
|
+
# This class is used to query alerts from grafana.
|
5
|
+
class AlertsTableQuery < AbstractQuery
|
6
|
+
# Check if mandatory {Grafana::Variable} +columns+ is specified in variables.
|
7
|
+
#
|
8
|
+
# The value of the +columns+ variable has to be a comma separated list of column titles, which
|
9
|
+
# need to be included in the following list:
|
10
|
+
# - limit
|
11
|
+
# - dashboardId
|
12
|
+
# - panelId
|
13
|
+
# - query
|
14
|
+
# - state
|
15
|
+
# - folderId
|
16
|
+
# - dashboardQuery
|
17
|
+
# - dashboardTag
|
18
|
+
# @return [void]
|
19
|
+
def pre_process
|
20
|
+
raise MissingMandatoryAttributeError, 'columns' unless @raw_query['columns']
|
21
|
+
|
22
|
+
@from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] ||
|
23
|
+
@variables['grafana_default_from_timezone'])
|
24
|
+
@to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] ||
|
25
|
+
@variables['grafana_default_to_timezone'])
|
26
|
+
@datasource = Grafana::GrafanaAlertsDatasource.new(nil)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Filter the query result for the given columns and sets the result in the preformatted SQL
|
30
|
+
# result stlye.
|
31
|
+
|
32
|
+
# Additionally it applies {QueryMixin#format_columns}, {QueryMixin#replace_values} and
|
33
|
+
# {QueryMixin#filter_columns}.
|
34
|
+
# @return [void]
|
35
|
+
def post_process
|
36
|
+
@result = format_columns(@result, @variables['format'])
|
37
|
+
@result = replace_values(@result, @variables.select { |k, _v| k =~ /^replace_values_\d+/ })
|
38
|
+
@result = filter_columns(@result, @variables['filter_columns'])
|
39
|
+
|
40
|
+
# TODO: move formatting to Asciidoctor namespace
|
41
|
+
@result = @result[:content].map { |row| "| #{row.map { |item| item.to_s.gsub('|', '\\|') }.join(' | ')}" }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GrafanaReporter
|
4
|
+
# This class is used to query annotations from grafana.
|
5
|
+
class AnnotationsTableQuery < AbstractQuery
|
6
|
+
# Check if mandatory {Grafana::Variable} +columns+ is specified in variables.
|
7
|
+
#
|
8
|
+
# The value of the +columns+ variable has to be a comma separated list of column titles, which
|
9
|
+
# need to be included in the following list:
|
10
|
+
# - limit
|
11
|
+
# - alertId
|
12
|
+
# - userId
|
13
|
+
# - type
|
14
|
+
# - tags
|
15
|
+
# - dashboardId
|
16
|
+
# - panelId
|
17
|
+
# @return [void]
|
18
|
+
def pre_process
|
19
|
+
raise MissingMandatoryAttributeError, 'columns' unless @raw_query['columns']
|
20
|
+
|
21
|
+
@from = translate_date(@from, @variables['grafana-report-timestamp'], false, @variables['from_timezone'] ||
|
22
|
+
@variables['grafana_default_from_timezone'])
|
23
|
+
@to = translate_date(@to, @variables['grafana-report-timestamp'], true, @variables['to_timezone'] ||
|
24
|
+
@variables['grafana_default_to_timezone'])
|
25
|
+
@datasource = Grafana::GrafanaAnnotationsDatasource.new(nil)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Filters the query result for the given columns and sets the result
|
29
|
+
# in the preformatted SQL result style.
|
30
|
+
#
|
31
|
+
# Additionally it applies {QueryMixin#format_columns}, {QueryMixin#replace_values} and
|
32
|
+
# {QueryMixin#filter_columns}.
|
33
|
+
# @return [void]
|
34
|
+
def post_process
|
35
|
+
@result = format_columns(@result, @variables['format'])
|
36
|
+
@result = replace_values(@result, @variables.select { |k, _v| k =~ /^replace_values_\d+/ })
|
37
|
+
@result = filter_columns(@result, @variables['filter_columns'])
|
38
|
+
|
39
|
+
# TODO: move formatting to Asciidoctor namespace
|
40
|
+
@result = result[:content].map { |row| "| #{row.map { |item| item.to_s.gsub('|', '\\|') }.join(' | ')}" }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -1,229 +1,162 @@
|
|
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
|
-
def
|
26
|
-
@
|
27
|
-
@
|
28
|
-
end
|
29
|
-
|
30
|
-
# This is the main method, which is called, if the application is
|
31
|
-
# run in standalone mode.
|
32
|
-
# @param params [Array]
|
33
|
-
# @return [Integer]
|
34
|
-
def configure_and_run(params = [])
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
Thread.new do
|
164
|
-
report.create_report
|
165
|
-
end
|
166
|
-
@reports << report
|
167
|
-
|
168
|
-
return http_response(302, 'Found', nil, Location: "/view_report?report_id=#{report.object_id}")
|
169
|
-
|
170
|
-
elsif request.split("\r\n")[0] =~ %r{^GET /overview[\? ]}
|
171
|
-
# show overview for current reports
|
172
|
-
return get_reports_status_as_html(@reports)
|
173
|
-
|
174
|
-
elsif request.split("\r\n")[0] =~ %r{^GET /view_report[\? ]}
|
175
|
-
# view report if already available, or show status view
|
176
|
-
report = @reports.select { |r| r.object_id.to_s == attrs['report_id'].to_s }.first
|
177
|
-
raise WebserviceGeneralRenderingError, 'view_report has been called without valid id' if report.nil?
|
178
|
-
|
179
|
-
# show report status
|
180
|
-
return get_reports_status_as_html([report]) if !report.done || !report.error.empty?
|
181
|
-
|
182
|
-
# provide report
|
183
|
-
@logger.debug("Returning PDF report at #{report.path}")
|
184
|
-
content = File.read(report.path)
|
185
|
-
return http_response(200, 'OK', content, "Content-Type": 'application/pdf') if content.start_with?("%PDF")
|
186
|
-
# TODO properly provide file as zip
|
187
|
-
return http_response(200, 'OK', content, "Content-Type": 'application/octet-stream', "Content-Disposition": "attachment; filename=report.zip")
|
188
|
-
|
189
|
-
elsif request.split("\r\n")[0] =~ %r{^GET /cancel_report[\? ]}
|
190
|
-
# view report if already available, or show status view
|
191
|
-
report = @reports.select { |r| r.object_id.to_s == attrs['report_id'].to_s }.first
|
192
|
-
raise WebserviceGeneralRenderingError, 'cancel_report has been called without valid id' if report.nil?
|
193
|
-
|
194
|
-
report.cancel! unless report.done
|
195
|
-
|
196
|
-
# redirect to view_report page
|
197
|
-
return http_response(302, 'Found', nil, Location: "/view_report?report_id=#{report.object_id}")
|
198
|
-
|
199
|
-
elsif request.split("\r\n")[0] =~ %r{^GET /view_log[\? ]}
|
200
|
-
# view report if already available, or show status view
|
201
|
-
report = @reports.select { |r| r.object_id.to_s == attrs['report_id'].to_s }.first
|
202
|
-
raise WebserviceGeneralRenderingError, 'view_log has been called without valid id' if report.nil?
|
203
|
-
|
204
|
-
content = report.full_log
|
205
|
-
|
206
|
-
return http_response(200, 'OK', content, "Content-Type": 'text/plain')
|
207
|
-
end
|
208
|
-
|
209
|
-
raise WebserviceUnknownPathError, request.split("\r\n")[0]
|
210
|
-
end
|
211
|
-
|
212
|
-
def get_reports_status_as_html(reports)
|
213
|
-
i = reports.length
|
214
|
-
|
215
|
-
content = '<html><head></head><body><table><thead><th>#</th><th>Start Time</th><th>End Time</th><th>Template</th><th>Execution time</th><th>Status</th><th>Error</th><th>Action</th></thead>' +
|
216
|
-
reports.reverse.map do |report|
|
217
|
-
"<tr><td>#{(i -= 1)}</td><td>#{report.start_time}</td><td>#{report.end_time}</td><td>#{report.template}</td><td>#{report.execution_time.to_i} secs</td><td>#{report.status} (#{(report.progress * 100).to_i}%)</td><td>#{report.error.join('<br>')}</td><td>#{!report.done && !report.cancel ? "<a href=\"/cancel_report?report_id=#{report.object_id}\">Cancel</a> " : ''}#{(report.status == 'finished') || (report.status == 'cancelled') ? "<a href=\"/view_report?report_id=#{report.object_id}\">View</a> " : ' '}<a href=\"/view_log?report_id=#{report.object_id}\">Log</a></td></tr>"
|
218
|
-
end.join('') +
|
219
|
-
'</table></body></html>'
|
220
|
-
|
221
|
-
http_response(200, 'OK', content, "Content-Type": 'text/html')
|
222
|
-
end
|
223
|
-
|
224
|
-
def http_response(code, text, body, opts = {})
|
225
|
-
"HTTP/1.1 #{code} #{text}\r\n#{opts.map { |k, v| "#{k}: #{v}" }.join("\r\n")}#{body ? "\r\nContent-Length: #{body.to_s.bytesize}" : ''}\r\n\r\n#{body}"
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end
|
229
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GrafanaReporter
|
4
|
+
# This module contains all classes, which are used by the grafana reporter
|
5
|
+
# application. The application is a set of classes, which allows to run the
|
6
|
+
# reporter in several ways.
|
7
|
+
#
|
8
|
+
# If you intend to use the reporter functionality, without the application,
|
9
|
+
# it might be helpful to not use the classes from here.
|
10
|
+
module Application
|
11
|
+
# This class contains the main application to run the grafana reporter.
|
12
|
+
#
|
13
|
+
# It can be run to test the grafana connection, render a single template
|
14
|
+
# or run as a service.
|
15
|
+
class Application
|
16
|
+
# Default file name for grafana reporter configuration file
|
17
|
+
CONFIG_FILE = 'grafana_reporter.config'
|
18
|
+
|
19
|
+
# Contains the {Configuration} object of the application.
|
20
|
+
attr_accessor :config
|
21
|
+
|
22
|
+
# Stores the {Webservice} object of the application
|
23
|
+
attr_reader :webservice
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
@config = Configuration.new
|
27
|
+
@webservice = Webservice.new
|
28
|
+
end
|
29
|
+
|
30
|
+
# This is the main method, which is called, if the application is
|
31
|
+
# run in standalone mode.
|
32
|
+
# @param params [Array<String>] command line parameters, mainly ARGV can be used.
|
33
|
+
# @return [Integer] 0 if everything is fine, -1 if execution aborted.
|
34
|
+
def configure_and_run(params = [])
|
35
|
+
config_file = CONFIG_FILE
|
36
|
+
tmp_config = Configuration.new
|
37
|
+
action_wizard = false
|
38
|
+
|
39
|
+
parser = OptionParser.new do |opts|
|
40
|
+
opts.banner = if ENV['OCRA_EXECUTABLE']
|
41
|
+
"Usage: #{ENV['OCRA_EXECUTABLE'].gsub("#{Dir.pwd}/".gsub('/', '\\'), '')} [options]"
|
42
|
+
else
|
43
|
+
"Usage: #{Gem.ruby} #{$PROGRAM_NAME} [options]"
|
44
|
+
end
|
45
|
+
|
46
|
+
opts.on('-c', '--config CONFIG_FILE_NAME', 'Specify custom configuration file,'\
|
47
|
+
" instead of #{CONFIG_FILE}.") do |file_name|
|
48
|
+
config_file = file_name
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on('-d', '--debug LEVEL', 'Specify detail level: FATAL, ERROR, WARN, INFO, DEBUG.') do |level|
|
52
|
+
tmp_config.set_param('grafana-reporter:debug-level', level)
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on('-o', '--output FILE', 'Output filename if only a single file is rendered') do |file|
|
56
|
+
tmp_config.set_param('to_file', file)
|
57
|
+
end
|
58
|
+
|
59
|
+
opts.on('-s', '--set VARIABLE,VALUE', Array, 'Set a variable value, which will be passed to the '\
|
60
|
+
'rendering') do |list|
|
61
|
+
raise ParameterValueError, list.length unless list.length == 2
|
62
|
+
|
63
|
+
tmp_config.set_param("default-document-attributes:#{list[0]}", list[1])
|
64
|
+
end
|
65
|
+
|
66
|
+
opts.on('--ssl-cert FILE', 'Manually specify a SSL cert file for HTTPS connection to grafana. Only '\
|
67
|
+
'needed if not working properly otherwise.') do |file|
|
68
|
+
if File.exist?(file)
|
69
|
+
tmp_config.set_param('grafana-reporter:ssl-cert', file)
|
70
|
+
else
|
71
|
+
config.logger.warn("SSL certificate file #{file} does not exist. Setting will be ignored.")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
opts.on('--test GRAFANA_INSTANCE', 'test current configuration against given GRAFANA_INSTANCE') do |instance|
|
76
|
+
tmp_config.set_param('grafana-reporter:run-mode', 'test')
|
77
|
+
tmp_config.set_param('grafana-reporter:test-instance', instance)
|
78
|
+
end
|
79
|
+
|
80
|
+
opts.on('-t', '--template TEMPLATE', 'Render a single ASCIIDOC template to PDF and exit') do |template|
|
81
|
+
tmp_config.set_param('grafana-reporter:run-mode', 'single-render')
|
82
|
+
tmp_config.set_param('default-document-attributes:var-template', template)
|
83
|
+
end
|
84
|
+
|
85
|
+
opts.on('-w', '--wizard', 'Configuration wizard to prepare environment for the reporter.') do
|
86
|
+
action_wizard = true
|
87
|
+
end
|
88
|
+
|
89
|
+
opts.on('-v', '--version', 'Version information') do
|
90
|
+
puts GRAFANA_REPORTER_VERSION.join('.')
|
91
|
+
return -1
|
92
|
+
end
|
93
|
+
|
94
|
+
opts.on('-h', '--help', 'Show this message') do
|
95
|
+
puts opts
|
96
|
+
return -1
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
begin
|
101
|
+
parser.parse!(params)
|
102
|
+
return ConsoleConfigurationWizard.new.start_wizard(config_file, tmp_config) if action_wizard
|
103
|
+
rescue ApplicationError => e
|
104
|
+
puts e.message
|
105
|
+
return -1
|
106
|
+
end
|
107
|
+
|
108
|
+
# abort if config file does not exist
|
109
|
+
unless File.exist?(config_file)
|
110
|
+
puts "Config file '#{config_file}' does not exist. Consider calling the configuration wizard"\
|
111
|
+
' with option \'-w\' or use \'-h\' to see help message. Aborting.'
|
112
|
+
return -1
|
113
|
+
end
|
114
|
+
|
115
|
+
# read config file
|
116
|
+
config_hash = nil
|
117
|
+
begin
|
118
|
+
config_hash = YAML.load_file(config_file)
|
119
|
+
rescue StandardError => e
|
120
|
+
raise ConfigurationError, "Could not read config file '#{config_file}' (Error: #{e.message})"
|
121
|
+
end
|
122
|
+
|
123
|
+
# merge command line configuration with read config file
|
124
|
+
@config.config = config_hash
|
125
|
+
@config.merge!(tmp_config)
|
126
|
+
|
127
|
+
run
|
128
|
+
end
|
129
|
+
|
130
|
+
# Runs the application with the current set {Configuration} object.
|
131
|
+
# @return [Integer] value smaller than 0, if error. 0 if successfull
|
132
|
+
def run
|
133
|
+
begin
|
134
|
+
config.validate
|
135
|
+
rescue ConfigurationError => e
|
136
|
+
puts e.message
|
137
|
+
return -2
|
138
|
+
end
|
139
|
+
|
140
|
+
case config.mode
|
141
|
+
when Configuration::MODE_CONNECTION_TEST
|
142
|
+
res = Grafana::Grafana.new(config.grafana_host(config.test_instance),
|
143
|
+
config.grafana_api_key(config.test_instance),
|
144
|
+
logger: config.logger).test_connection
|
145
|
+
puts res
|
146
|
+
|
147
|
+
when Configuration::MODE_SINGLE_RENDER
|
148
|
+
begin
|
149
|
+
config.report_class.new(config, config.template, config.to_file).create_report
|
150
|
+
rescue StandardError => e
|
151
|
+
puts "#{e.message}\n#{e.backtrace.join("\n")}"
|
152
|
+
end
|
153
|
+
|
154
|
+
when Configuration::MODE_SERVICE
|
155
|
+
@webservice.run(config)
|
156
|
+
end
|
157
|
+
|
158
|
+
0
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|