lm_rest 1.0.6 → 1.0.7

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.
data/bin/ds_checker.rb CHANGED
@@ -1,261 +1,278 @@
1
1
  #!/usr/bin/env ruby
2
- #
3
- #
4
- # Based on a script written originally by Matt Dunham
5
- #
2
+
6
3
  require 'lm_rest'
7
- require 'colorize'
4
+
5
+ STANDARD_GRAPH_NOTE = 'Standard datasource graph checks are skipped because the current LogicMonitor v3 Swagger surface only exposes datasource overview graph endpoints.'.freeze
8
6
 
9
7
  def usage
10
- puts "USAGE:\t" + $PROGRAM_NAME + ' account userid passwd datasource_name_or_glob'
8
+ puts "USAGE:\t#{$PROGRAM_NAME} account access_id access_key datasource_name_or_glob"
9
+ end
10
+
11
+ abort('Bad arguments.') unless ARGV.length == 4
12
+
13
+ @account, @access_id, @access_key, @datasource_filter = ARGV
14
+ @lm = LMRest::APIClient.new(@account, @access_id, @access_key)
15
+ @standard_graph_note_printed = false
16
+
17
+ def wrap_resources(items)
18
+ Array(items).map { |item| item.is_a?(LMRest::Resource) ? item : LMRest::Resource.new(item) }
19
+ end
20
+
21
+ def resource_value(resource, *keys)
22
+ keys.each do |key|
23
+ return resource.public_send(key) if resource.respond_to?(key)
24
+ return resource[key] if resource.respond_to?(:key?) && resource.key?(key)
25
+ return resource[key.to_s] if resource.is_a?(Hash) && resource.key?(key.to_s)
26
+ return resource[key] if resource.is_a?(Hash) && resource.key?(key)
27
+ end
28
+
29
+ nil
11
30
  end
12
31
 
13
- if ARGV.length == 4
14
- @account = ARGV[0]
15
- @userid = ARGV[1]
16
- @passwd = ARGV[2]
17
- @lm = LMRest.new(@account, @userid, @passwd)
18
- else
19
- usage
20
- fail 'Bad arguments.'
32
+ def fetch_datasource_details(datasource_id)
33
+ @lm.get_datasource(
34
+ datasource_id,
35
+ fields: 'name,displayName,description,appliesTo,collectInterval,hasMultiInstances,dataPoints'
36
+ )
37
+ end
38
+
39
+ def fetch_datapoints(datasource)
40
+ wrap_resources(resource_value(datasource, :dataPoints))
41
+ end
42
+
43
+ def fetch_overview_graphs(datasource_id)
44
+ wrap_resources(@lm.get_data_source_overview_graph_list(datasource_id))
45
+ end
46
+
47
+ def fetch_standard_graphs(_datasource_id)
48
+ return [] if @standard_graph_note_printed
49
+
50
+ puts STANDARD_GRAPH_NOTE
51
+ @standard_graph_note_printed = true
52
+ []
21
53
  end
22
54
 
23
55
  def dp_type(datapoint)
24
- datapoint.postProcessorMethod == 'expression' ? 'complex' : 'normal'
56
+ resource_value(datapoint, :postProcessorMethod).to_s == 'expression' ? 'complex' : 'normal'
25
57
  end
26
58
 
27
59
  def test_datasource_name(datasource)
28
60
  errors = []
61
+ name = resource_value(datasource, :name).to_s
62
+ display_name = resource_value(datasource, :displayName).to_s
29
63
 
30
- # does the name contain whitespace?
31
- errors.push('datasource name contains whitespace') if datasource.name =~ /\s+/
32
-
33
- # does the name end in a trailing dash?
34
- errors.push('datasource name has trailing dash') if datasource.name =~ /\-$/
35
-
36
- # does the display name end in a trailing dash?
37
- if datasource.displayName =~ /\-$/
38
- errors.push('datasource display name has trailing dash')
39
- end
64
+ errors << 'datasource name contains whitespace' if name.match?(/\s+/)
65
+ errors << 'datasource name has trailing dash' if name.end_with?('-')
66
+ errors << 'datasource display name has trailing dash' if display_name.end_with?('-')
40
67
 
41
68
  errors
42
69
  end
43
70
 
44
71
  def test_datasource_description(datasource)
