morpheus-cli 3.5.2 → 3.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/morpheus/api/api_client.rb +16 -0
  3. data/lib/morpheus/api/blueprints_interface.rb +84 -0
  4. data/lib/morpheus/api/execution_request_interface.rb +33 -0
  5. data/lib/morpheus/api/instances_interface.rb +21 -0
  6. data/lib/morpheus/api/packages_interface.rb +25 -5
  7. data/lib/morpheus/api/processes_interface.rb +34 -0
  8. data/lib/morpheus/api/roles_interface.rb +7 -0
  9. data/lib/morpheus/api/servers_interface.rb +8 -0
  10. data/lib/morpheus/api/user_settings_interface.rb +76 -0
  11. data/lib/morpheus/cli.rb +5 -1
  12. data/lib/morpheus/cli/alias_command.rb +1 -1
  13. data/lib/morpheus/cli/app_templates.rb +2 -1
  14. data/lib/morpheus/cli/apps.rb +173 -19
  15. data/lib/morpheus/cli/blueprints_command.rb +2134 -0
  16. data/lib/morpheus/cli/cli_command.rb +3 -1
  17. data/lib/morpheus/cli/clouds.rb +4 -10
  18. data/lib/morpheus/cli/coloring_command.rb +14 -8
  19. data/lib/morpheus/cli/containers_command.rb +92 -5
  20. data/lib/morpheus/cli/execution_request_command.rb +313 -0
  21. data/lib/morpheus/cli/hosts.rb +188 -7
  22. data/lib/morpheus/cli/instances.rb +472 -9
  23. data/lib/morpheus/cli/login.rb +1 -1
  24. data/lib/morpheus/cli/mixins/print_helper.rb +8 -0
  25. data/lib/morpheus/cli/mixins/processes_helper.rb +134 -0
  26. data/lib/morpheus/cli/option_types.rb +21 -16
  27. data/lib/morpheus/cli/packages_command.rb +469 -17
  28. data/lib/morpheus/cli/processes_command.rb +313 -0
  29. data/lib/morpheus/cli/remote.rb +20 -9
  30. data/lib/morpheus/cli/roles.rb +186 -6
  31. data/lib/morpheus/cli/shell.rb +10 -1
  32. data/lib/morpheus/cli/tasks.rb +4 -1
  33. data/lib/morpheus/cli/user_settings_command.rb +431 -0
  34. data/lib/morpheus/cli/version.rb +1 -1
  35. data/lib/morpheus/cli/whoami.rb +1 -1
  36. data/lib/morpheus/formatters.rb +14 -0
  37. data/lib/morpheus/morpkg.rb +119 -0
  38. data/morpheus-cli.gemspec +1 -0
  39. metadata +26 -2
@@ -762,7 +762,9 @@ module Morpheus
762
762
  end
763
763
 
764
764
  def default_command_name
765
- Morpheus::Cli::CliRegistry.cli_ize(self.name.split('::')[-1])
765
+ class_name = self.name.split('::')[-1]
766
+ #class_name.sub!(/Command$/, '')
767
+ Morpheus::Cli::CliRegistry.cli_ize(class_name)
766
768
  end
767
769
 
768
770
  def command_name
@@ -640,14 +640,6 @@ class Morpheus::Cli::Clouds
640
640
  def print_clouds_table(clouds, opts={})
641
641
  table_color = opts[:color] || cyan
642
642
  rows = clouds.collect do |cloud|
643
- status = nil
644
- if cloud['status'] == 'ok'
645
- status = "#{green}OK#{table_color}"
646
- elsif cloud['status'].nil?
647
- status = "#{white}UNKNOWN#{table_color}"
648
- else
649
- status = "#{red}#{cloud['status'] ? cloud['status'].upcase : 'N/A'}#{cloud['statusMessage'] ? "#{table_color} - #{cloud['statusMessage']}" : ''}#{table_color}"
650
- end
651
643
  cloud_type = cloud_type_for_id(cloud['zoneTypeId'])
