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