collins-cli 0.1.1 → 0.2.0

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.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: collins-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabe Conradi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-26 00:00:00.000000000 Z
11
+ date: 2014-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -52,25 +52,60 @@ dependencies:
52
52
  - - ~>
53
53
  - !ruby/object:Gem::Version
54
54
  version: 0.2.11
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 10.4.0
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 10.4.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 3.1.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 3.1.0
55
83
  description: CLI utilities to interact with the Collins API
56
84
  email:
57
85
  - gabe@tumblr.com
58
86
  - gummybearx@gmail.com
59
87
  executables:
60
88
  - collins
61
- - collins-action
62
- - collins-find
63
- - collins-log
64
- - collins-modify
65
89
  extensions: []
66
90
  extra_rdoc_files: []
67
91
  files:
92
+ - lib/collins/cli/find.rb
93
+ - lib/collins/cli/formatter.rb
94
+ - lib/collins/cli/ipam.rb
95
+ - lib/collins/cli/log.rb
96
+ - lib/collins/cli/mixins.rb
97
+ - lib/collins/cli/modify.rb
98
+ - lib/collins/cli/power.rb
99
+ - lib/collins/cli/provision.rb
100
+ - lib/collins-cli.rb
68
101
  - bin/collins
69
- - bin/collins-action
70
- - bin/collins-find
71
- - bin/collins-log
72
- - bin/collins-modify
73
102
  - README.md
103
+ - spec/collins__cli__find_spec.rb
104
+ - spec/collins__cli__log_spec.rb
105
+ - spec/collins__cli__modify_spec.rb
106
+ - spec/collins__cli__power_spec.rb
107
+ - spec/collins__cli__provision_spec.rb
108
+ - spec/spec_helper.rb
74
109
  homepage: http://github.com/byxorna/collins-cli
75
110
  licenses:
76
111
  - Apache License 2.0
@@ -95,4 +130,10 @@ rubygems_version: 2.0.14
95
130
  signing_key:
96
131
  specification_version: 4
97
132
  summary: CLI utilities to interact with the Collins API
