wavefront-cli 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +20 -0
  3. data/.gitignore +4 -0
  4. data/.travis.yml +16 -0
  5. data/Gemfile +2 -0
  6. data/Gemfile.lock +65 -0
  7. data/README.md +221 -0
  8. data/Rakefile +18 -0
  9. data/bin/wavefront +14 -0
  10. data/lib/wavefront-cli/alert.rb +60 -0
  11. data/lib/wavefront-cli/base.rb +320 -0
  12. data/lib/wavefront-cli/cloudintegration.rb +12 -0
  13. data/lib/wavefront-cli/commands/alert.rb +38 -0
  14. data/lib/wavefront-cli/commands/base.rb +105 -0
  15. data/lib/wavefront-cli/commands/dashboard.rb +29 -0
  16. data/lib/wavefront-cli/commands/event.rb +44 -0
  17. data/lib/wavefront-cli/commands/integration.rb +33 -0
  18. data/lib/wavefront-cli/commands/link.rb +34 -0
  19. data/lib/wavefront-cli/commands/message.rb +23 -0
  20. data/lib/wavefront-cli/commands/metric.rb +20 -0
  21. data/lib/wavefront-cli/commands/proxy.rb +25 -0
  22. data/lib/wavefront-cli/commands/query.rb +32 -0
  23. data/lib/wavefront-cli/commands/savedsearch.rb +32 -0
  24. data/lib/wavefront-cli/commands/source.rb +27 -0
  25. data/lib/wavefront-cli/commands/user.rb +24 -0
  26. data/lib/wavefront-cli/commands/webhook.rb +25 -0
  27. data/lib/wavefront-cli/commands/window.rb +33 -0
  28. data/lib/wavefront-cli/commands/write.rb +35 -0
  29. data/lib/wavefront-cli/constants.rb +17 -0
  30. data/lib/wavefront-cli/controller.rb +134 -0
  31. data/lib/wavefront-cli/dashboard.rb +27 -0
  32. data/lib/wavefront-cli/display/alert.rb +44 -0
  33. data/lib/wavefront-cli/display/base.rb +304 -0
  34. data/lib/wavefront-cli/display/cloudintegration.rb +18 -0
  35. data/lib/wavefront-cli/display/dashboard.rb +21 -0
  36. data/lib/wavefront-cli/display/event.rb +19 -0
  37. data/lib/wavefront-cli/display/externallink.rb +13 -0
  38. data/lib/wavefront-cli/display/maintenancewindow.rb +19 -0
  39. data/lib/wavefront-cli/display/message.rb +8 -0
  40. data/lib/wavefront-cli/display/metric.rb +22 -0
  41. data/lib/wavefront-cli/display/proxy.rb +13 -0
  42. data/lib/wavefront-cli/display/query.rb +69 -0
  43. data/lib/wavefront-cli/display/savedsearch.rb +17 -0
  44. data/lib/wavefront-cli/display/source.rb +26 -0
  45. data/lib/wavefront-cli/display/user.rb +16 -0
  46. data/lib/wavefront-cli/display/webhook.rb +24 -0
  47. data/lib/wavefront-cli/display/write.rb +19 -0
  48. data/lib/wavefront-cli/event.rb +162 -0
  49. data/lib/wavefront-cli/exception.rb +5 -0
  50. data/lib/wavefront-cli/externallink.rb +16 -0
  51. data/lib/wavefront-cli/maintenancewindow.rb +16 -0
  52. data/lib/wavefront-cli/message.rb +19 -0
  53. data/lib/wavefront-cli/metric.rb +24 -0
  54. data/lib/wavefront-cli/opt_handler.rb +62 -0
  55. data/lib/wavefront-cli/proxy.rb +22 -0
  56. data/lib/wavefront-cli/query.rb +74 -0
  57. data/lib/wavefront-cli/savedsearch.rb +24 -0
  58. data/lib/wavefront-cli/source.rb +20 -0
  59. data/lib/wavefront-cli/user.rb +25 -0
  60. data/lib/wavefront-cli/version.rb +1 -0
  61. data/lib/wavefront-cli/webhook.rb +8 -0
  62. data/lib/wavefront-cli/write.rb +244 -0
  63. data/spec/spec_helper.rb +197 -0
  64. data/spec/wavefront-cli/alert_spec.rb +44 -0
  65. data/spec/wavefront-cli/base_spec.rb +47 -0
  66. data/spec/wavefront-cli/cli_help_spec.rb +47 -0
  67. data/spec/wavefront-cli/cloudintegration_spec.rb +24 -0
  68. data/spec/wavefront-cli/dashboard_spec.rb +37 -0
  69. data/spec/wavefront-cli/event_spec.rb +19 -0
  70. data/spec/wavefront-cli/externallink_spec.rb +18 -0
  71. data/spec/wavefront-cli/maintanancewindow_spec.rb +19 -0
  72. data/spec/wavefront-cli/message_spec.rb +28 -0
  73. data/spec/wavefront-cli/metric_spec.rb +22 -0
  74. data/spec/wavefront-cli/proxy_spec.rb +26 -0
  75. data/spec/wavefront-cli/query_spec.rb +63 -0
  76. data/spec/wavefront-cli/resources/conf.yaml +10 -0
  77. data/spec/wavefront-cli/savedsearch_spec.rb +18 -0
  78. data/spec/wavefront-cli/source_spec.rb +18 -0
  79. data/spec/wavefront-cli/user_spec.rb +31 -0
  80. data/spec/wavefront-cli/webhook_spec.rb +17 -0
  81. data/wavefront-cli.gemspec +36 -0
  82. 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,13 @@
1
+ require_relative './base'
2
+
3
+ module WavefrontDisplay
4
+ #
5
+ # Format human-readable output for external links.
6
+ #
7
+ class ExternalLink < Base
8
+ def do_describe
9
+ readable_time(:createdEpochMillis, :updatedEpochMillis)
10
+ long_output
11
+ end
12
+ end
13
+ 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,8 @@
1
+ require_relative './base'
2
+
3
+ module WavefrontDisplay
4
+ #
5
+ # Format human-readable output for messages.
6
+ #
7
+ class Message < Base; end
8
+ 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,13 @@
1
+ require_relative './base'
2
+
3
+ module WavefrontDisplay
4
+ #
5
+ # Format human-readable output for proxies.
6
+ #
7
+ class Proxy < Base
8
+ def do_describe
9
+ readable_time(:lastCheckInTime)
10
+ long_output
11
+ end
12
+ end
13
+ 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