wavefront-cli 2.3.1 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ require_relative 'base'
2
+
3
+ module WavefrontOutput
4
+ #
5
+ # Display objects in an HCL-compatible way, for use with the
6
+ # Wavefront Terraform provider. We farm everything out, as
7
+ # different resource types need various amounts of massaging. Args
8
+ # are passed through to the child class.
9
+ #
10
+ class Hcl < Base
11
+ def run
12
+ require_relative File.join('hcl', options[:class])
13
+ oclass = Object.const_get(format('WavefrontHclOutput::%s',
14
+ options[:class].to_s.capitalize))
15
+ oclass.new(resp, options).run
16
+ rescue LoadError
17
+ abort "no HCL output for #{options[:class]}."
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'base'
2
+
3
+ module WavefrontHclOutput
4
+ #
5
+ # Define alerts which can be understood by the Wavefront Terraform
6
+ # provider.
7
+ #
8
+ class Alert < Base
9
+ def hcl_fields
10
+ %w[name target condition additional_information display_expression
11
+ minutes resolve_after_minutes severity tags]
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,92 @@
1
+ require 'securerandom'
2
+ require 'json'
3
+
4
+ module WavefrontHclOutput
5
+ class Base
6
+ attr_reader :resp, :options
7
+
8
+ def initialize(resp, options)
9
+ @resp = resp
10
+ @options = options
11
+ end
12
+
13
+ def run
14
+ puts open_output
15
+ required_fields.each { |k, v| puts handler(k, v) }
16
+ puts close_output
17
+ end
18
+
19
+ # Fields which the provider requires.
20
+ # @return [Array] of strings
21
+ #
22
+ def hcl_fields
23
+ []
24
+ end
25
+
26
+ def open_output
27
+ format('resource "wavefront_%s" "%s" {', resource_name,
28
+ SecureRandom.uuid)
29
+ end
30
+
31
+ def close_output
32
+ '}'
33
+ end
34
+
35
+ # Override this if the provider calls a resource something other
36
+ # than the name of the inheriting class
37
+ #
38
+ def resource_name
39
+ options[:class]
40
+ end
41
+
42
+ # The provider can only handle certain keys. Each class should
43
+ # provide a list of things it knows the provider requires. If it
44
+ # does not, we display everything
45
+ #
46
+ def required_fields
47
+ return resp if hcl_fields.empty?
48
+ resp.select { |k, _v| hcl_fields.include?(k) }
49
+ end
50
+
51
+ # Format each key-value pair
52
+ # @param k [String] key
53
+ # @param v [Any] value
54
+ # @return [String]
55
+ #
56
+ def handler(k, v)
57
+ key_handler = "khandle_#{k}".to_sym
58
+ value_handler = "vhandle_#{k}".to_sym
59
+ quote_handler = "qhandle_#{k}".to_sym
60
+ k = send(key_handler) if respond_to?(key_handler)
61
+ v = send(value_handler, v) if respond_to?(value_handler)
62
+
63
+ quote_handler = :quote_value unless respond_to?(quote_handler)
64
+
65
+ format(' %s = %s', k.to_snake, send(quote_handler, v))
66
+ end
67
+
68
+ # Tags need to be in an array. They aren't always called "tags"
69
+ # by the API.
70
+ # @param v [Array,Hash,String] tags
71
+ # @return [Array] of soft-quoted tags
72
+ #
73
+ def vhandle_tags(v)
74
+ v = v.values if v.is_a?(Hash)
75
+ Array(v).flatten
76
+ end
77
+
78
+ # Some values need to be quoted, some need to be escaped etc
79
+ # etc.
80
+ # @param v [Object] value
81
+ # @return [String]
82
+ #
83
+ def quote_value(v)
84
+ case v.class.to_s.to_sym
85
+ when :String
86
+ format('"%s"', v.gsub(/\"/, '\"'))
87
+ else
88
+ v
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,114 @@
1
+ require_relative 'base'
2
+
3
+ module WavefrontHclOutput
4
+ #
5
+ # This is a rather kludgy class which generates HCL output
6
+ # suitable for the Wavefront Terraform provider. It has to work
7
+ # round a number of inconsistencies and omissions in said
8
+ # provider, and will have to change as the provider improves.
9
+ #
10
+ # It works, manually, down the hierarchy described
11
+ # in https://github.com/spaceapegames/terraform-provider-wavefront/blob/master/wavefront/resource_dashboard.go
12
+ #
13
+ class Dashboard < Base
14
+
15
+ # Top-level fields
16
+ #
17
+ def hcl_fields
18
+ %w[name description url sections parameter_details tags]
19
+ end
20
+
21
+ def khandle_sections
22
+ 'section'
23
+ end
24
+
25
+ # @param vals [Array] an array of objects
26
+ # @param fn [Symbol] a method which knows how to deal with one
27
+ # of the objects in vals
28
+ # @return [String] HCL list of vals
29
+ #
30
+ def listmaker(vals, fn)
31
+ vals.each_with_object([]) { |v, a| a.<< send(fn, v) }.to_hcl_list
32
+ end
33
+
34
+ def vhandle_sections(v)
35
+ v.each_with_object([]) do |section, a|
36
+ a.<< ("name = \"#{section[:name]}\"\n row = " +
37
+ handle_rows(section[:rows])).braced(4)
38
+ end.to_hcl_list
39
+ end
40
+
41
+ def handle_rows(rows)
42
+ rows.each_with_object([]) do |row, a|
43
+ a.<< ("chart = " + handle_charts(row[:charts]).to_s).braced(8)
44
+ end.to_hcl_list
45
+ end
46
+
47
+ def handle_charts(charts)
48
+ listmaker(charts, :handle_chart)
49
+ end
50
+
51
+ def handle_chart(chart)
52
+ fields = %w[units name description]
53
+
54
+ lines = chart.each_with_object([]) do |(k, v), a|
55
+ a.<< format('%s = %s', k, quote_value(v)) if fields.include?(k)
56
+ end
57
+
58
+ lines.<< "source = #{handle_sources(chart[:sources])}"
59
+ lines.to_hcl_obj(10)
60
+ end
61
+
62
+ def handle_sources(sources)
63
+ listmaker(sources, :handle_source)
64
+ end
65
+
66
+ def handle_source(source)
67
+ fields = %w[name query disabled scatterPlotSource querybuilderEnabled
68
+ sourceDescription]
69
+
70
+ source.each_with_object([]) do |(k, v), a|
71
+ if fields.include?(k)
72
+ k = 'queryBuilderEnabled' if k == 'querybuilderEnabled'
73
+ a.<< format('%s = %s', k.to_snake, quote_value(v))
74
+ end
75
+ end.to_hcl_obj(14)
76
+ end
77
+
78
+ def qhandle_sections(v)
79
+ v
80
+ end
81
+
82
+ def quote_value(v)
83
+ v.gsub!(/\$/, '$$') if v.is_a?(String)
84
+ super
85
+ end
86
+ end
87
+ end
88
+
89
+ class String
90
+ def braced(indent = 0)
91
+ pad = ' ' * indent
92
+ "\n#{pad}{#{self}\n#{pad}}"
93
+ end
94
+ end
95
+
96
+ class Array
97
+ #
98
+ # Turn an array into a string which represents an HCL list
99
+ # @return [String]
100
+ #
101
+ def to_hcl_list
102
+ '[' + self.join(',') + ']'
103
+ end
104
+
105
+ # Turn an array into a string which represents an HCL object
106
+ # @return [String]
107
+ #
108
+ def to_hcl_obj(indent = 0)
109
+ outpad = ' ' * indent
110
+ inpad = ' ' * (indent + 2)
111
+
112
+ "\n#{outpad}{\n#{inpad}" + self.join("\n#{inpad}") + "\n#{outpad}}"
113
+ end
114
+ end
@@ -0,0 +1,30 @@
1
+ require_relative 'base'
2
+
3
+ module WavefrontHclOutput
4
+ #
5
+ # Munge notificant output into something compatible with the
6
+ # Wavefront Terraform provider
7
+ #
8
+ class Notificant < Base
9
+ def hcl_fields
10
+ %w[title description triggers template method recipient emailSubject
11
+ contentType customHttpHeaders]
12
+ end
13
+
14
+ def vhandle_template(v)
15
+ v.gsub(/\s*\n/, '')
16
+ end
17
+
18
+ def resource_name
19
+ 'alert_target'
20
+ end
21
+
22
+ def khandle_title
23
+ 'name'
24
+ end
25
+
26
+ def khandle_customHttpHeaders
27
+ 'custom_headers'
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,12 @@
1
+ require_relative 'base'
2
+
3
+ module WavefrontOutput
4
+ #
5
+ # Display as JSON
6
+ #
7
+ class Json < Base
8
+ def run
9
+ puts resp.to_json
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require_relative 'base'
2
+
3
+ module WavefrontOutput
4
+ #
5
+ # Display as a raw Ruby object
6
+ #
7
+ class Ruby < Base
8
+ def run
9
+ p resp
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,257 @@
1
+ MERP MERP
2
+ resource "wavefront_dashboard" "9f8d3f29-d965-4f7e-8473-43571e431b99" {
3
+ name = "JPC Website Host"
4
+ description = "overview of SmartOS zones"
5
+ url = "jpc-webhost"
6
+ section = [
7
+ { name = "Memory"
8
+ row = [
9
+ { chart = [
10
+ {
11
+ units = "B"
12
+ name = "Swap Free"
13
+ description = "Remember, on Solaris, \"swap\" means backing store, so this is all memory."
14
+ source = [
15
+ {
16
+ scatter_plot_source = "Y"
17
+ query_builder_enabled = false
18
+ source_description = ""
19
+ name = "New Query"
20
+ query = "ts(\"tenant.swapresv.value\", env=${env}) - ts(\"tenant.swapresv.usage\", env=${env})"
21
+ }]
22
+ },
23
+ {
24
+ units = ""
25
+ name = "Free Physical Memory"
26
+ source = [
27
+ {
28
+ scatter_plot_source = "Y"
29
+ query_builder_enabled = false
30
+ source_description = ""
31
+ name = "physical memory"
32
+ query = "ts(\"tenant.physicalmem.value\", env=${env}) - ts(\"tenant.physicalmem.usage\", env=${env})"
33
+ },
34
+ {
35
+ scatter_plot_source = "Y"
36
+ query_builder_enabled = true
37
+ source_description = ""
38
+ name = "out of memory"
39
+ query = "0"
40
+ }]
41
+ },
42
+ {
43
+ units = ""
44
+ name = "times over memory"
45
+ source = [
46
+ {
47
+ scatter_plot_source = "Y"
48
+ query_builder_enabled = false
49
+ source_description = ""
50
+ name = "times over memory"
51
+ query = "deriv(ts(\"tenant.memory_cap.nover\", env=${env}))"
52
+ }]
53
+ }]
54
+ },
55
+ { chart = [
56
+ {
57
+ units = "B"
58
+ name = "Sinatra application memory"
59
+ source = [
60
+ {
61
+ scatter_plot_source = "Y"
62
+ query_builder_enabled = false
63
+ source_description = ""
64
+ name = "New Query"
65
+ query = "sum(ts(\"process.memory.pr_rssize.*\", svc=\"svc:/application/sinatra/*\" and env=${env}), svc)"
66
+ }]
67
+ },
68
+ {
69
+ units = ""
70
+ name = "top memory consumers"
71
+ source = [
72
+ {
73
+ scatter_plot_source = "Y"
74
+ query_builder_enabled = false
75
+ source_description = ""
76
+ name = "by process name"
77
+ query = "sum(ts(\"process.memory.pr_rssize.*\", env=${env}), metrics)"
78
+ }]
79
+ }]
80
+ }]
81
+ },
82
+ { name = "nginx"
83
+ row = [
84
+ { chart = [
85
+ {
86
+ units = "req/s"
87
+ name = "\"200\" HTTP requests"
88
+ source = [
89
+ {
90
+ scatter_plot_source = "Y"
91
+ query_builder_enabled = false
92
+ source_description = ""
93
+ name = "Query"
94
+ query = "rate(ts(\"nginx.code.200\"))"
95
+ }]
96
+ },
97
+ {
98
+ units = "ms"
99
+ name = "request latency"
100
+ source = [
101
+ {
102
+ scatter_plot_source = "Y"
103
+ query_builder_enabled = false
104
+ source_description = ""
105
+ name = "total response time"
106
+ query = "ts(\"nginx.time.response.*\")"
107
+ },
108
+ {
109
+ scatter_plot_source = "Y"
110
+ query_builder_enabled = false
111
+ source_description = ""
112
+ name = "upstream response time"
113
+ query = "ts(\"nginx.time.upstream_response.*\")"
114
+ }]
115
+ },
116
+ {
117
+ units = "req/s"
118
+ name = "HTTP Errors"
119
+ source = [
120
+ {
121
+ scatter_plot_source = "Y"
122
+ query_builder_enabled = false
123
+ source_description = ""
124
+ name = "40x"
125
+ query = "ceil(rate(ts(\"nginx.code.4*\")))"
126
+ },
127
+ {
128
+ scatter_plot_source = "Y"
129
+ query_builder_enabled = false
130
+ source_description = ""
131
+ name = "50x"
132
+ query = "ceil(rate(ts(\"nginx.code.5*\")))"
133
+ }]
134
+ }]
135
+ }]
136
+ },
137
+ { name = "Processes and Services"
138
+ row = [
139
+ { chart = [
140
+ {
141
+ units = "processes"
142
+ name = "processes"
143
+ description = "number active processes in zone"
144
+ source = [
145
+ {
146
+ scatter_plot_source = "Y"
147
+ query_builder_enabled = true
148
+ source_description = ""
149
+ name = "running processes"
150
+ query = "ts(\"tenant.nprocs.usage\", env=${env})"
151
+ }]
152
+ },
153
+ {
154
+ units = "services"
155
+ name = "Service States"
156
+ description = "count of services in each possible state. Disabled service are not shown,"
157
+ source = [
158
+ {
159
+ scatter_plot_source = "Y"
160
+ query_builder_enabled = false
161
+ source_description = ""
162
+ name = "New Query"
163
+ query = "ts(\"smf.svcs.*\", env=${env})"
164
+ }]
165
+ }]
166
+ }]
167
+ },
168
+ { name = "Network"
169
+ row = [
170
+ { chart = [
171
+ {
172
+ units = "bytes"
173
+ name = "Network In"
174
+ source = [
175
+ {
176
+ scatter_plot_source = "Y"
177
+ query_builder_enabled = false
178
+ source_description = ""
179
+ name = "New Query"
180
+ query = "deriv(ts(\"network.*.net0.rbytes64\", env=${env}))"
181
+ }]
182
+ },
183
+ {
184
+ units = "bytes"
185
+ name = "Network Out"
186
+ source = [
187
+ {
188
+ scatter_plot_source = "Y"
189
+ query_builder_enabled = false
190
+ source_description = ""
191
+ name = "network out"
192
+ query = "deriv(ts(\"network.*.net0.obytes64\", env=${env}))"
193
+ }]
194
+ }]
195
+ },
196
+ { chart = [
197
+ {
198
+ units = "s"
199
+ name = "Puppet Run Time"
200
+ source = [
201
+ {
202
+ scatter_plot_source = "Y"
203
+ query_builder_enabled = false
204
+ source_description = ""
205
+ name = "time"
206
+ query = "ts(\"puppet.time.*\", env=${env})"
207
+ }]
208
+ },
209
+ {
210
+ units = "Units"
211
+ name = "Puppet Changes"
212
+ description = ""
213
+ source = [
214
+ {
215
+ scatter_plot_source = "Y"
216
+ query_builder_enabled = false
217
+ source_description = ""
218
+ name = "New Query"
219
+ query = "ts(\"puppet.changes.total\")"
220
+ }]
221
+ }]
222
+ }]
223
+ },
224
+ { name = "Page Impressions"
225
+ row = [
226
+ { chart = [
227
+ {
228
+ units = ""
229
+ name = "Most read posts"
230
+ source = [
231
+ {
232
+ scatter_plot_source = "Y"
233
+ query_builder_enabled = false
234
+ source_description = ""
235
+ name = "Query"
236
+ query = "sum(ts(\"nginx.code.200\", vhost=sysdef.xyz and path=\"/post/*\"), pointTags)"
237
+ }]
238
+ }]
239
+ },
240
+ { chart = [
241
+ {
242
+ units = ""
243
+ name = "most viewed pieces"
244
+ description = ""
245
+ source = [
246
+ {
247
+ scatter_plot_source = "Y"
248
+ query_builder_enabled = false
249
+ source_description = ""
250
+ name = "Query"
251
+ query = "sum(ts(\"nginx.code.200\", vhost=\"rdfisher.co.uk\" and path=\"/piece/*\"), pointTags)"
252
+ }]
253
+ }]
254
+ }]
255
+ }]
256
+ tags = []
257
+ }