morpheus-cli 4.2.18 → 5.0.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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +14 -0
  4. data/lib/morpheus/api/billing_interface.rb +33 -0
  5. data/lib/morpheus/api/catalog_item_types_interface.rb +9 -0
  6. data/lib/morpheus/api/rest_interface.rb +0 -6
  7. data/lib/morpheus/api/roles_interface.rb +14 -0
  8. data/lib/morpheus/cli.rb +2 -2
  9. data/lib/morpheus/cli/apps.rb +3 -4
  10. data/lib/morpheus/cli/backup_jobs_command.rb +3 -0
  11. data/lib/morpheus/cli/backups_command.rb +3 -0
  12. data/lib/morpheus/cli/blueprints_command.rb +27 -61
  13. data/lib/morpheus/cli/budgets_command.rb +4 -4
  14. data/lib/morpheus/cli/catalog_command.rb +507 -0
  15. data/lib/morpheus/cli/cli_command.rb +30 -15
  16. data/lib/morpheus/cli/cloud_resource_pools_command.rb +16 -0
  17. data/lib/morpheus/cli/clouds.rb +7 -10
  18. data/lib/morpheus/cli/commands/standard/curl_command.rb +23 -7
  19. data/lib/morpheus/cli/commands/standard/source_command.rb +1 -1
  20. data/lib/morpheus/cli/commands/standard/update_command.rb +76 -0
  21. data/lib/morpheus/cli/containers_command.rb +14 -0
  22. data/lib/morpheus/cli/deployments.rb +1 -1
  23. data/lib/morpheus/cli/hosts.rb +30 -2
  24. data/lib/morpheus/cli/instances.rb +19 -1
  25. data/lib/morpheus/cli/invoices_command.rb +8 -9
  26. data/lib/morpheus/cli/jobs_command.rb +30 -8
  27. data/lib/morpheus/cli/library_instance_types_command.rb +17 -3
  28. data/lib/morpheus/cli/library_option_lists_command.rb +14 -6
  29. data/lib/morpheus/cli/mixins/accounts_helper.rb +7 -6
  30. data/lib/morpheus/cli/mixins/backups_helper.rb +2 -4
  31. data/lib/morpheus/cli/mixins/catalog_helper.rb +66 -0
  32. data/lib/morpheus/cli/mixins/deployments_helper.rb +0 -1
  33. data/lib/morpheus/cli/mixins/option_source_helper.rb +1 -1
  34. data/lib/morpheus/cli/mixins/print_helper.rb +46 -0
  35. data/lib/morpheus/cli/network_pools_command.rb +14 -6
  36. data/lib/morpheus/cli/ping.rb +0 -1
  37. data/lib/morpheus/cli/projects_command.rb +7 -7
  38. data/lib/morpheus/cli/provisioning_licenses_command.rb +2 -2
  39. data/lib/morpheus/cli/remote.rb +0 -2
  40. data/lib/morpheus/cli/roles.rb +305 -3
  41. data/lib/morpheus/cli/service_plans_command.rb +2 -2
  42. data/lib/morpheus/cli/storage_providers_command.rb +40 -56
  43. data/lib/morpheus/cli/tasks.rb +24 -10
  44. data/lib/morpheus/cli/tenants_command.rb +1 -1
  45. data/lib/morpheus/cli/usage_command.rb +150 -0
  46. data/lib/morpheus/cli/user_settings_command.rb +1 -0
  47. data/lib/morpheus/cli/users.rb +12 -1
  48. data/lib/morpheus/cli/version.rb +1 -1
  49. data/lib/morpheus/cli/workflows.rb +12 -9
  50. data/lib/morpheus/formatters.rb +26 -5
  51. metadata +8 -2
@@ -194,7 +194,7 @@ module Morpheus
194
194
  if option_type['placeHolder']
195
195
  value_label = option_type['placeHolder']
196
196
  elsif option_type['type'] == 'checkbox'
197
- value_label = 'on|off' # or.. true|false
197
+ value_label = '[on|off]' # or.. true|false
198
198
  elsif option_type['type'] == 'number'