45
- error = nil
72
+ description = resource_value(datasource, :description).to_s.strip
73
+ return nil unless description.length < 10
46
74
 
47
- # is the description size less than 10 characters in length?
48
- if datasource.description.length < 10
49
- error = 'datasource description is empty or sparse'
50
- end
51
-
52
- error
75
+ 'datasource description is empty or sparse'
53
76
  end
54
77
 
55
78
  def test_datapoint_descriptions(datapoints)
79
+ datapoints.filter_map do |datapoint|
80
+ description = resource_value(datapoint, :description).to_s.strip
81
+ next unless description.length < 10
82
+
83
+ %(datapoint "#{resource_value(datapoint, :name)}" description is empty or sparse)
84
+ end
85
+ end
86
+
87
+ def test_datapoint_alerts(datapoints)
56
88
  errors = []
89
+ tokens = %w[##HOST## ##VALUE## ##DURATION## ##START##]
57
90
 
58
91
  datapoints.each do |datapoint|
59
- if datapoint.description.length < 10
60
- errors.push("datapoint \"" + datapoint.name + "\" description is empty or sparse")
61
- end
92
+ check_alert_threshold(datapoint, errors)
93
+ check_custom_alert_message(datapoint, tokens, errors)
62
94
  end
63
95
 
64
96
  errors
65
97
  end
66
98
 
67
- def test_datapoint_alerts(datapoints)
68
- errors = []
99
+ def check_alert_threshold(datapoint, errors)
100
+ alert_expr = resource_value(datapoint, :alertExpr).to_s
101
+ alert_body = resource_value(datapoint, :alertBody).to_s
102
+ return if alert_expr.empty? || !alert_body.empty?
69
103
 
70
- tokens = [
71
- '##HOST##',
72
- '##VALUE##',
73
- '##DURATION##',
74
- '##START##'
75
- ]
104
+ errors << %(datapoint "#{resource_value(datapoint, :name)}" has an alert threshold but no message)
105
+ end
76
106
 
77
- datapoints.each do |datapoint|
78
- # is there a datapoint alert trigger set, but no custom alert message
79
- #
80
- if (datapoint.alertExpr.size > 0) && (datapoint.alertBody == 0)
81
- errors.push("datapoint \"" + datapoint.name +
82
- "\" has an alert threshold but no message")
83
- end
107
+ def check_custom_alert_message(datapoint, tokens, errors)
108
+ alert_body = resource_value(datapoint, :alertBody).to_s
109
+ return if alert_body.empty?
84
110
 
85
- # is there a custom alert message on this datapoint?
86
- #
87
- next unless datapoint.alertBody.size > 0
88
- tokens.each do |token|
89
- # is this token in the datasource definition?
90
- #
91
- unless datapoint.alertBody.include? token
92
- errors.push("custom alert message on \"" + datapoint.name +
93
- "\" datpoint doesn't include token " + token)
94
- end
95
- end
96
- end
111
+ tokens.each do |token|
112
+ next if alert_body.include?(token)
97
113
 
98
- errors
114
+ errors << %(custom alert message on "#{resource_value(datapoint, :name)}" datapoint doesn't include token #{token})
115
+ end
99
116
  end
100
117
 
101
118
  def test_datapoint_usage(datapoints, complex_datapoints, graphs, overview_graphs)
102
- errors = []
103
119
  datapoint_ok = []
104
120
 
105
121
  puts 'Datapoints:'
106
- # is an alert trigger set?
107
122
  datapoints.each do |datapoint|
108
- if datapoint.alertExpr.size > 0
109
- puts ' - ' + dp_type(datapoint) + ' datapoint "' + datapoint.name + '" has alert threshold set'
110
- else
111
-
112
- # is this datapoint used in a complex datapoint?
113
- complex_datapoints.each do |complex_datapoint|
114
- next unless complex_datapoint.postProcessorParam.include? datapoint.name
115
- puts(' - ' + dp_type(datapoint) + ' datapoint "' + datapoint.name +
116
- '" used in complex datapoint' + complex_datapoint.name)
117
- datapoint_ok.push(datapoint.name)
118
- break
119
- end
120
-
121
- # is this datapoint used in any graphs?
122
- graphs.each do |graph|
123
- graph.dataPoints.each do |graph_datapoint|
124
- next unless datapoint.name == graph_datapoint['name']
125
- puts(' - ' + dp_type(datapoint) + ' datapoint "' + datapoint.name +
126
- '" used in graph "' + graph.name + '" datapoint')
127
- datapoint_ok.push(datapoint.name)
128
- break
129
- end
130
- end
131
-
132
- overview_graphs.each do |ograph|
133
- ograph.dataPoints.each do |ograph_datapoint|
134
- next unless datapoint.name == ograph_datapoint['name']
135
- puts(' - ' + dp_type(datapoint) + ' datapoint "' + datapoint.name +
136
- '" used in overview graph "' + ograph.name + '" datapoint')
137
- datapoint_ok.push(datapoint.name)
138
- break
139
- end
140
- end
141
-
142
- unless datapoint_ok.include? datapoint.name
143
- errors.push("datapoint \"" + datapoint.name + "\" appears to be unused")
144
- end
123
+ if !resource_value(datapoint, :alertExpr).to_s.empty?
124
+ puts %( - #{dp_type(datapoint)} datapoint "#{resource_value(datapoint, :name)}" has alert threshold set)
125
+ next
145
126
  end
127
+
128
+ check_complex_datapoints(datapoint, complex_datapoints, datapoint_ok)
129
+ track_graph_usage(datapoint, graphs, datapoint_ok, 'graph')
130
+ track_graph_usage(datapoint, overview_graphs, datapoint_ok, 'overview graph')
146
131
  end
147
132
 
148
- separator
149
- errors
133
+ check_datapoints(datapoints, datapoint_ok)
150
134
  end
151
135
 
152
- def test_graphs(graphs)
153
- errors = []
136
+ def check_complex_datapoints(datapoint, complex_datapoints, datapoint_ok)
137
+ complex_datapoints.each do |complex_datapoint|
138
+ next unless resource_value(complex_datapoint, :postProcessorParam).to_s.include?(resource_value(datapoint, :name).to_s)
154
139
 
155
- puts 'Graphs:'
140
+ puts %( - #{dp_type(datapoint)} datapoint "#{resource_value(datapoint, :name)}" used in complex datapoint #{resource_value(complex_datapoint, :name)})
141
+ datapoint_ok << resource_value(datapoint, :name)
142
+ break
143
+ end
144
+ end
145
+
146
+ def track_graph_usage(datapoint, graphs, datapoint_ok, graph_type)
147
+ datapoint_name = resource_value(datapoint, :name).to_s
156
148
 
157
- display_prios = {}
158
149
  graphs.each do |graph|
159
- puts ' - "' + graph.name + '" at display priority ' + graph.displayPrio.to_s
160
- # does the y-axis label contain capital letters?
161
- if graph.verticalLabel.match(/[A-Z]/)
162
- errors.push('graph "' + graph.name +
163
- '" has uppercase letters in the y-axis definition (' +
164
- graph.verticalLabel + ')')
165
- end
150
+ extract_graph_datapoints(graph).each do |graph_datapoint|
151
+ next unless datapoint_name == graph_datapoint_name(graph_datapoint)
166
152
 
167
- # has this graph priority already been used?
168
- if display_prios.include? graph.displayPrio
169
- errors.push('graph "' + graph.name + '" is assigned the same display priority (' +
170
- graph.displayPrio.to_s + ') as "' +
171
- display_prios[graph.displayPrio] + '"')
172
- else
173
- # no -- store this priority in a hash for further testing
174
- display_prios[graph.displayPrio] = graph.name
153
+ puts %( - #{dp_type(datapoint)} datapoint "#{datapoint_name}" used in #{graph_type} "#{resource_value(graph, :name)}")
154
+ datapoint_ok << datapoint_name
155
+ break
175
156
  end
176
157
  end
158
+ end
177
159
 
160
+ def extract_graph_datapoints(graph)
161
+ Array(resource_value(graph, :dataPoints, :lines, :graphDataPoints))
162
+ end
163
+
164
+ def graph_datapoint_name(graph_datapoint)
165
+ if graph_datapoint.is_a?(LMRest::Resource)
166
+ resource_value(graph_datapoint, :name, :dataPointName)
167
+ elsif graph_datapoint.is_a?(Hash)
168
+ graph_datapoint['name'] || graph_datapoint[:name] || graph_datapoint['dataPointName'] || graph_datapoint[:dataPointName]
169
+ else
170
+ nil
171
+ end.to_s
172
+ end
173
+
174
+ def test_graphs(graphs)
175
+ puts 'Graphs:'
176
+ errors = check_graph_definitions(graphs, 'graph')
178
177
  separator
179
178
  errors
180
179
  end
181
180
 
182
181
  def test_overview_graphs(overview_graphs)
183
- errors = []
184
-
185
182
  puts 'Overview Graphs:'
183
+ errors = check_graph_definitions(overview_graphs, 'overview graph')
184
+ separator
185
+ errors
186
+ end
187
+
188
+ def check_datapoints(datapoints, used_datapoints)
189
+ used_datapoints = used_datapoints.compact.uniq
190
+
191
+ datapoints.filter_map do |datapoint|
192
+ next if used_datapoints.include?(resource_value(datapoint, :name))
193
+
194
+ %(datapoint "#{resource_value(datapoint, :name)}" appears to be unused)
195
+ end
196
+ end
186
197
 
198
+ def check_graph_definitions(graphs, graph_type)
199
+ errors = []
187
200
  display_prios = {}
188
- overview_graphs.each do |ograph|
189
- puts ' - "' + ograph.name + '" at display priority ' + ograph.displayPrio.to_s
190
- if ograph.verticalLabel.match(/[A-Z]/)
191
- errors.push('overview graph "' + ograph.name +
192
- '" has uppercase letters in the y-axis definition (' +
193
- ograph.verticalLabel + ')')
201
+
202
+ graphs.each do |graph|
203
+ name = resource_value(graph, :name).to_s
204
+ display_prio = resource_value(graph, :displayPrio, :displayPriority)
205
+ vertical_label = resource_value(graph, :verticalLabel, :yAxisLabel).to_s
206
+
207
+ puts %( - "#{name}" at display priority #{display_prio})
208
+ if vertical_label.match?(/[A-Z]/)
209
+ errors << %(#{graph_type} "#{name}" has uppercase letters in the y-axis definition (#{vertical_label}))
194
210
  end
195
211
 
196
- if display_prios.include? ograph.displayPrio
197
- errors.push('overview graph "' + ograph.name + '" is assigned the same display priority (' +
198
- ograph.displayPrio.to_s + ') as "' +
199
- display_prios[ograph.displayPrio] + '"')
200
- else
201
- display_prios[ograph.displayPrio] = ograph.name
212
+ if !display_prio.nil? && display_prios.key?(display_prio)
213
+ errors << %(#{graph_type} "#{name}" is assigned the same display priority (#{display_prio}) as "#{display_prios[display_prio]}")
214
+ elsif !display_prio.nil?
215
+ display_prios[display_prio] = name
202
216
  end
203
217
  end
204
218
 
205
- separator
206
219
  errors
207
220
  end
208
221
 
209
222
  def summarize(datasource, datapoints, graphs, overview_graphs)
210
- datapoint_alert_count = 0
211
- datapoints.each do |datapoint|
212
- datapoint_alert_count += 1 if datapoint.alertExpr.size > 0
213
- end
223
+ datapoint_alert_count = datapoints.count { |datapoint| !resource_value(datapoint, :alertExpr).to_s.empty? }
214
224
 
215
225
  puts 'Summary:'
216
-
217
- puts " - datasource name:\t#{datasource.name}"
218
- puts " - display name:\t#{datasource.displayName}"
219
- puts " - applies to:\t\t#{datasource.appliesTo}"
220
- puts " - polling interval:\t#{datasource.collectInterval / 60}m"
221
- puts " - multipoint instance:\t#{datasource.hasMultiInstances}"
222
- puts " - datapoints:\t\t#{datasource.dataPoints.count}"
226
+ puts " - datasource name:\t#{resource_value(datasource, :name)}"
227
+ puts " - display name:\t#{resource_value(datasource, :displayName)}"
228
+ puts " - description:\t\t#{resource_value(datasource, :description)}"
229
+ puts " - applies to:\t\t#{resource_value(datasource, :appliesTo)}"
230
+ puts " - polling interval:\t#{resource_value(datasource, :collectInterval).to_i / 60}m"
231
+ puts " - multipoint instance:\t#{resource_value(datasource, :hasMultiInstances)}"
232
+ puts " - datapoints:\t\t#{datapoints.count}"
223
233
  puts " - datapoint alerts:\t#{datapoint_alert_count}"
224
234
  puts " - graphs:\t\t#{graphs.count}"
225
235
  puts " - overview graphs:\t#{overview_graphs.count}"
226
-
227
236
  separator
228
237
  end
229
238
 
230
239
  def propose_fixes(errors)
231
240
  puts 'Proposed Fixes:'
232
241
 
233
- errors.flatten.each do |error|
234
- puts " * #{error}".colorize(:red)
242
+ flattened = errors.flatten.compact
243
+ if flattened.empty?
244
+ puts ' * none'
245
+ return
246
+ end
247
+
248
+ flattened.each do |error|
249
+ puts " * #{error}"
235
250
  end
236
251
  end
237
252
 
238
253
  def separator
239
- puts '============================='
254
+ puts '-' * 40
240
255
  end
241
256
 
242
- @datasources = @lm.get_datasources(filter: "name:#{ARGV[3]}")
257
+ datasources = @lm.get_datasources(filter: %(name:"#{@datasource_filter}"))
258
+ abort("No datasources matched #{@datasource_filter.inspect}") if datasources.empty?
243
259
 
244
- @datasources.each do |datasource|
245
- errors = []
246
- datapoints = @lm.get_datapoints(datasource.id)
247
- complex_datapoints = datapoints.select { |dp| dp.postProcessorMethod == 'expression' }
248
- graphs = @lm.get_graphs(datasource.id)
249
- overview_graphs = @lm.get_overview_graphs(datasource.id)
260
+ datasources.each do |datasource|
261
+ datasource = fetch_datasource_details(datasource.id)
262
+ datapoints = fetch_datapoints(datasource)
263
+ complex_datapoints = datapoints.select { |dp| dp_type(dp) == 'complex' }
264
+ graphs = fetch_standard_graphs(datasource.id)
265
+ overview_graphs = fetch_overview_graphs(datasource.id)
250
266
 
267
+ errors = []
251
268
  summarize(datasource, datapoints, graphs, overview_graphs)
252
269
  errors << test_datasource_name(datasource)
270
+ errors << test_datasource_description(datasource)
253
271
  errors << test_datapoint_descriptions(datapoints)
254
272
  errors << test_datapoint_alerts(datapoints)
255
273
  errors << test_datapoint_usage(datapoints, complex_datapoints, graphs, overview_graphs)
256
274
  errors << test_graphs(graphs)
257
275
  errors << test_overview_graphs(overview_graphs)
258
276
  propose_fixes(errors)
259
-
260
277
  separator
261
278
  end
data/bin/graphgrab.rb ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ #
4
+ #
5
+ =begin require 'lm_rest'
6
+
7
+ def usage
8
+ puts "USAGE:\t" + $PROGRAM_NAME + ' account userid passwd'
9
+ end
10
+
11
+ if ARGV.length == 3
12
+ @account = ARGV[0]
13
+ @userid = ARGV[1]
14
+ @passwd = ARGV[2]
15
+ @lm = LMRest.new(@account, @userid, @passwd)
16
+ else
17
+ usage
18
+ fail 'Bad arguments.'
19
+ end
20
+
21
+
22
+ @datasources = @lm.get_datasources(filter: "name:#{ARGV[3]}")
23
+
24
+ @datasources.each do |datasource|
25
+ errors = []
26
+ datapoints = @lm.get_datapoints(datasource.id)
27
+ complex_datapoints = datapoints.select { |dp| dp.postProcessorMethod == 'expression' }
28
+ graphs = @lm.get_graphs(datasource.id)
29
+ overview_graphs = @lm.get_overview_graphs(datasource.id)
30
+
31
+ summarize(datasource, datapoints, graphs, overview_graphs)
32
+ errors << test_datasource_name(datasource)
33
+ errors << test_datapoint_descriptions(datapoints)
34
+ errors << test_datapoint_alerts(datapoints)
35
+ errors << test_datapoint_usage(datapoints, complex_datapoints, graphs, overview_graphs)
36
+ errors << test_graphs(graphs)
37
+ errors << test_overview_graphs(overview_graphs)
38
+ propose_fixes(errors)
39
+
40
+ separator
41
+ end =end