652
644
  {
653
645
  id: cloud['id'],
@@ -656,7 +648,7 @@ class Morpheus::Cli::Clouds
656
648
  location: cloud['location'],
657
649
  groups: (cloud['groups'] || []).collect {|it| it.instance_of?(Hash) ? it['name'] : it.to_s }.join(', '),
658
650
  servers: cloud['serverCount'],
659
- status: status
651
+ status: format_cloud_status(cloud)
660
652
  }
661
653
  end
662
654
  columns = [
@@ -714,7 +706,9 @@ class Morpheus::Cli::Clouds
714
706
  def format_cloud_status(cloud, return_color=cyan)
715
707
  out = ""
716
708
  status_string = cloud['status']
717
- if status_string.nil? || status_string.empty? || status_string == "unknown"
709
+ if cloud['enabled'] == false
710
+ out << "#{red}DISABLED#{return_color}"
711
+ elsif status_string.nil? || status_string.empty? || status_string == "unknown"
718
712
  out << "#{white}UNKNOWN#{return_color}"
719
713
  elsif status_string == 'ok'
720
714
  out << "#{green}#{status_string.upcase}#{return_color}"
@@ -22,18 +22,24 @@ class Morpheus::Cli::ColoringCommand
22
22
  opts.footer = "Enable [on] or Disable [off] ANSI Colors for all output."
23
23
  end
24
24
  optparse.parse!(args)
25
- if args.count != 1
25
+ if args.count > 1
26
26
  puts optparse
27
27
  exit 1
28
28
  end
29
- is_on = ["on","true", "1"].include?(args[0].to_s.strip.downcase)
30
- is_off = ["off","false", "0"].include?(args[0].to_s.strip.downcase)
31
- if !is_on && !is_off
32
- puts optparse
33
- exit 1
29
+ if args.count == 1
30
+ is_on = ["on","true", "1"].include?(args[0].to_s.strip.downcase)
31
+ is_off = ["off","false", "0"].include?(args[0].to_s.strip.downcase)
32
+ if !is_on && !is_off
33
+ puts optparse
34
+ exit 1
35
+ end
36
+ Term::ANSIColor::coloring = is_on
37
+ end
38
+ if Term::ANSIColor::coloring?
39
+ puts "#{cyan}coloring is #{bold}#{green}on#{reset}"
40
+ else
41
+ puts "coloring is off"
34
42
  end
35
- Term::ANSIColor::coloring = is_on
36
- return true
37
43
  end
38
44
 
39
45
  end
@@ -14,10 +14,12 @@ class Morpheus::Cli::ContainersCommand
14
14
  set_command_name :containers
15
15
 
16
16
  register_subcommands :get, :stop, :start, :restart, :suspend, :eject, :action, :actions
17
+ register_subcommands :exec => :execution_request
17
18
 
18
19
  def connect(opts)
19
20
  @api_client = establish_remote_appliance_connection(opts)
20
21
  @containers_interface = @api_client.containers
22
+ @execution_request_interface = @api_client.execution_request
21
23
  end
22
24
 
23
25
  def handle(args)
@@ -31,9 +33,9 @@ class Morpheus::Cli::ContainersCommand
31
33
  opts.on( nil, '--actions', "Display Available Actions" ) do
32
34
  options[:include_available_actions] = true
33
35
  end
34
- opts.on('--refresh-until [status]', String, "Refresh until status is reached. Default status is running.") do |val|
36
+ opts.on('--refresh [status]', String, "Refresh until status is reached. Default status is running.") do |val|
35
37
  if val.to_s.empty?
36
- options[:refresh_until_status] = "running"
38
+ options[:refresh_until_status] = "running,failed"
37
39
  else
38
40
  options[:refresh_until_status] = val.to_s.downcase
39
41
  end
@@ -135,10 +137,13 @@ class Morpheus::Cli::ContainersCommand
135
137
  if options[:refresh_interval].nil? || options[:refresh_interval].to_f < 0
136
138
  options[:refresh_interval] = 5
137
139
  end
138
- while container['status'].to_s.downcase != options[:refresh_until_status].to_s.downcase
140
+ statuses = options[:refresh_until_status].to_s.downcase.split(",").collect {|s| s.strip }.select {|s| !s.to_s.empty? }
141
+ if !statuses.include?(container['status'])
139
142
  print cyan
140
- print "Refreshing until status #{options[:refresh_until_status]} ..."
141
- sleep(options[:refresh_interval])
143
+ print "Status is #{container['status'] || 'unknown'}. Refreshing in #{options[:refresh_interval]} seconds"
144
+ #sleep(options[:refresh_interval])
145
+ sleep_with_dots(options[:refresh_interval])
146
+ print "\n"
142
147
  _get(arg, options)
143
148
  end
144
149
  end
@@ -493,6 +498,88 @@ class Morpheus::Cli::ContainersCommand
493
498
  return 0
494
499
  end
495
500
 
501
+ def execution_request(args)
502
+ options = {}
503
+ params = {}
504
+ script_content = nil
505
+ do_refresh = true
506
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
507
+ opts.banner = subcommand_usage("[id] [options]")
508
+ opts.on('--script SCRIPT', "Script to be executed" ) do |val|
509
+ script_content = val
510
+ end
511
+ opts.on('--file FILE', "File containing the script. This can be used instead of --script" ) do |filename|
512
+ full_filename = File.expand_path(filename)
513
+ if File.exists?(full_filename)
514
+ script_content = File.read(full_filename)
515
+ else
516
+ print_red_alert "File not found: #{full_filename}"
517
+ exit 1
518
+ end
519
+ end
520
+ opts.on(nil, '--no-refresh', "Do not refresh until finished" ) do
521
+ do_refresh = false
522
+ end
523
+ #build_option_type_options(opts, options, add_user_source_option_types())
524
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet, :remote])
525
+ opts.footer = "Execute an arbitrary command or script on a container." + "\n" +
526
+ "[id] is required. This is the id a container." + "\n" +
527
+ "[script] is required. This is the script that is to be executed."
528
+ end
529
+ optparse.parse!(args)
530
+ connect(options)
531
+ if args.count != 1
532
+ print_error Morpheus::Terminal.angry_prompt
533
+ puts_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
534
+ return 1
535
+ end
536
+
537
+
538
+ begin
539
+ container = find_container_by_id(args[0])
540
+ return 1 if container.nil?
541
+ params['containerId'] = container['id']
542
+ # construct payload
543
+ payload = {}
544
+ if options[:payload]
545
+ payload = options[:payload]
546
+ else
547
+ payload.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
548
+ # prompt for Script
549
+ if script_content.nil?
550
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'script', 'type' => 'code-editor', 'fieldLabel' => 'Script', 'required' => true, 'description' => 'The script content'}], options[:options])
551
+ script_content = v_prompt['script']
552
+ end
553
+ payload['script'] = script_content
554
+ end
555
+ # dry run?
556
+ if options[:dry_run]
557
+ print_dry_run @execution_request_interface.dry.create(params, payload)
558
+ return 0
559
+ end
560
+ # do it
561
+ json_response = @execution_request_interface.create(params, payload)
562
+ # print and return result
563
+ if options[:quiet]
564
+ return 0
565
+ elsif options[:json]
566
+ puts as_json(json_response, options)
567
+ return 0
568
+ end
569
+ execution_request = json_response['executionRequest']
570
+ print_green_success "Executing request #{execution_request['uniqueId']}"
571
+ if do_refresh
572
+ Morpheus::Cli::ExecutionRequestCommand.new.handle(["get", execution_request['uniqueId'], "--refresh"])
573
+ else
574
+ Morpheus::Cli::ExecutionRequestCommand.new.handle(["get", execution_request['uniqueId']])
575
+ end
576
+ return 0
577
+ rescue RestClient::Exception => e
578
+ print_rest_exception(e, options)
579
+ exit 1
580
+ end
581
+ end
582
+
496
583
  private
