collins_shell 0.2.14
Sign up to get free protection for your applications and to get access to all the features.
- data/.pryrc +1 -0
- data/.rvmrc +1 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +59 -0
- data/README.md +335 -0
- data/Rakefile +64 -0
- data/VERSION +1 -0
- data/bin/collins-shell +36 -0
- data/collins_shell.gemspec +95 -0
- data/lib/collins_shell.rb +3 -0
- data/lib/collins_shell/asset.rb +198 -0
- data/lib/collins_shell/cli.rb +185 -0
- data/lib/collins_shell/console.rb +129 -0
- data/lib/collins_shell/console/asset.rb +127 -0
- data/lib/collins_shell/console/cache.rb +17 -0
- data/lib/collins_shell/console/command_helpers.rb +131 -0
- data/lib/collins_shell/console/commands.rb +28 -0
- data/lib/collins_shell/console/commands/cat.rb +123 -0
- data/lib/collins_shell/console/commands/cd.rb +61 -0
- data/lib/collins_shell/console/commands/io.rb +26 -0
- data/lib/collins_shell/console/commands/iterators.rb +190 -0
- data/lib/collins_shell/console/commands/tail.rb +178 -0
- data/lib/collins_shell/console/commands/versions.rb +42 -0
- data/lib/collins_shell/console/filesystem.rb +121 -0
- data/lib/collins_shell/console/options_helpers.rb +8 -0
- data/lib/collins_shell/errors.rb +7 -0
- data/lib/collins_shell/ip_address.rb +144 -0
- data/lib/collins_shell/ipmi.rb +67 -0
- data/lib/collins_shell/monkeypatch.rb +60 -0
- data/lib/collins_shell/provision.rb +152 -0
- data/lib/collins_shell/state.rb +98 -0
- data/lib/collins_shell/tag.rb +41 -0
- data/lib/collins_shell/thor.rb +209 -0
- data/lib/collins_shell/util.rb +120 -0
- data/lib/collins_shell/util/asset_printer.rb +265 -0
- data/lib/collins_shell/util/asset_stache.rb +32 -0
- data/lib/collins_shell/util/log_printer.rb +187 -0
- data/lib/collins_shell/util/printer_util.rb +28 -0
- metadata +200 -0
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'collins_shell/monkeypatch'
|
2
|
+
require 'collins_shell/util/asset_stache'
|
3
|
+
|
4
|
+
module CollinsShell
|
5
|
+
module Util
|
6
|
+
|
7
|
+
SIZABLE_ATTRIBUTES = [:memory_size_total, :disk_storage_total]
|
8
|
+
|
9
|
+
def call_collins client, operation, &block
|
10
|
+
begin
|
11
|
+
block.call(client)
|
12
|
+
rescue SystemExit => e
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_selector selector, tags, size, remote = false
|
17
|
+
asset_params = ::Collins::Asset::Find.to_a.inject({}) {|res,el| res.update(el.to_s.upcase => el)}
|
18
|
+
selector = symbolize_hash(selector).inject({}) do |result, (k,v)|
|
19
|
+
upcase_key = k.to_s.upcase
|
20
|
+
if asset_params.key?(upcase_key) then # normalized reserved params
|
21
|
+
corrected_key = asset_params[upcase_key]
|
22
|
+
if ::Collins::Asset::Find::DATE_PARAMS.include?(corrected_key) then
|
23
|
+
result[corrected_key.to_sym] = ::Collins::Asset.format_date_string(v)
|
24
|
+
else
|
25
|
+
result[corrected_key.to_sym] = v
|
26
|
+
end
|
27
|
+
elsif upcase_key == k.to_s then # respect case
|
28
|
+
result[k] = v
|
29
|
+
else # otherwise downcase
|
30
|
+
result[k.downcase] = v
|
31
|
+
end
|
32
|
+
result
|
33
|
+
end
|
34
|
+
if not selector.include?(:operation) then
|
35
|
+
selector.update(:operation => 'and')
|
36
|
+
end
|
37
|
+
if not selector.include?(:remoteLookup) and remote then
|
38
|
+
selector.update(:remoteLookup => remote.to_s)
|
39
|
+
end
|
40
|
+
if not selector.include?(:size) and size then
|
41
|
+
selector.update(:size => size)
|
42
|
+
end
|
43
|
+
selector.update(:details => true) if is_array?(tags)
|
44
|
+
CollinsShell::Util::SIZABLE_ATTRIBUTES.each do |attrib|
|
45
|
+
attrib_value = selector[attrib]
|
46
|
+
if attrib_value && attrib_value.to_s.is_disk_size? then
|
47
|
+
selector.update(attrib => attrib_value.to_s.to_bytes)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
selector
|
51
|
+
end
|
52
|
+
|
53
|
+
def asset_exec asset, execs, confirm = true
|
54
|
+
return unless execs
|
55
|
+
mustache = CollinsShell::Util::AssetStache.new asset
|
56
|
+
rendered = mustache.render "/bin/bash -c '#{execs}'"
|
57
|
+
say_status("exec", rendered, :red)
|
58
|
+
require_yes("Running on #{asset.tag}. ARE YOU SURE?", :red) if confirm
|
59
|
+
system(rendered)
|
60
|
+
end
|
61
|
+
|
62
|
+
def asset_get tag, options
|
63
|
+
call_collins get_collins_client, "get asset" do |client|
|
64
|
+
as_asset = Collins::Asset.new(tag)
|
65
|
+
as_asset.location = options.remote
|
66
|
+
asset = client.get as_asset
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def print_find_results assets, tags, options = {}
|
71
|
+
tags = [:tag,:status,:type,:created,:updated,:location] if not is_array?(tags)
|
72
|
+
if options[:url] then
|
73
|
+
tags << :url
|
74
|
+
end
|
75
|
+
if options[:header] then
|
76
|
+
puts(tags.join(','))
|
77
|
+
end
|
78
|
+
[assets].flatten.each do |asset|
|
79
|
+
puts format_asset_tags(asset, tags)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def format_asset_tags asset, tags
|
84
|
+
tags.map do |tag|
|
85
|
+
if tag.to_s =~ /^ipmi_(.*)/ then
|
86
|
+
asset.ipmi.send($1.to_sym)
|
87
|
+
elsif tag.to_s.split('.').length == 2 then
|
88
|
+
o, m = tag.to_s.split('.')
|
89
|
+
result = asset.send(o.to_sym)
|
90
|
+
if result.nil? then
|
91
|
+
"nil"
|
92
|
+
else
|
93
|
+
format_asset_value result.send(m.to_sym)
|
94
|
+
end
|
95
|
+
elsif tag == :url then
|
96
|
+
config = get_collins_config
|
97
|
+
" #{config[:host]}/asset/#{asset.tag}"
|
98
|
+
else
|
99
|
+
result = asset.send(tag.to_sym)
|
100
|
+
format_asset_value result
|
101
|
+
end
|
102
|
+
end.join(',')
|
103
|
+
end
|
104
|
+
|
105
|
+
def format_asset_value value
|
106
|
+
if is_array? value then
|
107
|
+
value.map {|v| format_asset_value v}
|
108
|
+
elsif value.is_a?(Collins::Address) then
|
109
|
+
[:address,:gateway,:netmask].map {|s| value.send(s)}.join('|')
|
110
|
+
else
|
111
|
+
value.to_s
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def is_array? tags
|
116
|
+
tags && tags.is_a?(Array) && tags.length > 0
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,265 @@
|
|
1
|
+
require 'terminal-table'
|
2
|
+
require 'collins_shell/util/printer_util'
|
3
|
+
|
4
|
+
module CollinsShell
|
5
|
+
|
6
|
+
class AssetPrinter
|
7
|
+
|
8
|
+
include CollinsShell::PrinterUtil
|
9
|
+
|
10
|
+
attr_accessor :asset, :detailed, :table, :col_size, :thor, :separator, :logs, :color
|
11
|
+
|
12
|
+
def initialize asset, thor, options = {}
|
13
|
+
@asset = asset
|
14
|
+
@table = Terminal::Table.new
|
15
|
+
@thor = thor
|
16
|
+
@separator = options[:separator]
|
17
|
+
@detailed = options.fetch(:detailed, true)
|
18
|
+
@col_size = 6
|
19
|
+
@color = options.fetch(:color, true)
|
20
|
+
@logs = options[:logs]
|
21
|
+
end
|
22
|
+
|
23
|
+
def render
|
24
|
+
table.title = header_text("Asset #{asset.tag}")
|
25
|
+
collect_asset_basics
|
26
|
+
collect_disk_data if detailed
|
27
|
+
collect_ip_data if detailed
|
28
|
+
collect_ipmi_data if detailed
|
29
|
+
collect_nic_data if detailed
|
30
|
+
collect_mem_data if detailed
|
31
|
+
collect_power_data if detailed
|
32
|
+
collect_xtra_data
|
33
|
+
collect_log_data
|
34
|
+
table_s = table.render
|
35
|
+
if separator then
|
36
|
+
width = (0...@col_size).inject(0) do |result, idx|
|
37
|
+
result + table.column_width(idx)
|
38
|
+
end
|
39
|
+
sep_s = separator * (1.13 * width.to_f).to_i
|
40
|
+
table_s + "\n#{sep_s}\n"
|
41
|
+
else
|
42
|
+
table_s
|
43
|
+
end
|
44
|
+
end
|
45
|
+
alias :to_s :render
|
46
|
+
|
47
|
+
def collect_asset_basics
|
48
|
+
header = [:tag,:status,:type,:created,:updated,:location]
|
49
|
+
table << get_row(header, true, header.length)
|
50
|
+
table << :separator
|
51
|
+
table << get_row(header.map{|s| asset.send(s)}, false, header.length)
|
52
|
+
if asset.state && !asset.state.empty? then
|
53
|
+
state = asset.state
|
54
|
+
state_headers = [
|
55
|
+
title_text("State Name"),
|
56
|
+
title_text("State Label"),
|
57
|
+
{:value => title_text("State Description"), :colspan => 4}
|
58
|
+
]
|
59
|
+
table << :separator
|
60
|
+
table << state_headers
|
61
|
+
table << :separator
|
62
|
+
table << [state.name, state.label, {:value => state.description, :colspan => 4}]
|
63
|
+
end
|
64
|
+
@col_size = table.number_of_columns
|
65
|
+
end
|
66
|
+
|
67
|
+
def collect_disk_data
|
68
|
+
disk_header = ["SIZE_HUMAN", "SIZE", "TYPE", "DESCRIPTION"]
|
69
|
+
row_title "Disks"
|
70
|
+
table << get_row(disk_header, true)
|
71
|
+
table << :separator
|
72
|
+
asset.disks.each do |disk|
|
73
|
+
table << get_row(disk_header.map{|s| disk[s]})
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def collect_log_data
|
78
|
+
return if (not logs or logs.empty?)
|
79
|
+
row_title "Log Entries"
|
80
|
+
headers = [
|
81
|
+
title_text("Created"),
|
82
|
+
title_text("Severity"),
|
83
|
+
title_text("Source"),
|
84
|
+
title_text("Format"),
|
85
|
+
{:value => title_text("Message"), :colspan => 2}
|
86
|
+
]
|
87
|
+
table << headers
|
88
|
+
table << :separator
|
89
|
+
have_multi_row = false
|
90
|
+
logs_formatted = logs.map do |log|
|
91
|
+
rewritten_msg = log.MESSAGE.strip
|
92
|
+
message = wrap_text(rewritten_msg).strip
|
93
|
+
if message != rewritten_msg then
|
94
|
+
have_multi_row = true
|
95
|
+
end
|
96
|
+
[
|
97
|
+
format_datetime(log.CREATED), log.TYPE, log.SOURCE, log.FORMAT,
|
98
|
+
{:value => message, :colspan => 2}
|
99
|
+
]
|
100
|
+
end
|
101
|
+
last_log_idx = logs.length - 1
|
102
|
+
logs_formatted.each_with_index do |row, index|
|
103
|
+
table << row
|
104
|
+
# Don't print a separator if we don't have a multi-row message
|
105
|
+
if index != last_log_idx && have_multi_row then
|
106
|
+
table << :separator
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def collect_ip_data
|
112
|
+
address_header = [:address,:gateway,:netmask,:pool,:is_private?,:is_public?]
|
113
|
+
row_title "IP Addresses"
|
114
|
+
table << get_row(address_header, true)
|
115
|
+
table << :separator
|
116
|
+
asset.addresses.each{|a| table << get_row(address_header.map{|s| a.send(s)})}
|
117
|
+
end
|
118
|
+
|
119
|
+
def collect_ipmi_data
|
120
|
+
ipmi_header = [:address,:gateway,:netmask,:username,:password]
|
121
|
+
row_title "IPMI Information"
|
122
|
+
table << get_row(ipmi_header, true)
|
123
|
+
table << :separator
|
124
|
+
table << get_row(ipmi_header.map {|i| asset.ipmi.send(i)})
|
125
|
+
end
|
126
|
+
|
127
|
+
def collect_nic_data
|
128
|
+
nic_header = ["SPEED_HUMAN", "MAC_ADDRESS", "DESCRIPTION"]
|
129
|
+
row_title "Network Interfaces"
|
130
|
+
table << get_row(nic_header, true)
|
131
|
+
table << :separator
|
132
|
+
asset.nics.each {|nic| table << get_row(nic_header.map{|s| nic[s]})}
|
133
|
+
end
|
134
|
+
|
135
|
+
def collect_mem_data
|
136
|
+
memory_header = ["BANK","SIZE","SIZE_HUMAN","DESCRIPTION"]
|
137
|
+
row_title "Physical Memory"
|
138
|
+
table << get_row(memory_header, true)
|
139
|
+
table << :separator
|
140
|
+
memory_total = 0
|
141
|
+
asset.memory.each do |mem|
|
142
|
+
memory_total += mem["SIZE"].to_i
|
143
|
+
if mem["SIZE"].to_i != 0 then
|
144
|
+
table << get_row(memory_header.map{|s| mem[s]})
|
145
|
+
end
|
146
|
+
end
|
147
|
+
table << :separator
|
148
|
+
table << [title_text("TOTAL SIZE (Bytes)"), {:value => memory_total.to_s, :colspan => 5}]
|
149
|
+
table << [title_text("TOTAL SIZE (Human)"), {:value => memory_total.to_human_size, :colspan => 5}]
|
150
|
+
end
|
151
|
+
|
152
|
+
def collect_power_data
|
153
|
+
power_header = [:type,:key,:value,:label]
|
154
|
+
row_title "Power Information"
|
155
|
+
table << get_row(power_header, true)
|
156
|
+
table << :separator
|
157
|
+
asset.power.each do |power|
|
158
|
+
power.units.each do |unit|
|
159
|
+
table << get_row(power_header.map {|key| unit.send(key)})
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def format_text key, value
|
165
|
+
key_s = key.to_s.downcase
|
166
|
+
if key_s.start_with?("json_") or key_s.end_with?("_json") then
|
167
|
+
begin
|
168
|
+
JSON.parse(value, :symbolize_names => true).to_s
|
169
|
+
rescue Exception => e
|
170
|
+
value
|
171
|
+
end
|
172
|
+
else
|
173
|
+
value
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def collect_xtra_data
|
178
|
+
return unless (asset.extras and asset.extras["ATTRIBS"] && asset.extras["ATTRIBS"]["0"])
|
179
|
+
row_title "Extra Attributes"
|
180
|
+
attribs = []
|
181
|
+
attribs_unsorted = asset.extras["ATTRIBS"]["0"]
|
182
|
+
attribs_sorted = attribs_unsorted.keys.sort.inject({}) do |result,key|
|
183
|
+
result.update(key => attribs_unsorted[key])
|
184
|
+
end
|
185
|
+
attribs_sorted.each do |key,value|
|
186
|
+
attribs << title_text(key)
|
187
|
+
attribs << format_text(key, value)
|
188
|
+
if attribs.length == 6 then
|
189
|
+
table << attribs
|
190
|
+
attribs = []
|
191
|
+
end
|
192
|
+
if CollinsShell::Util::SIZABLE_ATTRIBUTES.include?(key.downcase.to_sym) then
|
193
|
+
attribs << title_text("#{key} (Human)")
|
194
|
+
attribs << value.to_f.to_human_size
|
195
|
+
end
|
196
|
+
if attribs.length == 6 then
|
197
|
+
table << attribs
|
198
|
+
attribs = []
|
199
|
+
end
|
200
|
+
end
|
201
|
+
if attribs.length != 0 then
|
202
|
+
until attribs.length == 6
|
203
|
+
attribs << " "
|
204
|
+
end
|
205
|
+
table << attribs
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def header_text txt
|
210
|
+
if @color then
|
211
|
+
thor.set_color(txt, :bold, :magenta)
|
212
|
+
else
|
213
|
+
txt
|
214
|
+
end
|
215
|
+
end
|
216
|
+
def title_text txt
|
217
|
+
if @color then
|
218
|
+
thor.set_color(txt, :bold, :white)
|
219
|
+
else
|
220
|
+
txt
|
221
|
+
end
|
222
|
+
end
|
223
|
+
def regular_text txt
|
224
|
+
txt
|
225
|
+
end
|
226
|
+
|
227
|
+
def get_row row_data, header = false, cols = 0
|
228
|
+
num_cols = @col_size
|
229
|
+
row_length = row_data.length
|
230
|
+
last_index = row_length - 1
|
231
|
+
average_size = (num_cols.to_f / row_length.to_f)
|
232
|
+
est_size = (num_cols / row_length)
|
233
|
+
est_is_actual = (average_size == est_size)
|
234
|
+
used_cols = 0
|
235
|
+
row = []
|
236
|
+
row_data.each_with_index do |col,index|
|
237
|
+
if est_is_actual then
|
238
|
+
size_per_col = est_size
|
239
|
+
elsif index == 0 then
|
240
|
+
size_per_col = 1
|
241
|
+
elsif average_size > (used_cols.to_f/index.to_f) then
|
242
|
+
size_per_col = 2
|
243
|
+
else
|
244
|
+
size_per_col = 1
|
245
|
+
end
|
246
|
+
if col.is_a?(DateTime) then
|
247
|
+
col = format_datetime(col)
|
248
|
+
end
|
249
|
+
used_cols += size_per_col
|
250
|
+
alignment = if size_per_col > 1 then :center else :left end
|
251
|
+
value = if header then title_text(col) else regular_text(col) end
|
252
|
+
row << {:value => value, :alignment => alignment, :colspan => size_per_col}
|
253
|
+
end
|
254
|
+
row
|
255
|
+
end
|
256
|
+
|
257
|
+
def row_title txt
|
258
|
+
table << :separator
|
259
|
+
table << [{:value => header_text(txt), :alignment => :center, :colspan => @col_size}]
|
260
|
+
table << :separator
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'mustache'
|
2
|
+
|
3
|
+
module CollinsShell; module Util
|
4
|
+
|
5
|
+
class AssetStache < Mustache
|
6
|
+
attr_accessor :asset
|
7
|
+
|
8
|
+
def initialize asset
|
9
|
+
@asset = asset
|
10
|
+
end
|
11
|
+
|
12
|
+
def respond_to? meth
|
13
|
+
result = asset.send(meth.to_sym)
|
14
|
+
if result.nil? then
|
15
|
+
super
|
16
|
+
else
|
17
|
+
result
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def method_missing meth, *args, &block
|
22
|
+
result = asset.send(meth.to_sym)
|
23
|
+
if result.nil? then
|
24
|
+
super
|
25
|
+
else
|
26
|
+
result
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end; end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
require 'terminal-table'
|
2
|
+
require 'collins_shell/util/printer_util'
|
3
|
+
|
4
|
+
module CollinsShell
|
5
|
+
|
6
|
+
class LogPrinter
|
7
|
+
|
8
|
+
include CollinsShell::PrinterUtil
|
9
|
+
|
10
|
+
attr_reader :asset_tag, :logs, :options, :table, :table_width
|
11
|
+
|
12
|
+
def initialize asset_or_tag, logs
|
13
|
+
@asset_tag = Collins::Util.get_asset_or_tag(asset_or_tag).tag
|
14
|
+
if logs.is_a?(Hash) then
|
15
|
+
@logs = logs.delete(:logs) || []
|
16
|
+
@options = logs
|
17
|
+
else
|
18
|
+
@logs = logs
|
19
|
+
@options = {}
|
20
|
+
end
|
21
|
+
@table_width = 0
|
22
|
+
@table = Terminal::Table.new(:title => "Logs for #{asset_tag}")
|
23
|
+
end
|
24
|
+
|
25
|
+
def render _logs = []
|
26
|
+
setup_table
|
27
|
+
setup_header
|
28
|
+
need_separator, formatted_logs = format_logs _logs
|
29
|
+
add_logs_to_table need_separator, formatted_logs
|
30
|
+
render!
|
31
|
+
end
|
32
|
+
alias :to_s :render
|
33
|
+
|
34
|
+
protected
|
35
|
+
# Add the specified logs to the table instance, using a separator if required
|
36
|
+
def add_logs_to_table need_separator, logs
|
37
|
+
last_log_idx = logs.length - 1
|
38
|
+
logs.each_with_index do |row, index|
|
39
|
+
table << row
|
40
|
+
# Don't print a separator if we don't have a multi-row message
|
41
|
+
if index != last_log_idx && need_separator then
|
42
|
+
table << :separator
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Format logs appropriately for table layout
|
48
|
+
def format_logs _logs
|
49
|
+
have_multi_row = streaming? # either false (default), or true based on streaming
|
50
|
+
logs_to_render = if not _logs.empty? then _logs else logs end
|
51
|
+
logs_formatted = logs_to_render.map do |log|
|
52
|
+
entry = format_log_entry log
|
53
|
+
have_multi_row = true unless entry.find_index{|e| e.to_s.include?("\n")}.nil?
|
54
|
+
entry
|
55
|
+
end
|
56
|
+
[have_multi_row, logs_formatted]
|
57
|
+
end
|
58
|
+
|
59
|
+
# We force column values to pad so terminal-table doesn't try and be smart
|
60
|
+
def format_column value, width, alignment
|
61
|
+
value_s = value.to_s.strip
|
62
|
+
if value_s.size > width then
|
63
|
+
alignment = :left # force to left is we wrap
|
64
|
+
value_s = wrap_text(value_s, width).strip
|
65
|
+
end
|
66
|
+
case alignment
|
67
|
+
when :left
|
68
|
+
value_s.ljust(width)
|
69
|
+
when :right
|
70
|
+
value_s.rjust(width)
|
71
|
+
else
|
72
|
+
value_s.center(width)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def format_log_entry log
|
77
|
+
res = []
|
78
|
+
if streaming? then
|
79
|
+
res = [
|
80
|
+
format_column(format_datetime(log.CREATED), created_col_width, :center),
|
81
|
+
format_column(log.TYPE, severity_col_width, :center),
|
82
|
+
format_column(log.SOURCE, source_col_width, :center),
|
83
|
+
format_column(log.FORMAT, format_col_width, :center),
|
84
|
+
format_column(log.MESSAGE, message_col_width, :left)
|
85
|
+
]
|
86
|
+
res.unshift(format_column(log.ASSET_TAG, tag_col_width, :left)) if requires_tag?
|
87
|
+
else
|
88
|
+
res = [
|
89
|
+
format_datetime(log.CREATED), log.TYPE, log.SOURCE, log.FORMAT, wrap_text(log.MESSAGE.strip).strip
|
90
|
+
]
|
91
|
+
res.unshift(log.ASSET_TAG) if requires_tag?
|
92
|
+
end
|
93
|
+
res
|
94
|
+
end
|
95
|
+
|
96
|
+
# Grab the rendered table and reset the table instance
|
97
|
+
def render!
|
98
|
+
if displayed? then
|
99
|
+
buffer = table.rows.map do |row|
|
100
|
+
row.render + "\n" + Terminal::Table::Separator.new(table).render
|
101
|
+
end.join("\n")
|
102
|
+
else
|
103
|
+
buffer = table.render
|
104
|
+
end
|
105
|
+
set_displayed
|
106
|
+
@table = Terminal::Table.new
|
107
|
+
buffer
|
108
|
+
end
|
109
|
+
|
110
|
+
# If needed setup table headings
|
111
|
+
def setup_header
|
112
|
+
if options.fetch(:header, true) and not displayed? then
|
113
|
+
if streaming? then
|
114
|
+
headings = [
|
115
|
+
format_column("Created", created_col_width, :center),
|
116
|
+
format_column("Severity", severity_col_width, :center),
|
117
|
+
format_column("Source", source_col_width, :center),
|
118
|
+
format_column("Format", format_col_width, :center),
|
119
|
+
format_column("Message", message_col_width, :center)
|
120
|
+
]
|
121
|
+
headings.unshift(format_column("Asset", tag_col_width, :center)) if requires_tag?
|
122
|
+
else
|
123
|
+
headings = [
|
124
|
+
"Created", "Severity", "Source", "Format", "Message"
|
125
|
+
]
|
126
|
+
headings.unshift("Asset") if requires_tag?
|
127
|
+
end
|
128
|
+
table.headings = headings
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Setup table width according to options
|
133
|
+
def setup_table
|
134
|
+
# Only setup table style once. Either dynamic or based on width of
|
135
|
+
if streaming? and not displayed? then
|
136
|
+
# If logs will be streaming, use the maximum width for the table
|
137
|
+
term_width = dynamic_terminal_width
|
138
|
+
@table_width = term_width
|
139
|
+
table.style = {:width => table_width}
|
140
|
+
elsif streaming? then
|
141
|
+
table.style = {:width => table_width}
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def streaming?
|
146
|
+
options.fetch(:streaming, false)
|
147
|
+
end
|
148
|
+
|
149
|
+
def displayed?
|
150
|
+
@already_displayed
|
151
|
+
end
|
152
|
+
def set_displayed
|
153
|
+
@already_displayed = true
|
154
|
+
end
|
155
|
+
|
156
|
+
# Require a tag if mode is all
|
157
|
+
def requires_tag?
|
158
|
+
@requires_tag ||= (options.fetch(:all_tag, nil).to_s.downcase == asset_tag.to_s.downcase)
|
159
|
+
end
|
160
|
+
|
161
|
+
def created_col_width
|
162
|
+
19 # this is the formatted datetime
|
163
|
+
end
|
164
|
+
def severity_col_width
|
165
|
+
13 # width of INFORMATIONAL
|
166
|
+
end
|
167
|
+
def source_col_width
|
168
|
+
8 # width of INTERNAL
|
169
|
+
end
|
170
|
+
def format_col_width
|
171
|
+
16 # width of application/json
|
172
|
+
end
|
173
|
+
def tag_col_width
|
174
|
+
requires_tag? ? 24 : 0
|
175
|
+
end
|
176
|
+
|
177
|
+
# This needs to take into account all the styling and such to avoid terminal table throwing an
|
178
|
+
# exception
|
179
|
+
def message_col_width
|
180
|
+
cells = requires_tag? ? 6 : 5
|
181
|
+
dyn_width = (table.cell_spacing * cells) + table.style.border_y.length
|
182
|
+
table_width - created_col_width - severity_col_width - source_col_width - format_col_width - tag_col_width - dyn_width
|
183
|
+
end
|
184
|
+
|
185
|
+
end # class LogPrinter
|
186
|
+
|
187
|
+
end # module CollinsShell
|