98
- test_files: []
133
+ test_files:
134
+ - spec/collins__cli__find_spec.rb
135
+ - spec/collins__cli__log_spec.rb
136
+ - spec/collins__cli__modify_spec.rb
137
+ - spec/collins__cli__power_spec.rb
138
+ - spec/collins__cli__provision_spec.rb
139
+ - spec/spec_helper.rb
data/bin/collins-action DELETED
@@ -1,130 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # collins-action
3
- # provision and manage assets in collins easily from the CLI
4
-
5
- require 'collins_auth'
6
- require 'yaml'
7
- require 'optparse'
8
- require 'etc'
9
- require 'colorize'
10
-
11
- SUCCESS = "SUCCESS".colorize(:color => :green)
12
- ERROR = "ERROR".colorize(:color => :red)
13
- ALLOWABLE_POWER_ACTIONS = ['reboot','rebootsoft','reboothard','on','off','poweron','poweroff','identify']
14
- options = {
15
- :timeout => 120,
16
- :build_contact => Etc.getlogin,
17
- :provision => { }
18
- }
19
-
20
- basename = File.basename(File.realpath($0))
21
- parser = OptionParser.new do |opts|
22
- opts.banner = "Usage: #{basename} [options]"
23
- #TODO -s to show provisoining_profiles
24
- #TODO update IPMI stuff with ipmi_update
25
- #TODO create IPMI with ipmi_create
26
- #TODO implement IP allocation
27
- opts.separator "Actions:"
28
- opts.on('-P','--provision',"Provision assets (see Provisioning flags).") {|v| options[:mode] = :provision }
29
- opts.on('-S','--power-status',"Show IPMI power status.") {|v| options[:mode] = :power_status }
30
- opts.on('-A','--power-action ACTION',String,"Perform IPMI power ACTION on assets"){|v| options[:mode] = :power ; options[:power_action] = v}
31
-
32
- opts.separator ""
33
- opts.separator "Provisioning Flags:"
34
- opts.on('-n','--nodeclass NODECLASS',String,"Nodeclass to provision as. (Required)") {|v| options[:provision][:nodeclass] = v }
35
- opts.on('-p','--pool POOL',String,"Provision with pool POOL.") {|v| options[:provision][:pool] = v }
36
- opts.on('-r','--role ROLE',String,"Provision with primary role ROLE.") {|v| options[:provision][:primary_role] = v }
37
- opts.on('-R','--secondary-role ROLE',String,"Provision with secondary role ROLE.") {|v| options[:provision][:secondary_role] = v }
38
- opts.on('-s','--suffix SUFFIX',String,"Provision with suffix SUFFIX.") {|v| options[:provision][:suffix] = v }
39
- opts.on('-a','--activate',"Activate server on provision (useful with SL plugin) (Default: ignored)") {|v| options[:provision][:activate] = true }
40
- opts.on('-b','--build-contact USER',String,"Build contact. (Default: #{options[:build_contact]})") {|v| options[:build_contact] = v }
41
-
42
- opts.separator ""
43
- opts.separator "General:"
44
- opts.on('-t','--tags TAG[,...]',Array,"Tags to work on, comma separated") {|v| options[:tags] = v.map(&:to_sym)}
45
- opts.on('-C','--config CONFIG',String,'Use specific Collins config yaml for Collins::Client') {|v| options[:config] = v}
46
- opts.on('-h','--help',"Help") {puts opts ; exit 0}
47
-
48
- opts.separator ""
49
- opts.separator "Examples:"
50
- opts.separator <<_EXAMPLES_
51
- Provision some machines:
52
- cf -Sunallocated -arack_position:716|#{basename} -P -napiwebnode6 -RALL
53
- Show power status:
54
- cf ^dev6-gabe|#{basename} -S
55
- Power cycle a bunch of machines:
56
- #{basename} -t 001234,004567,007890 -A reboot
57
- _EXAMPLES_
58
- end.parse!
59
-
60
- if ARGV.size > 0
61
- # anything else left in ARGV is garbage
62
- puts "Not sure what I am supposed to do with these arguments: #{ARGV.join(' ')}"
63
- puts parser
64
- exit 1
65
- end
66
-
67
-
68
- abort "See --help for #{basename} usage" unless [:provision, :power_status, :power].include? options[:mode]
69
- abort "You need to specify at least a nodeclass when provisioning" if options[:mode] == :provision && options[:provision][:nodeclass].nil?
70
- if options[:mode] == :power
71
- # convert what we allow to be specified to what collins::power allows
72
- options[:power_action] = 'rebootsoft' if options[:power_action] == 'reboot'
73
- abort "Unknown power action #{options[:power_action]}, expecting one of #{ALLOWABLE_POWER_ACTIONS.join(',')}" unless ALLOWABLE_POWER_ACTIONS.include? options[:power_action]
74
- begin
75
- options[:power_action] = Collins::Power.normalize_action options[:power_action]
76
- rescue => e
77
- abort "Unknown power action #{options[:power_action]}! #{e.message}"
78
- end
79
- end
80
-
81
- if options[:tags].nil? or options[:tags].empty?
82
- # read tags from stdin. first field on the line is the tag
83
- input = ARGF.readlines
84
- options[:tags] = input.map{|l| l.split(/\s+/)[0] rescue nil}.compact.uniq
85
- end
86
-
87
- begin
88
- @collins = Collins::Authenticator.setup_client timeout: options[:timeout], config_file: options[:config], prompt: true
89
- rescue => e
90
- abort "Unable to set up Collins client! #{e.message}"
91
- end
92
-
93
- def api_call desc, method, *varargs
94
- success,message = begin
95
- [@collins.send(method,*varargs),nil]
96
- rescue => e
97
- [false,e.message]
98
- end
99
- puts "#{success ? SUCCESS : ERROR}: #{desc}#{message.nil? ? nil : " (%s)" % e.message}"
100
- success
101
- end
102
-
103
- action_successes = []
104
- options[:tags].each do |t|
105
- case options[:mode]
106
- when :provision
107
- action_string = "#{t} provisioning with #{options[:provision].map{|k,v| "#{k}:#{v}"}.join(" ")} by #{options[:build_contact]}... "
108
- printf action_string
109
- begin
110
- res = @collins.provision(t, options[:provision][:nodeclass], options[:build_contact], options[:provision])
111
- puts (res ? SUCCESS : ERROR )
112
- action_successes << res
113
- rescue => e
114
- puts "#{ERROR} (#{e.message})"
115
- action_successes << false
116
- end
117
- when :power_status
118
- begin
119
- s = @collins.power_status(t)
120
- puts "#{SUCCESS}: #{t} power status is #{s}"
121
- rescue => e
122
- puts "#{ERROR}: Unable to query power status for #{t}#{e.message.nil? ? nil : " (%s)" % e.message}"
123
- end
124
- when :power
125
- action_successes << api_call("#{t} performing #{options[:power_action]}", :power!, t, options[:power_action])
126
- end
127
- end
128
-
129
- exit action_successes.all? ? 0 : 1
130
-
data/bin/collins-find DELETED
@@ -1,225 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # stands for collins-find
3
- # look up hosts quickly from collins from the CLI
4
-
5
- require 'collins_auth'
6
- require 'yaml'
7
- require 'json'
8
- require 'optparse'
9
-
10
- #TODO: querying for :status or :state with -a status:maintenance doesnt play nice with -Smaintenance,allocated
11
- #TODO: we construct the query for states and statuses only from the parameters to --status (ignoring any -a attributes)
12
- #TODO: add a formatting option to display the assets in a different way (not a table)
13
-
14
- # this gets stuffed into the collins find call
15
- query_opts = {
16
- :operation => 'AND',
17
- :size => 9999,
18
- }
19
- # these are all the attributes we search for
20
- # may be converted into a CQL query
21
- search_attrs = { }
22
- options = {
23
- :display => :table, # how to display the results
24
- :separator => "\t",
25
- :attributes => {}, # additional attributes to query for
26
- :columns => [:tag, :hostname, :nodeclass, :status, :pool, :primary_role, :secondary_role],
27
- :column_override => [], # if set, these are the columns to display
28
- :timeout => 120,
29
- :show_header => false, # if the header for columns should be displayed
30
- :config => nil # collins config to give to setup_client
31
- }
32
-
33
-
34
- basename = File.basename(File.realpath($0))
35
- abort "See --help for #{basename} usage" if ARGV.empty?
36
- OptionParser.new do |opts|
37
- opts.banner = "Usage: #{basename} [options] [hostnamepattern]"
38
- opts.separator "Query options:"
39
- opts.on('-t','--tag TAG[,...]',Array, "Assets with tag[s] TAG") {|v| search_attrs[:tag] = v}
40
- opts.on('-T','--type TYPE',String, "Only show assets with type TYPE") {|v| search_attrs[:type] = v}
41
- opts.on('-n','--nodeclass NODECLASS[,...]',Array, "Assets in nodeclass NODECLASS") {|v| search_attrs[:nodeclass] = v}
42
- opts.on('-p','--pool POOL[,...]',Array, "Assets in pool POOL") {|v| search_attrs[:pool] = v}
43
- opts.on('-s','--size SIZE',Integer, "Number of assets to return (Default: #{query_opts[:size]})") {|v| query_opts[:size] = v}
44
- opts.on('-r','--role ROLE[,...]',Array,"Assets in primary role ROLE") {|v| search_attrs[:primary_role] = v}
45
- opts.on('-R','--secondary-role ROLE[,...]',Array,"Assets in secondary role ROLE") {|v| search_attrs[:secondary_role] = v}
46
- opts.on('-i','--ip-address IP[,...]',Array,"Assets with IP address[es]") {|v| search_attrs[:ip_address] = v}
47
- opts.on('-S','--status STATUS[:STATE][,...]',Array,"Asset status (and optional state after :)") do |v|
48
- # in order to know what state was paired with what status, lets store the original params
49
- # so the query constructor can create the correct CQL query
50
- options[:status_state] = v
51
- search_attrs[:status], search_attrs[:state] = v.inject([[],[]]) do |memo,s|
52
- status,state = s.split(':')
53
- memo[0] << status.upcase if not status.nil? and not status.empty?
54
- memo[1] << state.upcase if not state.nil? and not state.empty?
55
- memo
56
- end
57
- end
58
- opts.on('-a','--attribute attribute[:value[,...]]',String,"Arbitrary attributes and values to match in query. : between key and value") do |x|
59
- x.split(',').each do |p|
60
- a,v = p.split(':')
61
- a = a.to_sym
62
- if not search_attrs[a].nil? and not search_attrs[a].is_a? Array
63
- # its a single value, turn it into an array
64
- search_attrs[a] = [search_attrs[a]]
65
- end
66
- if search_attrs[a].is_a? Array
67
- # already multivalue, append
68
- search_attrs[a] << v
69
- else
70
- search_attrs[a] = v
71
- end
72
- end
73
- end
74
-
75
- opts.separator ""
76
- opts.separator "Table formatting:"
77
- opts.on('-H','--show-header',"Show header fields in output") {options[:show_header] = true}
78
- opts.on('-c','--columns ATTRIBUTES',Array,"Attributes to output as columns, comma separated (Default: #{options[:columns].map(&:to_s).join(',')})") {|v| options[:column_override] = v.map(&:to_sym)}
79
- opts.on('-x','--extra-columns ATTRIBUTES',Array,"Show these columns in addition to the default columns, comma separated") {|v| options[:columns].push(v.map(&:to_sym)).flatten! }
80
- opts.on('-f','--field-separator SEPARATOR',String,"Separator between columns in output (Default: #{options[:separator]})") {|v| options[:separator] = v}
81
-
82
- opts.separator ""
83
- opts.separator "Robot formatting:"
84
- opts.on('-l','--link',"Output link to assets found in web UI") {options[:display] = :link}
85
- opts.on('-j','--json',"Output results in JSON (NOTE: This probably wont be what you expected)") {options[:display] = :json}
86
- opts.on('-y','--yaml',"Output results in YAML") {options[:display] = :yaml}
87
-
88
- opts.separator ""
89
- opts.separator "Extra options:"
90
- opts.on('--expire SECONDS',Integer,"Timeout in seconds (0 == forever)") {|v| options[:timeout] = v}
91
- opts.on('-C','--config CONFIG',String,'Use specific Collins config yaml for Collins::Client') {|v| options[:config] = v}
92
- opts.on('-h','--help',"Help") {puts opts ; exit 0}
93
-
94
- opts.separator ""
95
- opts.separator <<_EXAMPLES_
96
- Examples:
97
- Query for devnodes in DEVEL pool that are VMs
98
- cf -n develnode -p DEVEL
99
- Query for asset 001234, and show its system_password
100
- cf -t 001234 -x system_password
101
- Query for all decommissioned VM assets
102
- cf -a is_vm:true -S decommissioned
103
- Query for hosts matching hostname '^web6-'
104
- cf ^web6-
105
- Query for all develnode6 nodes with a value for PUPPET_SERVER
106
- cf -n develnode6 -a puppet_server -H
107
- _EXAMPLES_
108
- end.parse!
109
-
110
- # hostname is the final option, no flags
111
- search_attrs[:hostname] = ARGV.shift
112
- # fix bug where assets wont get found if they dont have that meta attribute
113
- search_attrs.delete(:hostname) if search_attrs[:hostname].nil?
114
-
115
- # if nothing passed to us, lets not search for EVERYTHING
116
- #abort "You need to query for _something_, see --help" if
117
- # selector_keys.all? {|key| options[key].nil?} and options[:attributes].empty?
118
-
119
- # for any search attributes, lets not pass arrays of 1 element
120
- # as that will confuse as_query?
121
- search_attrs.each do |k,v|
122
- if v.is_a? Array
123
- search_attrs[k] = v.first if v.length == 1
124
- search_attrs[k] = nil if v.empty?
125
- end
126
- end
127
-
128
- def as_query?(attrs)
129
- attrs.any?{|k,v| v.is_a? Array}
130
- end
131
-
132
- def convert_to_query(op, attrs, options)
133
- # we want to support being able to query -Smaintenance:noop,:running,:provisioning_problem
134
- # and not have the states ored together. Handle status/state pairs separately
135
- basic_query = attrs.reject {|k,v| [:status,:state].include?(k)}.map do |k,v|
136
- next if v.nil?
137
- if v.is_a? Array
138
- "(" + v.map{|x| "#{k} = #{x}"}.join(' OR ') + ")"
139
- else
140
- "#{k} = #{v}"
141
- end
142
- end.compact.join(" #{op} ")
143
- # because they are provided in pairs, lets handle them together
144
- # create the (( STATUS = maintenance AND STATE = noop) OR (STATE = provisioning_problem)) query
145
- if options[:status_state]
146
- status_query = options[:status_state].flat_map do |ss|
147
- h = {}
148
- h[:status], h[:state] = ss.split(':')
149
- h[:status] = nil if h[:status].nil? or h[:status].empty?
150
- h[:state] = nil if h[:state].nil? or h[:state].empty?
151
- "( " + h.map {|k,v| v.nil? ? nil : "#{k.to_s.upcase} = #{v}"}.compact.join(" AND ") + " )"
152
- end.compact.join(' OR ')
153
- status_query = "( #{status_query} )"
154
- end
155
- [basic_query,status_query].reject {|q| q.nil? or q.empty?}.join(" #{op} ")
156
- end
157
-
158
- def display_as_robot_talk(assets, format = :json)
159
- puts assets.send("to_#{format}".to_sym)
160
- end
161
- def display_as_table(assets, columns, separator, show_header = false)
162
- # lets figure out how wide each column is, including header
163
- column_width_pairs = columns.map do |column|
164
- # grab all attributes == column and figure out max width
165
- width = assets.map{|a| (column == :state) ? a.send(column).label.to_s.length : a.send(column).to_s.length}.max
166
- width = [width, column.to_s.length].max if show_header
167
- [column,width]
168
- end
169
- column_width_map = Hash[column_width_pairs]
170
-
171
- if show_header
172
- $stderr.puts column_width_map.map{|c,w| "%-#{w}s" % c}.join(separator)
173
- end
174
- assets.each do |a|
175
- puts column_width_map.map {|c,w| v = (c == :state) ? a.send(c).label : a.send(c) ; "%-#{w}s" % v }.join(separator)
176
- end
177
- end
178
- def display_as_link assets, client
179
- assets.each do |a|
180
- puts "#{client.host}/asset/#{a.tag}"
181
- end
182
- end
183
-
184
- # merge search_attrs into query
185
- if as_query?(search_attrs)
186
- query_opts[:query] = convert_to_query(query_opts[:operation], search_attrs, options)
187
- #puts "Query: #{query_opts[:query]}"
188
- else
189
- query_opts.merge!(search_attrs)
190
- end
191
-
192
- begin
193
- collins = Collins::Authenticator.setup_client timeout: options[:timeout], config_file: options[:config], prompt: true
194
- rescue => e
195
- abort "Unable to set up Collins client! #{e.message}"
196
- end
197
-
198
- begin
199
- assets = collins.find(query_opts)
200
- if assets.length > 0
201
- case options[:display]
202
- when :table
203
- # if the user passed :column_override, respect that absolutely. otherwise, the columns to display
204
- # should be options[:columns] + any extra attributes queried for. this way ```cf -c hostname -a is_vm:true```
205
- # wont return 2 columns; only the one you asked for
206
- columns = if options[:column_override].empty?
207
- options[:columns].concat(search_attrs.keys).compact.uniq
208
- else
209
- options[:column_override]
210
- end
211
- display_as_table(assets,columns,options[:separator],options[:show_header])
212
- when :link
213
- display_as_link assets, collins
214
- when :json,:yaml
215
- display_as_robot_talk(assets,options[:display])
216
- else
217
- abort "I don't know how to display assets in #{options[:display]} format!"
218
- end
219
- else
220
- abort "No assets found"
221
- end
222
- rescue => e
223
- abort "Error querying collins: #{e.message}"
224
- end
225
-
data/bin/collins-log DELETED
@@ -1,143 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # stands for collins-log
3
- # looks at logs on assets
4
-
5
- # TODO: make poll_wait tunable
6
- # TODO: add options to sort ascending or descending on date
7
- # TODO: implement searching logs (is this really useful?)
8
- # TODO: add duplicate line detection and compression (...)
9
-
10
- require 'collins_auth'
11
- require 'yaml'
12
- require 'optparse'
13
- require 'colorize'
14
- require 'set'
15
-
16
- log_levels = Collins::Api::Logging::Severity.constants.map(&:to_s)
17
-
18
- @options = {
19
- :tags => [],
20
- :show_all => false,
21
- :poll_wait => 2,
22
- :follow => false,
23
- :severities => [],
24
- :timeout => 20,
25
- :sev_colors => {
26
- 'EMERGENCY' => {:color => :red, :background => :light_blue},
27
- 'ALERT' => {:color => :red},
28
- 'CRITICAL' => {:color => :black, :background => :red},
29
- 'ERROR' => {:color => :red},
30
- 'WARNING' => {:color => :yellow},
31
- 'NOTICE' => {},
32
- 'INFORMATIONAL' => {:color => :green},
33
- 'DEBUG' => {:color => :blue},
34
- 'NOTE' => {:color => :light_cyan},
35
- },
36
- :config => nil
37
- }
38
- @search_opts = {
39
- :size => 20,
40
- :filter => nil,
41
- }
42
-
43
- basename = File.basename(File.realpath($0))
44
- abort "See --help for #{basename} usage" if ARGV.empty?
45
- OptionParser.new do |opts|
46
- opts.banner = "Usage: #{basename} [options]"
47
- opts.on('-a','--all',"Show logs from ALL assets") {|v| @options[:show_all] = true}
48
- opts.on('-n','--number LINES',Integer,"Show the last LINES log entries. (Default: #{@search_opts[:size]})") {|v| @search_opts[:size] = v}
49
- opts.on('-t','--tags TAGS',Array,"Tags to work on, comma separated") {|v| @options[:tags] = v}
50
- opts.on('-f','--follow',"Poll for logs every #{@options[:poll_wait]} seconds") {|v| @options[:follow] = true}
51
- opts.on('-s','--severity SEVERITY[,...]',Array,"Log severities to return (Defaults to all). Use !SEVERITY to exclude one.") {|v| @options[:severities] = v.map(&:upcase) }
52
- #opts.on('-i','--interleave',"Interleave all log entries (Default: groups by asset)") {|v| options[:interleave] = true}
53
- opts.on('-C','--config CONFIG',String,'Use specific Collins config yaml for Collins::Client') {|v| @options[:config] = v}
54
- opts.on('-h','--help',"Help") {puts opts ; exit 0}
55
- opts.separator ""
56
- opts.separator <<_EOE_
57
- Severities:
58
- #{Collins::Api::Logging::Severity.to_a.map{|s| s.colorize(@options[:sev_colors][s])}.join(", ")}
59
-
60
- Examples:
61
- Show last 20 logs for an asset
62
- #{basename} -t 001234
63
- Show last 100 logs for an asset
64
- #{basename} -t 001234 -n100
65
- Show last 10 logs for 2 assets that are ERROR severity
66
- #{basename} -t 001234,001235 -n10 -sERROR
67
- Show last 10 logs all assets that are not note or informational severity
68
- #{basename} -a -n10 -s'!informational,!note'
69
- Show last 10 logs for all web nodes that are provisioned having verification in the message
70
- cf -S provisioned -n webnode\$ | #{basename} -n10 -s debug | grep -i verification
71
- _EOE_
72
- end.parse!
73
-
74
-
75
- abort "Log severities #{@options[:severities].join(',')} are invalid! Use one of #{log_levels.join(', ')}" unless @options[:severities].all? {|l| Collins::Api::Logging::Severity.valid?(l.tr('!','')) }
76
- @search_opts[:filter] = @options[:severities].join(';')
77
-
78
- if @options[:tags].empty? and not @options[:show_all]
79
- # read tags from stdin. first field on the line is the tag
80
- input = ARGF.readlines
81
- @options[:tags] = input.map{|l| l.split(/\s+/)[0] rescue nil}.compact.uniq
82
- end
83
-
84
- abort "You need to give me some assets to display logs; see --help" if @options[:tags].empty? and not @options[:show_all]
85
-
86
- begin
87
- @collins = Collins::Authenticator.setup_client timeout: @options[:timeout], config_file: @options[:config], prompt: true
88
- rescue => e
89
- abort "Unable to set up Collins client! #{e.message}"
90
- end
91
-
92
- def output_logs(logs)
93
- # colorize output before computing width of fields
94
- logs.map! do |l|
95
- l.TYPE = @options[:sev_colors].has_key?(l.TYPE) ? l.TYPE.colorize(@options[:sev_colors][l.TYPE]) : l.TYPE
96
- l
97
- end
98
- # show newest last
99
- sorted_logs = logs.sort_by {|l| l.CREATED }
100
- tag_width = sorted_logs.map{|l| l.ASSET_TAG.length}.max
101
- sev_width = sorted_logs.map{|l| l.TYPE.length}.max
102
- time_width = sorted_logs.map{|l| l.CREATED.length}.max
103
- sorted_logs.each do |l|
104
- puts "%-#{time_width}s: %-#{sev_width}s %-#{tag_width}s %s" % [l.CREATED, l.TYPE, l.ASSET_TAG, l.MESSAGE]
105
- end
106
- end
107
-
108
- def grab_logs
109
- if @options[:tags].empty?
110
- begin
111
- @collins.all_logs(@search_opts)
112
- rescue => e
113
- $stderr.puts "Unable to fetch logs:".colorize(@options[:sev_colors]['WARNING']) + " #{e.message}"
114
- []
115
- end
116
- else
117
- @options[:tags].flat_map do |t|
118
- begin
119
- @collins.logs(t, @search_opts)
120
- rescue => e
121
- $stderr.puts "Unable to fetch logs for #{t}:".colorize(@options[:sev_colors]['WARNING']) + " #{e.message}"
122
- []
123
- end
124
- end
125
- end
126
- end
127
-
128
- begin
129
- all_logs = grab_logs
130
- logs_seen = all_logs.map(&:ID).to_set
131
- output_logs(all_logs)
132
- while @options[:follow]
133
- sleep @options[:poll_wait]
134
- logs = grab_logs
135
- new_logs = logs.reject {|l| logs_seen.include?(l.ID)}
136
- output_logs(new_logs)
137
- logs_seen = logs_seen | new_logs.map(&:ID)
138
- end
139
- rescue Interrupt => e
140
- exit 0
141
- end
142
-
143
-