497
584
 
498
585
  def find_container_by_id(id)
@@ -0,0 +1,313 @@
1
+ require 'morpheus/cli/cli_command'
2
+ # require 'morpheus/cli/mixins/provisioning_helper'
3
+ # require 'morpheus/cli/mixins/infrastructure_helper'
4
+
5
+ class Morpheus::Cli::ExecutionRequestCommand
6
+ include Morpheus::Cli::CliCommand
7
+ # include Morpheus::Cli::InfrastructureHelper
8
+ # include Morpheus::Cli::ProvisioningHelper
9
+
10
+ set_command_name :'execution-request'
11
+
12
+ register_subcommands :get, :execute
13
+ #register_subcommands :'execute-against-lease' => :execute_against_lease
14
+
15
+ # set_default_subcommand :list
16
+
17
+ def initialize()
18
+ # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
19
+ end
20
+
21
+ def connect(opts)
22
+ @api_client = establish_remote_appliance_connection(opts)
23
+ # @instances_interface = @api_client.instances
24
+ # @containers_interface = @api_client.containers
25
+ # @servers_interface = @api_client.servers
26
+ @execution_request_interface = @api_client.execution_request
27
+ end
28
+
29
+ def handle(args)
30
+ handle_subcommand(args)
31
+ end
32
+
33
+ def get(args)
34
+ raw_args = args
35
+ options = {}
36
+ params = {}
37
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
38
+ opts.banner = subcommand_usage("[uid]")
39
+ build_common_options(opts, options, [:query, :json, :yaml, :csv, :fields, :dry_run, :remote])
40
+ opts.on('--refresh', String, "Refresh until execution is finished.") do |val|
41
+ options[:refresh_until_finished] = true
42
+ end
43
+ opts.on('--refresh-interval seconds', String, "Refresh interval. Default is 5 seconds.") do |val|
44
+ options[:refresh_interval] = val.to_f
45
+ end
46
+ opts.footer = "Get details about an execution request." + "\n" +
47
+ "[uid] is required. This is the unique id of an execution request."
48
+ end
49
+ optparse.parse!(args)
50
+ connect(options)
51
+ if args.count != 1
52
+ print_error Morpheus::Terminal.angry_prompt
53
+ puts_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
54
+ return 1
55
+ end
56
+ execution_request_id = args[0]
57
+ begin
58
+ params.merge!(parse_list_options(options))
59
+ if options[:dry_run]
60
+ print_dry_run @execution_request_interface.dry.get(execution_request_id, params)
61
+ return
62
+ end
63
+ json_response = @execution_request_interface.get(execution_request_id, params)
64
+ if options[:json]
65
+ puts as_json(json_response, options, "executionRequest")
66
+ return 0
67
+ elsif options[:yaml]
68
+ puts as_yaml(json_response, options, "executionRequest")
69
+ return 0
70
+ elsif options[:csv]
71
+ puts records_as_csv([json_response['executionRequest']], options)
72
+ return 0
73
+ end
74
+
75
+ execution_request = json_response['executionRequest']
76
+
77
+ # refresh until a status is reached
78
+ if options[:refresh_until_finished]
79
+ if options[:refresh_interval].nil? || options[:refresh_interval].to_f < 0
80
+ options[:refresh_interval] = 5
81
+ end
82
+ if execution_request['exitCode'] || ['complete','failed','expired'].include?(execution_request['status'])
83
+ # it is finished
84
+ else
85
+ print cyan
86
+ print "Execution request has not yet finished. Refreshing every #{options[:refresh_interval]} seconds"
87
+ while execution_request['exitCode'].nil? do
88
+ sleep(options[:refresh_interval])
89
+ print cyan,".",reset
90
+ json_response = @execution_request_interface.get(execution_request_id, params)
91
+ execution_request = json_response['executionRequest']
92
+ end
93
+ #sleep_with_dots(options[:refresh_interval])
94
+ print "\n", reset
95
+ # get(raw_args)
96
+ end
97
+ end
98
+
99
+ print_h1 "Execution Request Details"
100
+ print cyan
101
+ description_cols = {
102
+ #"ID" => lambda {|it| it['id'] },
103
+ "Unique ID" => lambda {|it| it['uniqueId'] },
104
+ "Server ID" => lambda {|it| it['serverId'] },
105
+ "Instance ID" => lambda {|it| it['instanceId'] },
106
+ "Container ID" => lambda {|it| it['containerId'] },
107
+ "Expires At" => lambda {|it| format_local_dt it['expiresAt'] },
108
+ "Exit Code" => lambda {|it| it['exitCode'] },
109
+ "Status" => lambda {|it| format_execution_request_status(it) },
110
+ #"Created By" => lambda {|it| it['createdById'] },
111
+ #"Subdomain" => lambda {|it| it['subdomain'] },
112
+ }
113
+ print_description_list(description_cols, execution_request)
114
+
115
+ if execution_request['stdErr']
116
+ print_h2 "Error"
117
+ puts execution_request['stdErr'].to_s.strip
118
+ end
119
+ if execution_request['stdOut']
120
+ print_h2 "Output"
121
+ puts execution_request['stdOut'].to_s.strip
122
+ end
123
+ print reset, "\n"
124
+ return 0
125
+ rescue RestClient::Exception => e
126
+ print_rest_exception(e, options)
127
+ return 1
128
+ end
129
+ end
130
+
131
+ def execute(args)
132
+ options = {}
133
+ params = {}
134
+ script_content = nil
135
+ do_refresh = true
136
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
137
+ opts.banner = subcommand_usage("[options]")
138
+ opts.on('--server ID', String, "Server ID") do |val|
139
+ params['serverId'] = val
140
+ end
141
+ opts.on('--instance ID', String, "Instance ID") do |val|
142
+ params['instanceId'] = val
143
+ end
144
+ opts.on('--container ID', String, "Container ID") do |val|
145
+ params['containerId'] = val
146
+ end
147
+ opts.on('--request ID', String, "Execution Request ID") do |val|
148
+ params['requestId'] = val
149
+ end
150
+ opts.on('--script SCRIPT', "Script to be executed" ) do |val|
151
+ script_content = val
152
+ end
153
+ opts.on('--file FILE', "File containing the script. This can be used instead of --script" ) do |filename|
154
+ full_filename = File.expand_path(filename)
155
+ if File.exists?(full_filename)
156
+ script_content = File.read(full_filename)
157
+ else
158
+ print_red_alert "File not found: #{full_filename}"
159
+ exit 1
160
+ end
161
+ end
162
+ opts.on(nil, '--no-refresh', "Do not refresh until finished" ) do
163
+ do_refresh = false
164
+ end
165
+ #build_option_type_options(opts, options, add_user_source_option_types())
166
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet, :remote])
167
+ opts.footer = "Execute an arbitrary script." + "\n" +
168
+ "[server] or [instance] or [container] is required. This is the id of a server, instance or container." + "\n" +
169
+ "[script] is required. This is the script that is to be executed."
170
+ end
171
+ optparse.parse!(args)
172
+ connect(options)
173
+ if args.count != 0
174
+ print_error Morpheus::Terminal.angry_prompt
175
+ puts_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args.inspect}\n#{optparse}"
176
+ return 1
177
+ end
178
+ if params['serverId'].nil? && params['instanceId'].nil? && params['containerId'].nil? && params['requestId'].nil?
179
+ puts_error "#{Morpheus::Terminal.angry_prompt}missing required option: --server or --instance or --container\n#{optparse}"
180
+ return 1
181
+ end
182
+ begin
183
+ # construct payload
184
+ payload = {}
185
+ if options[:payload]
186
+ payload = options[:payload]
187
+ else
188
+ payload.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
189
+ # could prompt for Server or Container or Instance
190
+ # prompt for Script
191
+ if script_content.nil?
192
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'script', 'type' => 'code-editor', 'fieldLabel' => 'Script', 'required' => true, 'description' => 'The script content'}], options[:options])
193
+ script_content = v_prompt['script']
194
+ end
195
+ payload['script'] = script_content
196
+ end
197
+ # dry run?
198
+ if options[:dry_run]
199
+ print_dry_run @execution_request_interface.dry.create(params, payload)
200
+ return 0
201
+ end
202
+ # do it
203
+ json_response = @execution_request_interface.create(params, payload)
204
+ # print and return result
205
+ if options[:quiet]
206
+ return 0
207
+ elsif options[:json]
208
+ puts as_json(json_response, options)
209
+ return 0
210
+ end
211
+ execution_request = json_response['executionRequest']
212
+ print_green_success "Executing request #{execution_request['uniqueId']}"
213
+ if do_refresh
214
+ get([execution_request['uniqueId'], "--refresh"])
215
+ else
216
+ get([execution_request['uniqueId']])
217
+ end
218
+ return 0
219
+ rescue RestClient::Exception => e
220
+ print_rest_exception(e, options)
221
+ exit 1
222
+ end
223
+ end
224
+
225
+ def execute_against_lease(args)
226
+ options = {}
227
+ params = {}
228
+ do_refresh = true
229
+ script_content = nil
230
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
231
+ opts.banner = subcommand_usage("[uid] [options]")
232
+ opts.on('--script SCRIPT', "Script to be executed" ) do |val|
233
+ script_content = val
234
+ end
235
+ opts.on('--file FILE', "File containing the script. This can be used instead of --script" ) do |filename|
236
+ full_filename = File.expand_path(filename)
237
+ if File.exists?(full_filename)
238
+ script_content = File.read(full_filename)
239
+ else
240
+ print_red_alert "File not found: #{full_filename}"
241
+ exit 1
242
+ end
243
+ end
244
+ opts.on(nil, '--no-refresh', "Do not refresh until finished" ) do
245
+ do_refresh = false
246
+ end
247
+ #build_option_type_options(opts, options, add_user_source_option_types())
248
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet, :remote])
249
+ opts.footer = "Execute request against lease.\n" +
250
+ "[uid] is required. This is the unique id of the execution request.\n" +
251
+ "[script] is required. This is the script that is to be executed."
252
+ end
253
+ optparse.parse!(args)
254
+ connect(options)
255
+ if args.count != 1
256
+ print_error Morpheus::Terminal.angry_prompt
257
+ puts_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
258
+ return 1
259
+ end
260
+ execution_request_id = args[0]
261
+ begin
262
+ # construct payload
263
+ payload = {}
264
+ if options[:payload]
265
+ payload = options[:payload]
266
+ else
267
+ payload.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
268
+ if script_content
269
+ payload['script'] = script_content
270
+ end
271
+ end
272
+ # dry run?
273
+ if options[:dry_run]
274
+ print_dry_run @execution_request_interface.dry.execute_against_lease(execution_request_id, params, payload)
275
+ return 0
276
+ end
277
+ # do it
278
+ json_response = @execution_request_interface.execute_against_lease(execution_request_id, params, payload)
279
+ # print and return result
280
+ if options[:quiet]
281
+ return 0
282
+ elsif options[:json]
283
+ puts as_json(json_response, options)
284
+ return 0
285
+ end
286
+ execution_request = json_response['executionRequest']
287
+ print_green_success "Executing request #{execution_request['uniqueId']} against lease"
288
+ if do_refresh
289
+ get([execution_request['uniqueId'], "--refresh"])
290
+ else
291
+ get([execution_request['uniqueId']])
292
+ end
293
+ return 0
294
+ rescue RestClient::Exception => e
295
+ print_rest_exception(e, options)
296
+ exit 1
297
+ end
298
+ end
299
+
300
+ def format_execution_request_status(execution_request, return_color=cyan)
301
+ out = ""
302
+ status_str = execution_request['status']
303
+ if status_str == 'complete'
304
+ out << "#{green}#{status_str.upcase}#{return_color}"
305
+ elsif status_str == 'failed' || status_str == 'expired'
306
+ out << "#{red}#{status_str.upcase}#{return_color}"
307
+ else
308
+ out << "#{cyan}#{status_str.upcase}#{return_color}"
309
+ end
310
+ out
311
+ end
312
+
313
+ end