199
199
  value_label = 'NUMBER'
200
200
  elsif option_type['type'] == 'multiSelect'
@@ -204,12 +204,16 @@ module Morpheus
204
204
  # elsif option['type'] == 'select'
205
205
  end
206
206
  opts.on("--#{full_field_name} #{value_label}", String, description) do |val|
207
- # attempt to parse JSON, this allows blank arrays for multiSelect like --tenants []
208
- if (val.to_s[0] == '{' && val.to_s[-1] == '}') || (val.to_s[0] == '[' && val.to_s[-1] == ']')
209
- begin
210
- val = JSON.parse(val)
211
- rescue
212
- Morpheus::Logging::DarkPrinter.puts "Failed to parse option value '#{val}' as JSON" if Morpheus::Logging.debug?
207
+ if option_type['type'] == 'checkbox'
208
+ val = (val.to_s != 'false' && val.to_s != 'off')
209
+ else
210
+ # attempt to parse JSON, this allows blank arrays for multiSelect like --tenants []
211
+ if (val.to_s[0] == '{' && val.to_s[-1] == '}') || (val.to_s[0] == '[' && val.to_s[-1] == ']')
212
+ begin
213
+ val = JSON.parse(val)
214
+ rescue
215
+ Morpheus::Logging::DarkPrinter.puts "Failed to parse option value '#{val}' as JSON" if Morpheus::Logging.debug?
216
+ end
213
217
  end
214
218
  end
215
219
  cur_namespace = custom_options
@@ -489,13 +493,17 @@ module Morpheus
489
493
 
490
494
  when :query, :query_filters
491
495
  # arbitrary query parameters in the format -Q "category=web&phrase=nginx"
496
+ # or pass it many times like -Q foo=bar -Q hello=world
492
497
  opts.on( '-Q', '--query PARAMS', "Query parameters. PARAMS format is 'foo=bar&category=web'" ) do |val|
493
- options[:query_filters_raw] = val
494
- options[:query_filters] = {}
495
- # todo: smarter parsing
498
+ if options[:query_filters_raw] && !options[:query_filters_raw].empty?
499
+ options[:query_filters_raw] += ("&" + val)
500
+ else
501
+ options[:query_filters_raw] = val
502
+ end
503
+ options[:query_filters] ||= {}
496
504
  val.split('&').each do |filter|
497
505
  k, v = filter.split('=')
498
- # allow "woot:true instead of woot=true"
506
+ # allow woot:true instead of woot=true
499
507
  if (k.include?(":") && v == nil)
500
508
  k, v = k.split(":")
501
509
  end
@@ -524,6 +532,12 @@ module Morpheus
524
532
  end
525
533
  end
526
534
 
535
+ when :find_by_name
536
+ opts.on('--find-by-name', "Always treat the identifier argument as a name, never an ID. Useful for specifying names that look like numbers. eg. '1234'" ) do
537
+ options[:find_by_name] = true
538
+ end
539
+ # opts.add_hidden_option('--find-by-name') if opts.is_a?(Morpheus::Cli::OptionParser)
540
+
527
541
  when :remote
528
542
  opts.on( '-r', '--remote REMOTE', "Remote name. The current remote is used by default." ) do |val|
529
543
  options[:remote] = val
@@ -1237,17 +1251,18 @@ module Morpheus
1237
1251
  end
1238
1252
  end
1239
1253
  if options[:outfile]
1254
+ full_outfile = File.expand_path(options[:outfile])
1240
1255
  if output
1241
1256
  print_to_file(output, options[:outfile], options[:overwrite])
1242
- print "#{cyan}Wrote output to file #{options[:outfile]} (#{File.size(options[:outfile])} B)\n" unless options[:quiet]
1257
+ print "#{cyan}Wrote output to file #{options[:outfile]} (#{File.size(full_outfile)} B)\n" unless options[:quiet]
1243
1258
  else
1244
1259
  # uhhh ok lets try this
