collins-cli 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
-