wavefront-cli 0.0.2

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.
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