1245
- Morpheus::Logging::DarkPrinter.puts "using experimental feature: --outfile without a common format like json, yml or csv" if Morpheus::Logging.debug?
1260
+ Morpheus::Logging::DarkPrinter.puts "using experimental feature: --out without a common format like json, yml or csv" if Morpheus::Logging.debug?
1246
1261
  result = with_stdout_to_file(options[:outfile], options[:overwrite], 'w+', &block)
1247
- print "#{cyan}Wrote output to file #{options[:outfile]} (#{File.size(options[:outfile])} B)\n" unless options[:quiet]
1248
- if result
1262
+ if result && result != 0
1249
1263
  return result
1250
1264
  end
1265
+ print "#{cyan}Wrote output to file #{options[:outfile]} (#{File.size(full_outfile)} B)\n" unless options[:quiet]
1251
1266
  return 0, nil
1252
1267
  end
1253
1268
  else
@@ -326,6 +326,9 @@ class Morpheus::Cli::CloudResourcePoolsCommand
326
326
  opts.on('--active [on|off]', String, "Can be used to disable a resource pool") do |val|
327
327
  options['active'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
328
328
  end
329
+ opts.on('--default-pool [on|off]', String, "Set resource pool as the default") do |val|
330
+ options['defaultPool'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
331
+ end
329
332
  build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
330
333
  opts.footer = "Update a resource pool." + "\n" +
331
334
  "[cloud] is required. This is the name or id of the cloud."
@@ -437,6 +440,11 @@ class Morpheus::Cli::CloudResourcePoolsCommand
437
440
  else
438
441
  payload['resourcePool']['active'] = true
439
442
  end
443
+
444
+ # Default
445
+ if options['defaultPool'] != nil
446
+ payload['resourcePool']['defaultPool'] = options['defaultPool']
447
+ end
440
448
 
441
449
  # Visibility
442
450
  if options['visibility'] != nil
@@ -552,6 +560,9 @@ class Morpheus::Cli::CloudResourcePoolsCommand
552
560
  opts.on('--active [on|off]', String, "Can be used to disable a resource pool") do |val|
553
561
  options['active'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
554
562
  end
563
+ opts.on('--default-pool [on|off]', String, "Set resource pool as the default") do |val|
564
+ options['defaultPool'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
565
+ end
555
566
  build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
556
567
  opts.footer = "Update a resource pool." + "\n" +
557
568
  "[cloud] is required. This is the name or id of the cloud." + "\n"
@@ -641,6 +652,11 @@ class Morpheus::Cli::CloudResourcePoolsCommand
641
652
  if options['active'] != nil
642
653
  payload['resourcePool']['active'] = options['active']
643
654
  end
655
+
656
+ # Default
657
+ if options['defaultPool'] != nil
658
+ payload['resourcePool']['defaultPool'] = options['defaultPool']
659
+ end
644
660
 
645
661
  # Visibility
646
662
  if options['visibility'] != nil
@@ -46,11 +46,15 @@ class Morpheus::Cli::Clouds
46
46
  opts.on( '-t', '--type TYPE', "Cloud Type" ) do |val|
47
47
  options[:zone_type] = val
48
48
  end
49
- build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
49
+ build_standard_list_options(opts, options)
50
50
  opts.footer = "List clouds."
51
51
  end
52
52
  optparse.parse!(args)
53
53
  connect(options)
54
+ # verify_args!(args:args, optparse:optparse, count:0)
55
+ if args.count > 0
56
+ options[:phrase] = args.join(" ")
57
+ end
54
58
  begin
55
59
  if options[:zone_type]
56
60
  cloud_type = cloud_type_for_name(options[:zone_type])
@@ -71,15 +75,7 @@ class Morpheus::Cli::Clouds
71
75
  end
72
76
 
73
77
  json_response = @clouds_interface.list(params)
74
- if options[:json]
75
- puts as_json(json_response, options, "zones")
76
- return 0
77
- elsif options[:yaml]
78
- puts as_yaml(json_response, options, "zones")
79
- return 0
80
- elsif options[:csv]
81
- puts records_as_csv(json_response['zones'], options)
82
- else
78
+ render_response(json_response, options, 'zones') do
83
79
  clouds = json_response['zones']
84
80
  title = "Morpheus Clouds"
85
81
  subtitles = []
@@ -99,6 +95,7 @@ class Morpheus::Cli::Clouds
99
95
  end
100
96
  print reset,"\n"
101
97
  end
98
+ return 0, nil
102
99
  rescue RestClient::Exception => e
103
100
  print_rest_exception(e, options)
104
101
  exit 1
@@ -9,15 +9,20 @@ class Morpheus::Cli::CurlCommand
9
9
  set_command_hidden
10
10
 
11
11
  def handle(args)
12
- split_args = args.join(" ").split(" -- ")
13
- args = split_args[0].split(" ")
14
- curl_args = split_args[1] ? split_args[1].split(" ") : []
12
+ # support syntax for arbitrary curl args after " -- "
13
+ # eg. curl /api/instances -- -ksv
14
+ split_index = args.index("--")
15
+ curl_args = []
16
+ if split_index
17
+ if args.length > (split_index + 1)
18
+ curl_args = args[(split_index + 1)..-1]
19
+ end
20
+ args = args[0..(split_index - 1)]
21
+ end
15
22
  curl_method = nil
16
23
  curl_data = nil
17
24
  curl_verbsose = false
18
25
  show_progress = false
19
- # puts "args is : #{args}"
20
- # puts "curl_args is : #{curl_args}"
21
26
  options = {}
22
27
  optparse = Morpheus::Cli::OptionParser.new do|opts|
23
28
  opts.banner = "Usage: morpheus curl [path] -- [*args]"
@@ -98,16 +103,27 @@ EOT
98
103
  end
99
104
  curl_cmd << " \"#{url}\""
100
105
  if @access_token
101
- curl_cmd << " -H \"Authorization: Bearer #{@access_token}\""
106
+ if !(options[:headers] && options[:headers]['Authorization'])
107
+ curl_cmd << " -H \"Authorization: Bearer #{@access_token}\""
108
+ end
102
109
  end
103
110
  if curl_data
104
111
  #todo: curl_data.gsub("'","\\'")
105
112
  curl_cmd << " --data '#{curl_data}'"
113
+ if api_path !~ /^\/?oauth/
114
+ if !(options[:headers] && options[:headers]['Content-Type'])
115
+ curl_cmd << " -H \"Content-Type: application/json\""
116
+ end
117
+ end
118
+ end
119
+ if options[:headers]
120
+ options[:headers].each do |k,v|
121
+ curl_cmd << " -H \"#{k}: #{v}\""
122
+ end
106
123
  end
107
124
  if !curl_args.empty?
108
125
  curl_cmd << " " + curl_args.join(' ')
109
126
  end
110
-
111
127
  # Morpheus::Logging::DarkPrinter.puts "#{curl_cmd}" if Morpheus::Logging.debug?
112
128
  curl_cmd_str = options[:scrub] ? Morpheus::Logging.scrub_message(curl_cmd) : curl_cmd
113
129
 
@@ -14,7 +14,7 @@ class Morpheus::Cli::SourceCommand
14
14
  optparse = Morpheus::Cli::OptionParser.new do|opts|
15
15
  opts.banner = "Usage: morpheus #{command_name} [file] [file2]"
16
16
  build_common_options(opts, options, [])
17
- opts.footer = "This will execute a file, treatin it as a script of morpheus commands"
17
+ opts.footer = "This will execute a file as a script where each line is a morpheus command or expression."
18
18
  end
19
19
  optparse.parse!(args)
20
20
  if args.count < 1
@@ -0,0 +1,76 @@
1
+ require 'morpheus/cli/cli_command'
2
+ require 'json'
3
+
4
+ # This is for use in dotfile scripts and the shell..
5
+ class Morpheus::Cli::UpdateCommand
6
+ include Morpheus::Cli::CliCommand
7
+ set_command_name :update
8
+
9
+ def handle(args)
10
+ options = {}
11
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
12
+ opts.banner = "Usage: morpheus #{command_name}"
13
+ opts.on( '-f', '--force', "Force Update, executes update even if latest version is already installed." ) do
14
+ options[:force] = true
15
+ end
16
+ build_common_options(opts, options, [:dry_run, :quiet])
17
+ opts.footer = "This will update the morpheus command line interface to the latest version.\nThis is done by executing the system command: `gem update #{morpheus_gem_name}`"
18
+ end
19
+ optparse.parse!(args)
20
+ verify_args!(args:args, optparse:optparse, count:0)
21
+
22
+ current_version = Morpheus::Cli::VERSION
23
+ latest_version = get_latest_version()
24
+ latest_version = latest_version
25
+
26
+ if current_version == latest_version && !options[:force]
27
+ unless options[:quiet]
28
+ print cyan, "The latest version is already installed. (#{latest_version})", "\n", reset
29
+ end
30
+ return 0, nil
31
+ end
32
+
33
+ unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to update the #{morpheus_gem_name} gem from version #{current_version} to version #{latest_version}?")
34
+ return 9, "aborted command"
35
+ end
36
+
37
+ gem_update_command = "gem update #{morpheus_gem_name}"
38
+
39
+ if options[:dry_run]
40
+ unless options[:quiet]
41
+ print "\n"
42
+ print "#{cyan}#{bold}#{dark}COMMAND#{reset}\n"
43
+ puts gem_update_command
44
+ print "\n", reset
45
+ end
46
+ return 0, nil
47
+ end
48
+
49
+ # ok, update it
50
+ if options[:quiet]
51
+ system(gem_update_command)
52
+ else
53
+ `#{gem_update_command}`
54
+ end
55
+
56
+ if $?.success?
57
+ return 0, nil
58
+ else
59
+ return $?.exitstatus, "update failed"
60
+ end
61
+
62
+ end
63
+
64
+ protected
65
+
66
+ def morpheus_gem_name
67
+ 'morpheus-cli'
68
+ end
69
+
70
+ def get_latest_version
71
+ result = HTTP.get("https://rubygems.org/api/v1/gems/#{morpheus_gem_name}.json")
72
+ json_response = JSON.parse(result.body)
73
+ json_response["version"]
74
+ end
75
+
76
+ end
@@ -35,6 +35,9 @@ class Morpheus::Cli::ContainersCommand
35
35
  opts.on( nil, '--actions', "Display Available Actions" ) do
36
36
  options[:include_available_actions] = true
37
37
  end
38
+ opts.on( nil, '--costs', "Display Cost and Price" ) do
39
+ options[:include_costs] = true
40
+ end
38
41
  opts.on('--refresh [SECONDS]', String, "Refresh until status is running,failed. Default interval is #{default_refresh_interval} seconds.") do |val|
39
42
  options[:refresh_until_status] ||= "running,failed"
40
43
  if !val.to_s.empty?
@@ -99,6 +102,8 @@ class Morpheus::Cli::ContainersCommand
99
102
  "Name" => lambda {|it| it['server'] ? it['server']['name'] : '(no server)' }, # there is a server.displayName too?
100
103
  "Type" => lambda {|it| it['containerType'] ? it['containerType']['name'] : '' },
101
104
  "Plan" => lambda {|it| it['plan'] ? it['plan']['name'] : '' },
105
+ # "Cost" => lambda {|it| it['hourlyCost'] ? format_money(it['hourlyCost'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
106
+ # "Price" => lambda {|it| it['hourlyPrice'] ? format_money(it['hourlyPrice'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
102
107
  "Instance" => lambda {|it| it['instance'] ? it['instance']['name'] : '' },
103
108
  "Host" => lambda {|it| it['server'] ? it['server']['name'] : '' },
104
109
  "Cloud" => lambda {|it| it['cloud'] ? it['cloud']['name'] : '' },
@@ -133,6 +138,15 @@ class Morpheus::Cli::ContainersCommand
133
138
  end
134
139
  end
135
140
 
141
+ if options[:include_costs]
142
+ print_h2 "Container Cost"
143
+ cost_columns = {
144
+ "Cost" => lambda {|it| it['hourlyCost'] ? format_money(it['hourlyCost'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
145
+ "Price" => lambda {|it| it['hourlyPrice'] ? format_money(it['hourlyPrice'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
146
+ }
147
+ print_description_list(cost_columns, container)
148
+ end
149
+
136
150
  print reset, "\n"
137
151
 
138
152
  # refresh until a status is reached
@@ -388,7 +388,7 @@ EOT
388
388
  deployment = find_deployment_by_name_or_id(args[0])
389
389
  return 1 if deployment.nil?
390
390
  else
391
- deployment_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'deploymentId', 'fieldLabel' => 'Deployment', 'type' => 'select', 'required' => true, 'description' => 'Deployment to add version to', 'optionSource' => lambda {
391
+ deployment_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'deploymentId', 'fieldLabel' => 'Deployment', 'type' => 'select', 'required' => true, 'description' => 'Deployment to add version to', 'optionSource' => lambda { |api_client, api_params|
392
392
  @deployments_interface.list(max:10000)['deployments'].collect {|it|
393
393
  {'name' => it['name'], 'value' => it['id']}
394
394
  }
@@ -119,6 +119,10 @@ class Morpheus::Cli::Hosts
119
119
  end
120
120
  optparse.parse!(args)
121
121
  connect(options)
122
+ # verify_args!(args:args, optparse:optparse, count:0)
123
+ if args.count > 0
124
+ options[:phrase] = args.join(" ")
125
+ end
122
126
  begin
123
127
  params.merge!(parse_list_options(options))
124
128
  account = nil
@@ -392,6 +396,9 @@ class Morpheus::Cli::Hosts
392
396
  options = {}
393
397
  optparse = Morpheus::Cli::OptionParser.new do |opts|
394
398
  opts.banner = subcommand_usage("[name]")
399
+ opts.on( nil, '--costs', "Display Cost and Price" ) do
400
+ options[:include_costs] = true
401
+ end
395
402
  opts.on('--refresh [SECONDS]', String, "Refresh until status is provisioned,failed. Default interval is #{default_refresh_interval} seconds.") do |val|
396
403
  options[:refresh_until_status] ||= "provisioned,failed"
397
404
  if !val.to_s.empty?
@@ -453,7 +460,7 @@ class Morpheus::Cli::Hosts
453
460
  title = "Host Details"
454
461
  print_h1 title, [], options
455
462
  print cyan
456
- print_description_list({
463
+ server_columns = {
457
464
  "ID" => 'id',
458
465
  "Name" => 'name',
459
466
  "Description" => 'description',
@@ -463,12 +470,23 @@ class Morpheus::Cli::Hosts
463
470
  "Type" => lambda {|it| it['computeServerType'] ? it['computeServerType']['name'] : 'unmanaged' },
464
471
  "Platform" => lambda {|it| it['serverOs'] ? it['serverOs']['name'].upcase : 'N/A' },
465
472
  "Plan" => lambda {|it| it['plan'] ? it['plan']['name'] : '' },
473
+ "Cost" => lambda {|it| it['hourlyCost'] ? format_money(it['hourlyCost'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
474
+ "Price" => lambda {|it| it['hourlyPrice'] ? format_money(it['hourlyPrice'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
466
475
  "Agent" => lambda {|it| it['agentInstalled'] ? "#{server['agentVersion'] || ''} updated at #{format_local_dt(server['lastAgentUpdate'])}" : '(not installed)' },
467
476
  "Status" => lambda {|it| format_server_status(it) },
468
477
  "Nodes" => lambda {|it| it['containers'] ? it['containers'].size : 0 },
469
478
  "Power" => lambda {|it| format_server_power_state(it) },
470
- }, server)
479
+ }
471
480
 
481
+ if server['hourlyCost'].to_f == 0
482
+ server_columns.delete("Cost")
483
+ end
484
+ if server['hourlyPrice'].to_f == 0 || server['hourlyPrice'] == server['hourlyCost']
485
+ server_columns.delete("Price")
486
+ end
487
+
488
+ print_description_list(server_columns, server)
489
+
472
490
  if server['statusMessage']
473
491
  print_h2 "Status Message", options
474
492
  if server['status'] == 'failed'
@@ -485,6 +503,16 @@ class Morpheus::Cli::Hosts
485
503
 
486
504
  print_h2 "Host Usage", options
487
505
  print_stats_usage(stats)
506
+
507
+ if options[:include_costs]
508
+ print_h2 "Host Cost"
509
+ cost_columns = {
510
+ "Cost" => lambda {|it| it['hourlyCost'] ? format_money(it['hourlyCost'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
511
+ "Price" => lambda {|it| it['hourlyPrice'] ? format_money(it['hourlyPrice'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
512
+ }
513
+ print_description_list(cost_columns, server)
514
+ end
515
+
488
516
  print reset, "\n"
489
517
 
490
518
 
@@ -96,7 +96,10 @@ class Morpheus::Cli::Instances
96
96
  opts.footer = "List instances."
97
97
  end
98
98
  optparse.parse!(args)
99
- verify_args!(args:args, count:0, optparse:optparse)
99
+ # verify_args!(args:args, optparse:optparse, count:0)
100
+ if args.count > 0
101
+ options[:phrase] = args.join(" ")
102
+ end
100
103
  connect(options)
101
104
  begin
102
105
  params.merge!(parse_list_options(options))
@@ -1148,6 +1151,9 @@ class Morpheus::Cli::Instances
1148
1151
  opts.on( nil, '--scaling', "Display Instance Scaling Settings" ) do
1149
1152
  options[:include_scaling] = true
1150
1153
  end
1154
+ opts.on( nil, '--costs', "Display Cost and Price" ) do
1155
+ options[:include_costs] = true
1156
+ end
1151
1157
  opts.on('--refresh [SECONDS]', String, "Refresh until status is running,failed. Default interval is #{default_refresh_interval} seconds.") do |val|
1152
1158
  options[:refresh_until_status] ||= "running,failed"
1153
1159
  if !val.to_s.empty?
@@ -1249,6 +1255,8 @@ class Morpheus::Cli::Instances
1249
1255
  "Layout" => lambda {|it| it['layout'] ? it['layout']['name'] : '' },
1250
1256
  "Version" => lambda {|it| it['instanceVersion'] },
1251
1257
  "Plan" => lambda {|it| it['plan'] ? it['plan']['name'] : '' },
1258
+ # "Cost" => lambda {|it| it['hourlyCost'] ? format_money(it['hourlyCost'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
1259
+ # "Price" => lambda {|it| it['hourlyPrice'] ? format_money(it['hourlyPrice'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
1252
1260
  "Environment" => 'instanceContext',
1253
1261
  "Labels" => lambda {|it| it['tags'] ? it['tags'].join(',') : '' },
1254
1262
  "Metadata" => lambda {|it| it['metadata'] ? it['metadata'].collect {|m| "#{m['name']}: #{m['value']}" }.join(', ') : '' },
@@ -1304,6 +1312,16 @@ class Morpheus::Cli::Instances
1304
1312
  print_h2 "Instance Usage", options
1305
1313
  print_stats_usage(stats)
1306
1314
  end
1315
+
1316
+ if options[:include_costs]
1317
+ print_h2 "Instance Cost"
1318
+ cost_columns = {
1319
+ "Cost" => lambda {|it| it['hourlyCost'] ? format_money(it['hourlyCost'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
1320
+ "Price" => lambda {|it| it['hourlyPrice'] ? format_money(it['hourlyPrice'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
1321
+ }
1322
+ print_description_list(cost_columns, instance)
1323
+ end
1324
+
1307
1325
  print reset, "\n"
1308
1326
 
1309
1327
  # if options[:include_lb]