ruby-grafana-reporter 0.1.7 → 0.2.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 +166 -339
- data/bin/ruby-grafana-reporter +5 -4
- data/lib/VERSION.rb +5 -3
- data/lib/grafana/abstract_panel_query.rb +22 -20
- data/lib/grafana/abstract_query.rb +132 -127
- data/lib/grafana/abstract_sql_query.rb +51 -42
- data/lib/grafana/dashboard.rb +77 -66
- data/lib/grafana/errors.rb +66 -61
- data/lib/grafana/grafana.rb +130 -131
- data/lib/grafana/panel.rb +41 -39
- data/lib/grafana/panel_image_query.rb +52 -49
- data/lib/grafana/variable.rb +217 -259
- data/lib/grafana_reporter/abstract_report.rb +112 -109
- data/lib/grafana_reporter/application/application.rb +404 -229
- data/lib/grafana_reporter/application/errors.rb +33 -30
- data/lib/grafana_reporter/application/webservice.rb +231 -0
- data/lib/grafana_reporter/asciidoctor/alerts_table_query.rb +104 -99
- data/lib/grafana_reporter/asciidoctor/annotations_table_query.rb +99 -96
- data/lib/grafana_reporter/asciidoctor/errors.rb +40 -37
- data/lib/grafana_reporter/asciidoctor/extensions/alerts_table_include_processor.rb +92 -86
- data/lib/grafana_reporter/asciidoctor/extensions/annotations_table_include_processor.rb +91 -86
- data/lib/grafana_reporter/asciidoctor/extensions/panel_image_block_macro.rb +69 -67
- data/lib/grafana_reporter/asciidoctor/extensions/panel_image_inline_macro.rb +68 -65
- data/lib/grafana_reporter/asciidoctor/extensions/panel_property_inline_macro.rb +61 -58
- data/lib/grafana_reporter/asciidoctor/extensions/panel_query_table_include_processor.rb +78 -75
- data/lib/grafana_reporter/asciidoctor/extensions/panel_query_value_inline_macro.rb +73 -70
- data/lib/grafana_reporter/asciidoctor/extensions/processor_mixin.rb +20 -18
- data/lib/grafana_reporter/asciidoctor/extensions/show_environment_include_processor.rb +43 -41
- data/lib/grafana_reporter/asciidoctor/extensions/sql_table_include_processor.rb +70 -67
- data/lib/grafana_reporter/asciidoctor/extensions/sql_value_inline_macro.rb +66 -65
- data/lib/grafana_reporter/asciidoctor/extensions/value_as_variable_include_processor.rb +61 -57
- data/lib/grafana_reporter/asciidoctor/panel_first_value_query.rb +34 -32
- data/lib/grafana_reporter/asciidoctor/panel_image_query.rb +25 -23
- data/lib/grafana_reporter/asciidoctor/panel_property_query.rb +44 -43
- data/lib/grafana_reporter/asciidoctor/panel_table_query.rb +38 -36
- data/lib/grafana_reporter/asciidoctor/query_mixin.rb +310 -309
- data/lib/grafana_reporter/asciidoctor/report.rb +177 -159
- data/lib/grafana_reporter/asciidoctor/sql_first_value_query.rb +37 -34
- data/lib/grafana_reporter/asciidoctor/sql_table_query.rb +39 -32
- data/lib/grafana_reporter/configuration.rb +257 -326
- data/lib/grafana_reporter/errors.rb +48 -38
- data/lib/grafana_reporter/logger/two_way_logger.rb +58 -52
- data/lib/ruby-grafana-reporter.rb +29 -27
- metadata +10 -23
data/bin/ruby-grafana-reporter
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative '../lib/ruby-grafana-reporter'
|
5
|
+
GrafanaReporter::Application::Application.new.configure_and_run(ARGV)
|
data/lib/VERSION.rb
CHANGED
@@ -1,20 +1,22 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'abstract_query'
|
4
|
+
|
5
|
+
module Grafana
|
6
|
+
# @abstract
|
7
|
+
#
|
8
|
+
# Used as a superclass for all queries, which rely on a {Panel} object.
|
9
|
+
#
|
10
|
+
# @see AbstractQuery
|
11
|
+
class AbstractPanelQuery < AbstractQuery
|
12
|
+
attr_reader :panel
|
13
|
+
|
14
|
+
# Initializes the variables of the query using {AbstractQuery#extract_dashboard_variables}.
|
15
|
+
# @param panel [Panel] panel for which the query shall be executed
|
16
|
+
def initialize(panel)
|
17
|
+
super()
|
18
|
+
@panel = panel
|
19
|
+
extract_dashboard_variables(@panel.dashboard)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,127 +1,132 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
#
|
16
|
-
# - calls {#
|
17
|
-
# -
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
@result
|
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
|
-
@variables[k.to_s]
|
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
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grafana
|
4
|
+
# @abstract Override {#url}, #{#request}, {#pre_process} and {#post_process} in subclass.
|
5
|
+
#
|
6
|
+
# Superclass containing everything for all queries towards grafana.
|
7
|
+
class AbstractQuery
|
8
|
+
attr_accessor :from, :to, :timeout, :result
|
9
|
+
attr_reader :variables
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@variables = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
# Runs the whole process to receive values properly from this query:
|
16
|
+
# - calls {#pre_process}
|
17
|
+
# - executes this query against the given {Grafana} instance
|
18
|
+
# - calls {#post_process}
|
19
|
+
# - returns the result
|
20
|
+
#
|
21
|
+
# @param grafana [Grafana] {Grafana} object, against which the query is executed
|
22
|
+
# @return [Object] result of the query
|
23
|
+
def execute(grafana)
|
24
|
+
return @result unless @result.nil?
|
25
|
+
|
26
|
+
pre_process(grafana)
|
27
|
+
@result = grafana.execute_http_request(url, request, timeout)
|
28
|
+
post_process
|
29
|
+
@result
|
30
|
+
end
|
31
|
+
|
32
|
+
# Used to retrieve default configurations from the given {Dashboard} and store them as settings in the query.
|
33
|
+
#
|
34
|
+
# Following data is extracted:
|
35
|
+
# - +from+, by {Dashboard#from_time}
|
36
|
+
# - +to+, by {Dashboard#to_time}
|
37
|
+
# - and all variables as {Variable}, prefixed with +var-+, as grafana also does it
|
38
|
+
def extract_dashboard_variables(dashboard)
|
39
|
+
@from = dashboard.from_time
|
40
|
+
@to = dashboard.to_time
|
41
|
+
dashboard.variables.each { |item| merge_variables({ "var-#{item.name}": item }) }
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
# Merges the given Hash with the stored variables.
|
46
|
+
#
|
47
|
+
# Can be used to easily set many values at once in the local variables hash.
|
48
|
+
#
|
49
|
+
# Please note, that the values of the Hash need to be of type {Variable}.
|
50
|
+
#
|
51
|
+
# @param hash [Hash<String,Variable>] Hash containing variable name as key and {Variable} as value
|
52
|
+
# @return [AbstractQuery] this object
|
53
|
+
def merge_variables(hash)
|
54
|
+
hash.each do |k, v|
|
55
|
+
if @variables[k.to_s].nil?
|
56
|
+
@variables[k.to_s] = v
|
57
|
+
else
|
58
|
+
@variables[k.to_s].raw_value = v.raw_value
|
59
|
+
end
|
60
|
+
end
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Hash<String, Variable>] all grafana variables stored in this query, i.e. the variable name
|
65
|
+
# is prefixed with +var-+
|
66
|
+
def grafana_variables
|
67
|
+
@variables.select { |k, _v| k =~ /^var-.+/ }
|
68
|
+
end
|
69
|
+
|
70
|
+
# Replaces the grafana variables in the given string with their replacement value.
|
71
|
+
#
|
72
|
+
# @param string [String] string in which the variables shall be replaced
|
73
|
+
# @param variables [Hash<String,Variable>] Hash containing the variables, which shall be replaced in the
|
74
|
+
# given string
|
75
|
+
# @return [String] string in which all variables are properly replaced
|
76
|
+
def replace_variables(string, variables = {})
|
77
|
+
res = string
|
78
|
+
repeat = true
|
79
|
+
repeat_count = 0
|
80
|
+
|
81
|
+
# TODO: find a proper way to replace variables recursively instead of over and over again
|
82
|
+
# TODO: add tests for recursive replacement of variable
|
83
|
+
while repeat && (repeat_count < 3)
|
84
|
+
repeat = false
|
85
|
+
repeat_count += 1
|
86
|
+
variables.each do |var_name, obj|
|
87
|
+
# only set ticks if value is string
|
88
|
+
variable = var_name.gsub(/^var-/, '')
|
89
|
+
res = res.gsub(/(?:\$\{#{variable}(?::(?<format>\w+))?\}|(?<!\.)\$#{variable}(?!\.))/) do
|
90
|
+
obj.value_formatted($LAST_MATCH_INFO[:format])
|
91
|
+
end
|
92
|
+
end
|
93
|
+
repeat = true if res.include?('$')
|
94
|
+
end
|
95
|
+
|
96
|
+
res
|
97
|
+
end
|
98
|
+
|
99
|
+
# @abstract
|
100
|
+
#
|
101
|
+
# @return [String] String containing the relative URL to execute the query
|
102
|
+
def uri
|
103
|
+
raise NotImplementedError
|
104
|
+
end
|
105
|
+
|
106
|
+
# @abstract
|
107
|
+
#
|
108
|
+
# @return [Hash] Hash containing the request parameters, which shall be overwritten or extended in
|
109
|
+
# {Grafana#execute_http_request}
|
110
|
+
def request
|
111
|
+
raise NotImplementedError
|
112
|
+
end
|
113
|
+
|
114
|
+
# @abstract
|
115
|
+
#
|
116
|
+
# Use this function to perform all necessary actions, before the query is actually executed.
|
117
|
+
# Here you can e.g. set values of variables or similar.
|
118
|
+
#
|
119
|
+
# @param grafana [Grafana] {Grafana} object, against which the query shall be executed
|
120
|
+
def pre_process(grafana)
|
121
|
+
raise NotImplementedError
|
122
|
+
end
|
123
|
+
|
124
|
+
# @abstract
|
125
|
+
#
|
126
|
+
# Use this function to format the raw result of the @result variable to conform to the expected return value.
|
127
|
+
# You might also want to {#replace_variables} in the @result or similar.
|
128
|
+
def post_process
|
129
|
+
raise NotImplementedError
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -1,42 +1,51 @@
|
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
@sql.
|
40
|
-
|
41
|
-
|
42
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grafana
|
4
|
+
# @abstract
|
5
|
+
#
|
6
|
+
# Used as a superclass for all queries, which execute SQL queries against {Grafana}.
|
7
|
+
#
|
8
|
+
# @see AbstractQuery
|
9
|
+
class AbstractSqlQuery < AbstractQuery
|
10
|
+
attr_reader :sql, :datasource_id
|
11
|
+
|
12
|
+
# @param raw_sql [String] raw sql statement, as it can be sent to a SQL database
|
13
|
+
# @param datasource_id [Integer] ID of the datasource against which the query is run
|
14
|
+
def initialize(raw_sql, datasource_id)
|
15
|
+
super()
|
16
|
+
@sql = raw_sql
|
17
|
+
@datasource_id = datasource_id
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [String] relative URL, where the request has to be sent to.
|
21
|
+
def url
|
22
|
+
'/api/tsdb/query'
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Hash] request, which executes the SQL statement against the specified datasource
|
26
|
+
def request
|
27
|
+
{
|
28
|
+
body: {
|
29
|
+
from: @from,
|
30
|
+
to: @to,
|
31
|
+
queries: [rawSql: @sql, datasourceId: @datasource_id.to_i, format: 'table']
|
32
|
+
}.to_json,
|
33
|
+
request: Net::HTTP::Post
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
# Replaces all variables in the SQL statement.
|
38
|
+
def pre_process(grafana)
|
39
|
+
raise MissingSqlQueryError if @sql.nil?
|
40
|
+
unless grafana.datasource_id_exists?(@datasource_id.to_i)
|
41
|
+
raise DatasourceDoesNotExistError.new('id', @datasource_id)
|
42
|
+
end
|
43
|
+
|
44
|
+
@sql = replace_variables(@sql, grafana_variables)
|
45
|
+
# remove comments in query
|
46
|
+
@sql.gsub!(/--[^\r\n]*(?:[\r\n]+|$)/, ' ')
|
47
|
+
@sql.gsub!(/\r\n/, ' ')
|
48
|
+
@sql.gsub!(/\n/, ' ')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/grafana/dashboard.rb
CHANGED
@@ -1,66 +1,77 @@
|
|
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
|
-
|
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
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grafana
|
4
|
+
# Representation of one specific dashboard in a {Grafana} instance.
|
5
|
+
class Dashboard
|
6
|
+
# @return [Grafana] parent {Grafana} object
|
7
|
+
attr_reader :grafana
|
8
|
+
attr_reader :panels, :variables
|
9
|
+
|
10
|
+
# @param model [Hash] converted JSON Hash of the grafana dashboard
|
11
|
+
# @param grafana [Grafana] parent {Grafana} object
|
12
|
+
def initialize(model, grafana)
|
13
|
+
@grafana = grafana
|
14
|
+
@model = model
|
15
|
+
|
16
|
+
initialize_panels
|
17
|
+
initialize_variables
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [String] +from+ time configured in the dashboard.
|
21
|
+
def from_time
|
22
|
+
return @model['time']['from'] if @model['time']
|
23
|
+
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [String] +to+ time configured in the dashboard.
|
28
|
+
def to_time
|
29
|
+
@model['time']['to'] if @model['time']
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [String] dashboard UID
|
34
|
+
def id
|
35
|
+
@model['uid']
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Panel] panel for the specified ID
|
39
|
+
def panel(id)
|
40
|
+
panels = @panels.select { |item| item.field('id') == id.to_i }
|
41
|
+
raise PanelDoesNotExistError.new(id, self) if panels.empty?
|
42
|
+
|
43
|
+
panels.first
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# store variables in array as objects of type Variable
|
50
|
+
def initialize_variables
|
51
|
+
@variables = []
|
52
|
+
return unless @model.key?('templating')
|
53
|
+
|
54
|
+
list = @model['templating']['list']
|
55
|
+
return unless list.is_a? Array
|
56
|
+
|
57
|
+
list.each do |item|
|
58
|
+
@variables << Variable.new(item)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# read panels
|
63
|
+
def initialize_panels
|
64
|
+
@panels = []
|
65
|
+
return unless @model.key?('panels')
|
66
|
+
|
67
|
+
@model['panels'].each do |panel|
|
68
|
+
if panel.key?('panels')
|
69
|
+
panel['panels'].each do |subpanel|
|
70
|
+
@panels << Panel.new(subpanel, self)
|
71
|
+
end
|
72
|
+
else
|
73
|
+
@panels << Panel.new(panel, self)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|