wavefront-cli 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +20 -0
- data/.gitignore +4 -0
- data/.travis.yml +16 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +65 -0
- data/README.md +221 -0
- data/Rakefile +18 -0
- data/bin/wavefront +14 -0
- data/lib/wavefront-cli/alert.rb +60 -0
- data/lib/wavefront-cli/base.rb +320 -0
- data/lib/wavefront-cli/cloudintegration.rb +12 -0
- data/lib/wavefront-cli/commands/alert.rb +38 -0
- data/lib/wavefront-cli/commands/base.rb +105 -0
- data/lib/wavefront-cli/commands/dashboard.rb +29 -0
- data/lib/wavefront-cli/commands/event.rb +44 -0
- data/lib/wavefront-cli/commands/integration.rb +33 -0
- data/lib/wavefront-cli/commands/link.rb +34 -0
- data/lib/wavefront-cli/commands/message.rb +23 -0
- data/lib/wavefront-cli/commands/metric.rb +20 -0
- data/lib/wavefront-cli/commands/proxy.rb +25 -0
- data/lib/wavefront-cli/commands/query.rb +32 -0
- data/lib/wavefront-cli/commands/savedsearch.rb +32 -0
- data/lib/wavefront-cli/commands/source.rb +27 -0
- data/lib/wavefront-cli/commands/user.rb +24 -0
- data/lib/wavefront-cli/commands/webhook.rb +25 -0
- data/lib/wavefront-cli/commands/window.rb +33 -0
- data/lib/wavefront-cli/commands/write.rb +35 -0
- data/lib/wavefront-cli/constants.rb +17 -0
- data/lib/wavefront-cli/controller.rb +134 -0
- data/lib/wavefront-cli/dashboard.rb +27 -0
- data/lib/wavefront-cli/display/alert.rb +44 -0
- data/lib/wavefront-cli/display/base.rb +304 -0
- data/lib/wavefront-cli/display/cloudintegration.rb +18 -0
- data/lib/wavefront-cli/display/dashboard.rb +21 -0
- data/lib/wavefront-cli/display/event.rb +19 -0
- data/lib/wavefront-cli/display/externallink.rb +13 -0
- data/lib/wavefront-cli/display/maintenancewindow.rb +19 -0
- data/lib/wavefront-cli/display/message.rb +8 -0
- data/lib/wavefront-cli/display/metric.rb +22 -0
- data/lib/wavefront-cli/display/proxy.rb +13 -0
- data/lib/wavefront-cli/display/query.rb +69 -0
- data/lib/wavefront-cli/display/savedsearch.rb +17 -0
- data/lib/wavefront-cli/display/source.rb +26 -0
- data/lib/wavefront-cli/display/user.rb +16 -0
- data/lib/wavefront-cli/display/webhook.rb +24 -0
- data/lib/wavefront-cli/display/write.rb +19 -0
- data/lib/wavefront-cli/event.rb +162 -0
- data/lib/wavefront-cli/exception.rb +5 -0
- data/lib/wavefront-cli/externallink.rb +16 -0
- data/lib/wavefront-cli/maintenancewindow.rb +16 -0
- data/lib/wavefront-cli/message.rb +19 -0
- data/lib/wavefront-cli/metric.rb +24 -0
- data/lib/wavefront-cli/opt_handler.rb +62 -0
- data/lib/wavefront-cli/proxy.rb +22 -0
- data/lib/wavefront-cli/query.rb +74 -0
- data/lib/wavefront-cli/savedsearch.rb +24 -0
- data/lib/wavefront-cli/source.rb +20 -0
- data/lib/wavefront-cli/user.rb +25 -0
- data/lib/wavefront-cli/version.rb +1 -0
- data/lib/wavefront-cli/webhook.rb +8 -0
- data/lib/wavefront-cli/write.rb +244 -0
- data/spec/spec_helper.rb +197 -0
- data/spec/wavefront-cli/alert_spec.rb +44 -0
- data/spec/wavefront-cli/base_spec.rb +47 -0
- data/spec/wavefront-cli/cli_help_spec.rb +47 -0
- data/spec/wavefront-cli/cloudintegration_spec.rb +24 -0
- data/spec/wavefront-cli/dashboard_spec.rb +37 -0
- data/spec/wavefront-cli/event_spec.rb +19 -0
- data/spec/wavefront-cli/externallink_spec.rb +18 -0
- data/spec/wavefront-cli/maintanancewindow_spec.rb +19 -0
- data/spec/wavefront-cli/message_spec.rb +28 -0
- data/spec/wavefront-cli/metric_spec.rb +22 -0
- data/spec/wavefront-cli/proxy_spec.rb +26 -0
- data/spec/wavefront-cli/query_spec.rb +63 -0
- data/spec/wavefront-cli/resources/conf.yaml +10 -0
- data/spec/wavefront-cli/savedsearch_spec.rb +18 -0
- data/spec/wavefront-cli/source_spec.rb +18 -0
- data/spec/wavefront-cli/user_spec.rb +31 -0
- data/spec/wavefront-cli/webhook_spec.rb +17 -0
- data/wavefront-cli.gemspec +36 -0
- metadata +279 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative './base'
|
2
|
+
|
3
|
+
module WavefrontDisplay
|
4
|
+
#
|
5
|
+
# Format human-readable output for cloud integrations.
|
6
|
+
#
|
7
|
+
class CloudIntegration < Base
|
8
|
+
def do_list_brief
|
9
|
+
terse_output(:id, :service)
|
10
|
+
end
|
11
|
+
|
12
|
+
def do_describe
|
13
|
+
readable_time(:lastReceivedDataPointMs, :lastProcessingTimestamp)
|
14
|
+
drop_fields(:forceSave)
|
15
|
+
long_output
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative './base'
|
2
|
+
|
3
|
+
module WavefrontDisplay
|
4
|
+
#
|
5
|
+
# Format human-readable output for dashboards.
|
6
|
+
#
|
7
|
+
class Dashboard < Base
|
8
|
+
def do_list
|
9
|
+
long_output [:id, :minutes, :target, :status, :tags, :hostsUsed,
|
10
|
+
:condition, :displayExpression, :severity,
|
11
|
+
:additionalInformation]
|
12
|
+
end
|
13
|
+
|
14
|
+
def do_describe
|
15
|
+
drop_fields(:parameterDetails)
|
16
|
+
readable_time(:updatedEpochMillis)
|
17
|
+
data[:sections] = data[:sections].map { |s| s[:name] }
|
18
|
+
long_output
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'date'
|
2
|
+
require_relative './base'
|
3
|
+
|
4
|
+
module WavefrontDisplay
|
5
|
+
#
|
6
|
+
# Format human-readable output for events.
|
7
|
+
#
|
8
|
+
class Event < Base
|
9
|
+
def do_describe
|
10
|
+
readable_time(:startTime, :endTime, :updatedAt, :createdAt,
|
11
|
+
:createdEpochMillis, :updatedEpochMillis)
|
12
|
+
long_output
|
13
|
+
end
|
14
|
+
|
15
|
+
def do_list_brief
|
16
|
+
terse_output(:id, :runningState)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative './base'
|
2
|
+
|
3
|
+
module WavefrontDisplay
|
4
|
+
#
|
5
|
+
# Format human-readable output for maintenance windows.
|
6
|
+
#
|
7
|
+
class MaintenanceWindow < Base
|
8
|
+
def do_describe
|
9
|
+
readable_time(:startTimeInSeconds, :endTimeInSeconds,
|
10
|
+
:createdEpochMillis, :updatedEpochMillis)
|
11
|
+
drop_fields(:hostTagGroupHostNamesGroupAnded, :relevantHostTagsAnded)
|
12
|
+
long_output
|
13
|
+
end
|
14
|
+
|
15
|
+
def do_list_brief
|
16
|
+
terse_output(:id, :title)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative './base'
|
2
|
+
|
3
|
+
module WavefrontDisplay
|
4
|
+
#
|
5
|
+
# Format human-readable output for metrics.
|
6
|
+
#
|
7
|
+
class Metric < Base
|
8
|
+
def do_describe
|
9
|
+
if data.hosts.empty?
|
10
|
+
puts "No matches."
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
|
14
|
+
modified_data = data['hosts'].map do |h, aggr|
|
15
|
+
{ host: h[:host],
|
16
|
+
last_update: human_time(h[:last_update]) }
|
17
|
+
end.sort_by{ |h| h[:last_update] }.reverse
|
18
|
+
|
19
|
+
terse_output(:host, :last_update, modified_data)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require_relative './base'
|
2
|
+
|
3
|
+
module WavefrontDisplay
|
4
|
+
#
|
5
|
+
# Format human-readable output for queries.
|
6
|
+
#
|
7
|
+
class Query < Base
|
8
|
+
def do_default
|
9
|
+
|
10
|
+
ts = if data.key?(:timeseries)
|
11
|
+
data[:timeseries].each do |s|
|
12
|
+
s[:data] = humanize_series(s[:data])
|
13
|
+
end
|
14
|
+
else
|
15
|
+
[]
|
16
|
+
end
|
17
|
+
|
18
|
+
events = if data.key?(:events)
|
19
|
+
data[:events].map { |s| humanize_event(s) }
|
20
|
+
else
|
21
|
+
[]
|
22
|
+
end
|
23
|
+
|
24
|
+
new = {
|
25
|
+
name: data.name,
|
26
|
+
query: data.query,
|
27
|
+
timeseries: ts,
|
28
|
+
events: events
|
29
|
+
}
|
30
|
+
|
31
|
+
@data = new
|
32
|
+
long_output
|
33
|
+
end
|
34
|
+
|
35
|
+
def do_raw
|
36
|
+
data.each { |ts| puts humanize_series(ts[:points]).join("\n") }
|
37
|
+
end
|
38
|
+
|
39
|
+
def do_raw_404
|
40
|
+
puts 'API 404: metric does not exist.'
|
41
|
+
end
|
42
|
+
|
43
|
+
def humanize_event(data)
|
44
|
+
data[:start] = human_time(data[:start])
|
45
|
+
data[:end] = human_time(data[:end]) if data[:end]
|
46
|
+
data.delete(:isEphemeral)
|
47
|
+
data
|
48
|
+
end
|
49
|
+
|
50
|
+
def humanize_series(data)
|
51
|
+
last_date = nil
|
52
|
+
|
53
|
+
data.map! do |row|
|
54
|
+
if row.is_a?(Hash)
|
55
|
+
ht = human_time(row[:timestamp])
|
56
|
+
val = row[:value]
|
57
|
+
else
|
58
|
+
ht = human_time(row[0])
|
59
|
+
val = row[1]
|
60
|
+
end
|
61
|
+
|
62
|
+
date, time = ht.split
|
63
|
+
ds = date == last_date ? '' : date
|
64
|
+
last_date = date
|
65
|
+
format('%12s %s %s', ds, time, val)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative './base'
|
2
|
+
|
3
|
+
module WavefrontDisplay
|
4
|
+
#
|
5
|
+
# Format human-readable output for saved searches.
|
6
|
+
#
|
7
|
+
class SavedSearch < Base
|
8
|
+
def do_describe
|
9
|
+
readable_time(:createdEpochMillis, :updatedEpochMillis)
|
10
|
+
long_output
|
11
|
+
end
|
12
|
+
|
13
|
+
def do_list_brief
|
14
|
+
terse_output(:id, :entityType)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative './base'
|
2
|
+
|
3
|
+
module WavefrontDisplay
|
4
|
+
#
|
5
|
+
# Format human-readable output for webhooks.
|
6
|
+
#
|
7
|
+
class Source < Base
|
8
|
+
def do_list
|
9
|
+
drop_cluster_sources
|
10
|
+
long_output
|
11
|
+
end
|
12
|
+
|
13
|
+
def do_list_brief
|
14
|
+
drop_cluster_sources
|
15
|
+
terse_output(:id, :description)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Filter out the Wavefront cluster sources
|
19
|
+
#
|
20
|
+
def drop_cluster_sources
|
21
|
+
data.sort_by! { |k, _v| k }
|
22
|
+
return if options[:all]
|
23
|
+
data.delete_if { |k| k.id =~ /prod-[\da-f]{2}-/ }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative './base'
|
2
|
+
|
3
|
+
module WavefrontDisplay
|
4
|
+
#
|
5
|
+
# Format human-readable output for user management.
|
6
|
+
#
|
7
|
+
class User < Base
|
8
|
+
def do_list_brief
|
9
|
+
data.each { |user| puts user[:identifier] }
|
10
|
+
end
|
11
|
+
|
12
|
+
def do_delete
|
13
|
+
puts "Deleted user '#{options[:'<id>']}."
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative './base'
|
2
|
+
|
3
|
+
module WavefrontDisplay
|
4
|
+
#
|
5
|
+
# Format human-readable output for webhooks.
|
6
|
+
#
|
7
|
+
class Webhook < Base
|
8
|
+
def do_list
|
9
|
+
long_output([:id, :description, :createdEpochMillis,
|
10
|
+
:updatedEpochMillis, :updaterId, :creatorId,
|
11
|
+
:title])
|
12
|
+
end
|
13
|
+
|
14
|
+
def do_list_brief
|
15
|
+
terse_output(:id, :title)
|
16
|
+
end
|
17
|
+
|
18
|
+
def do_describe
|
19
|
+
readable_time(:createdEpochMillis, :updatedEpochMillis)
|
20
|
+
drop_fields(:template)
|
21
|
+
long_output
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative './base'
|
2
|
+
|
3
|
+
module WavefrontDisplay
|
4
|
+
# Format human-readable output when writing points.
|
5
|
+
#
|
6
|
+
class Write < Base
|
7
|
+
def do_point
|
8
|
+
[:sent, :rejected, :unsent].each do |k|
|
9
|
+
puts format(' %12s %d', k.to_s, data[k])
|
10
|
+
end
|
11
|
+
|
12
|
+
exit(data.rejected.zero? && data.unsent.zero? ? 0 : 1)
|
13
|
+
end
|
14
|
+
|
15
|
+
def do_file
|
16
|
+
do_point
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'wavefront-sdk/mixins'
|
3
|
+
require_relative './base'
|
4
|
+
|
5
|
+
EVENT_STATE_DIR = Pathname.new('/var/tmp/wavefront')
|
6
|
+
|
7
|
+
module WavefrontCli
|
8
|
+
#
|
9
|
+
# CLI coverage for the v2 'event' API.
|
10
|
+
#
|
11
|
+
class Event < WavefrontCli::Base
|
12
|
+
attr_reader :state_dir
|
13
|
+
include Wavefront::Mixins
|
14
|
+
|
15
|
+
def post_initialize(_options)
|
16
|
+
begin
|
17
|
+
@state_dir = EVENT_STATE_DIR + Etc.getlogin
|
18
|
+
rescue
|
19
|
+
@state_dir = EVENT_STATE_DIR + 'notty'
|
20
|
+
end
|
21
|
+
|
22
|
+
create_state_dir
|
23
|
+
end
|
24
|
+
|
25
|
+
def do_list
|
26
|
+
options[:start] = Time.now - 600 unless options[:start]
|
27
|
+
options[:end] = Time.now unless options[:end]
|
28
|
+
wf.list(options[:start], options[:end], options[:limit] || 100,
|
29
|
+
options[:offset] || nil)
|
30
|
+
end
|
31
|
+
|
32
|
+
def do_update
|
33
|
+
k, v = options[:'<key=value>'].split('=')
|
34
|
+
wf.update(options[:'<id>'], k => v)
|
35
|
+
end
|
36
|
+
|
37
|
+
def do_create
|
38
|
+
options[:start] = Time.now unless options[:start]
|
39
|
+
|
40
|
+
t_start = parse_time(options[:start], true)
|
41
|
+
id = [t_start, options[:'<event>']].join(':')
|
42
|
+
|
43
|
+
body = { name: options[:'<event>'],
|
44
|
+
startTime: t_start,
|
45
|
+
id: id,
|
46
|
+
annotations: {} }
|
47
|
+
|
48
|
+
body[:annotations][:details] = options[:desc] if options[:desc]
|
49
|
+
body[:annotations][:severity] = options[:severity] if options[:severity]
|
50
|
+
body[:annotations][:type] = options[:type] if options[:type]
|
51
|
+
body[:hosts] = options[:host] if options[:host]
|
52
|
+
|
53
|
+
if options[:instant]
|
54
|
+
body[:endTime] = t_start + 1
|
55
|
+
elsif options[:end]
|
56
|
+
body[:endTime] = parse_time(options[:end], true)
|
57
|
+
end
|
58
|
+
|
59
|
+
resp = wf.create(body)
|
60
|
+
|
61
|
+
unless options[:nostate] || options[:end] || options[:instant]
|
62
|
+
create_state_file(id, options[:host])
|
63
|
+
end
|
64
|
+
|
65
|
+
resp
|
66
|
+
end
|
67
|
+
|
68
|
+
# The user doesn't have to give us an event ID. If no event
|
69
|
+
# name is given, we'll pop the last event off the stack. If an
|
70
|
+
# event name is given and it doesn't look like a full WF event
|
71
|
+
# name, we'll look for something on the stack. If it does look
|
72
|
+
# like a real event, we'll make and API call straight away.
|
73
|
+
#
|
74
|
+
def do_close
|
75
|
+
ev_file = nil
|
76
|
+
|
77
|
+
ev = if options[:'<id>'] == false
|
78
|
+
pop_event
|
79
|
+
elsif options[:'<id>'] =~ /^\d{13}:.+/
|
80
|
+
ev_file = state_dir + options[:'<id>']
|
81
|
+
options[:'<id>']
|
82
|
+
else
|
83
|
+
pop_event(options[:'<id>'])
|
84
|
+
end
|
85
|
+
|
86
|
+
abort "No locally stored event matches '#{options[:'<id>']}'." unless ev
|
87
|
+
|
88
|
+
res = wf.close(ev)
|
89
|
+
ev_file.unlink if ev_file && ev_file.exist? && res.status.code == 200
|
90
|
+
res
|
91
|
+
end
|
92
|
+
|
93
|
+
def do_show
|
94
|
+
begin
|
95
|
+
events = state_dir.children
|
96
|
+
rescue Errno::ENOENT
|
97
|
+
raise 'There is no event state directory on this host.'
|
98
|
+
end
|
99
|
+
|
100
|
+
if events.size.zero?
|
101
|
+
puts 'No open events.'
|
102
|
+
else
|
103
|
+
events.sort.reverse.each { |e| puts e.basename }
|
104
|
+
end
|
105
|
+
|
106
|
+
exit
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
# Write a state file. We put the hosts bound to the event into the
|
112
|
+
# file. These aren't currently used by anything in the CLI, but they
|
113
|
+
# might be useful to someone, somewhere, someday.
|
114
|
+
#
|
115
|
+
def create_state_file(id, hosts = [])
|
116
|
+
puts "statfile fir #{id}"
|
117
|
+
fname = state_dir + id
|
118
|
+
|
119
|
+
begin
|
120
|
+
File.open(fname, 'w') { hosts.to_s }
|
121
|
+
rescue
|
122
|
+
raise 'Event was created but state file was not.'
|
123
|
+
end
|
124
|
+
|
125
|
+
puts "Event state recorded at #{fname}."
|
126
|
+
end
|
127
|
+
|
128
|
+
def create_state_dir
|
129
|
+
FileUtils.mkdir_p(state_dir)
|
130
|
+
return true if state_dir.exist? && state_dir.directory? &&
|
131
|
+
state_dir.writable?
|
132
|
+
raise 'Cannot create state directory.'
|
133
|
+
end
|
134
|
+
|
135
|
+
def validate_input
|
136
|
+
validate_id if options[:'<id>'] && !options[:close]
|
137
|
+
validate_tags if options[:'<tag>']
|
138
|
+
send(:extra_validation) if respond_to?(:extra_validation)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Get the last event this script created. If you supply a name, you
|
142
|
+
# get the last event with that name. If not, you get the last event.
|
143
|
+
# Chances are you'll only ever have one in-play at once.
|
144
|
+
#
|
145
|
+
# @param name [String] name of event
|
146
|
+
# Returns an array of [timestamp, event_name]
|
147
|
+
#
|
148
|
+
def pop_event(name = false)
|
149
|
+
return false unless state_dir.exist?
|
150
|
+
|
151
|
+
list = state_dir.children
|
152
|
+
|
153
|
+
list.select! { |f| f.basename.to_s.split(':').last == name } if name
|
154
|
+
|
155
|
+
return false if list.empty?
|
156
|
+
|
157
|
+
ev_file = list.sort.last
|
158
|
+
File.unlink(ev_file)
|
159
|
+
ev_file.basename.to_s
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|