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.
- checksums.yaml +4 -4
- data/.travis.yml +4 -0
- data/lib/wavefront-cli/base.rb +26 -10
- data/lib/wavefront-cli/base_write.rb +231 -0
- data/lib/wavefront-cli/commands/report.rb +35 -0
- data/lib/wavefront-cli/commands/write.rb +5 -2
- data/lib/wavefront-cli/controller.rb +8 -4
- data/lib/wavefront-cli/display/report.rb +17 -0
- data/lib/wavefront-cli/display/write.rb +7 -4
- data/lib/wavefront-cli/output/base.rb +10 -0
- data/lib/wavefront-cli/output/hcl.rb +20 -0
- data/lib/wavefront-cli/output/hcl/alert.rb +14 -0
- data/lib/wavefront-cli/output/hcl/base.rb +92 -0
- data/lib/wavefront-cli/output/hcl/dashboard.rb +114 -0
- data/lib/wavefront-cli/output/hcl/notificant.rb +30 -0
- data/lib/wavefront-cli/output/json.rb +12 -0
- data/lib/wavefront-cli/output/ruby.rb +12 -0
- data/lib/wavefront-cli/output/wavefront.tf +257 -0
- data/lib/wavefront-cli/output/yaml.rb +15 -0
- data/lib/wavefront-cli/report.rb +15 -0
- data/lib/wavefront-cli/string.rb +12 -1
- data/lib/wavefront-cli/version.rb +1 -1
- data/lib/wavefront-cli/write.rb +9 -221
- data/spec/wavefront-cli/string_spec.rb +24 -0
- data/wavefront-cli.gemspec +1 -1
- metadata +20 -6
@@ -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,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
|
+
}
|