morpheus-cli 4.2.21 → 5.2.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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +30 -0
  4. data/lib/morpheus/api/billing_interface.rb +34 -0
  5. data/lib/morpheus/api/catalog_item_types_interface.rb +9 -0
  6. data/lib/morpheus/api/deploy_interface.rb +1 -1
  7. data/lib/morpheus/api/deployments_interface.rb +20 -1
  8. data/lib/morpheus/api/forgot_password_interface.rb +17 -0
  9. data/lib/morpheus/api/instances_interface.rb +16 -2
  10. data/lib/morpheus/api/rest_interface.rb +0 -6
  11. data/lib/morpheus/api/roles_interface.rb +14 -0
  12. data/lib/morpheus/api/search_interface.rb +13 -0
  13. data/lib/morpheus/api/servers_interface.rb +14 -0
  14. data/lib/morpheus/api/service_catalog_interface.rb +89 -0
  15. data/lib/morpheus/api/usage_interface.rb +18 -0
  16. data/lib/morpheus/cli.rb +7 -3
  17. data/lib/morpheus/cli/apps.rb +6 -27
  18. data/lib/morpheus/cli/backup_jobs_command.rb +3 -0
  19. data/lib/morpheus/cli/backups_command.rb +3 -0
  20. data/lib/morpheus/cli/catalog_item_types_command.rb +622 -0
  21. data/lib/morpheus/cli/cli_command.rb +70 -21
  22. data/lib/morpheus/cli/commands/standard/curl_command.rb +26 -12
  23. data/lib/morpheus/cli/commands/standard/history_command.rb +3 -1
  24. data/lib/morpheus/cli/commands/standard/man_command.rb +74 -40
  25. data/lib/morpheus/cli/commands/standard/source_command.rb +1 -1
  26. data/lib/morpheus/cli/commands/standard/update_command.rb +76 -0
  27. data/lib/morpheus/cli/containers_command.rb +14 -24
  28. data/lib/morpheus/cli/cypher_command.rb +6 -2
  29. data/lib/morpheus/cli/deploy.rb +199 -90
  30. data/lib/morpheus/cli/deployments.rb +341 -28
  31. data/lib/morpheus/cli/deploys.rb +206 -41
  32. data/lib/morpheus/cli/error_handler.rb +7 -0
  33. data/lib/morpheus/cli/forgot_password.rb +133 -0
  34. data/lib/morpheus/cli/groups.rb +1 -1
  35. data/lib/morpheus/cli/health_command.rb +59 -2
  36. data/lib/morpheus/cli/hosts.rb +265 -34
  37. data/lib/morpheus/cli/instances.rb +186 -100
  38. data/lib/morpheus/cli/invoices_command.rb +33 -16
  39. data/lib/morpheus/cli/jobs_command.rb +28 -6
  40. data/lib/morpheus/cli/library_option_lists_command.rb +15 -7
  41. data/lib/morpheus/cli/library_option_types_command.rb +5 -2
  42. data/lib/morpheus/cli/logs_command.rb +9 -6
  43. data/lib/morpheus/cli/mixins/accounts_helper.rb +12 -7
  44. data/lib/morpheus/cli/mixins/backups_helper.rb +2 -4
  45. data/lib/morpheus/cli/mixins/deployments_helper.rb +31 -3
  46. data/lib/morpheus/cli/mixins/option_source_helper.rb +1 -1
  47. data/lib/morpheus/cli/mixins/print_helper.rb +46 -21
  48. data/lib/morpheus/cli/mixins/provisioning_helper.rb +100 -4
  49. data/lib/morpheus/cli/network_pools_command.rb +14 -6
  50. data/lib/morpheus/cli/option_types.rb +271 -22
  51. data/lib/morpheus/cli/ping.rb +0 -1
  52. data/lib/morpheus/cli/remote.rb +35 -12
  53. data/lib/morpheus/cli/reports_command.rb +99 -30
  54. data/lib/morpheus/cli/roles.rb +453 -113
  55. data/lib/morpheus/cli/search_command.rb +182 -0
  56. data/lib/morpheus/cli/service_catalog_command.rb +1474 -0
  57. data/lib/morpheus/cli/service_plans_command.rb +2 -2
  58. data/lib/morpheus/cli/setup.rb +1 -1
  59. data/lib/morpheus/cli/shell.rb +33 -11
  60. data/lib/morpheus/cli/storage_providers_command.rb +40 -56
  61. data/lib/morpheus/cli/tasks.rb +29 -32
  62. data/lib/morpheus/cli/usage_command.rb +203 -0
  63. data/lib/morpheus/cli/user_settings_command.rb +1 -0
  64. data/lib/morpheus/cli/users.rb +12 -1
  65. data/lib/morpheus/cli/version.rb +1 -1
  66. data/lib/morpheus/cli/virtual_images.rb +429 -254
  67. data/lib/morpheus/cli/whoami.rb +6 -6
  68. data/lib/morpheus/cli/workflows.rb +34 -41
  69. data/lib/morpheus/formatters.rb +75 -7
  70. data/lib/morpheus/terminal.rb +6 -2
  71. metadata +14 -2
@@ -19,7 +19,7 @@ class Morpheus::Cli::Instances
19
19
  :history, {:'history-details' => :history_details}, {:'history-event' => :history_event_details},
20
20
  :stats, :stop, :start, :restart, :actions, :action, :suspend, :eject, :stop_service, :start_service, :restart_service,
21
21
  :backup, :backups, :resize, :clone, :envs, :setenv, :delenv,
22
- :security_groups, :apply_security_groups, :run_workflow, :import_snapshot,
22
+ :security_groups, :apply_security_groups, :run_workflow, :import_snapshot, :snapshot, :snapshots,
23
23
  :console, :status_check, {:containers => :list_containers},
24
24
  :scaling, {:'scaling-update' => :scaling_update},
25
25
  :wiki, :update_wiki,
@@ -80,9 +80,6 @@ class Morpheus::Cli::Instances
80
80
  options[:owner] = val
81
81
  end
82
82
  opts.add_hidden_option('--created-by')
83
- opts.on('--details', "Display more details: memory and storage usage used / max values." ) do
84
- options[:details] = true
85
- end
86
83
  opts.on('--status STATUS', "Filter by status i.e. provisioning,running,starting,stopping") do |val|
87
84
  params['status'] = (params['status'] || []) + val.to_s.split(',').collect {|s| s.strip }.select {|s| s != "" }
88
85
  end
@@ -92,11 +89,45 @@ class Morpheus::Cli::Instances
92
89
  opts.on('--pending-removal-only', "Only instances pending removal.") do
93
90
  options[:deleted] = true
94
91
  end
92
+ opts.on( '--plan NAME', String, "Filter by Plan name(s)" ) do |val|
93
+ # commas used in names a lot so use --plan one --plan two
94
+ params['plan'] ||= []
95
+ params['plan'] << val
96
+ end
97
+ opts.on( '--plan-id ID', String, "Filter by Plan id(s)" ) do |val|
98
+ params['planId'] = parse_id_list(val)
99
+ end
100
+ opts.on( '--plan-code CODE', String, "Filter by Plan code(s)" ) do |val|
101
+ params['planCode'] = parse_id_list(val)
102
+ end
103
+ opts.on('--labels label',String, "Filter by labels (keywords).") do |val|
104
+ val.split(",").each do |k|
105
+ options[:labels] ||= []
106
+ options[:labels] << k.strip
107
+ end
108
+ end
109
+ opts.on('--tags Name=Value',String, "Filter by tags (metadata name value pairs).") do |val|
110
+ val.split(",").each do |value_pair|
111
+ k,v = value_pair.strip.split("=")
112
+ options[:tags] ||= {}
113
+ options[:tags][k] ||= []
114
+ options[:tags][k] << (v || '')
115
+ end
116
+ end
117
+ opts.on('--stats', "Display values for memory and storage usage used / max values." ) do
118
+ options[:stats] = true
119
+ end
120
+ opts.on('-a', '--details', "Display all details: plan, stats, etc" ) do
121
+ options[:details] = true
122
+ end
95
123
  build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
96
124
  opts.footer = "List instances."
97
125
  end
98
126
  optparse.parse!(args)
99
- verify_args!(args:args, count:0, optparse:optparse)
127
+ # verify_args!(args:args, optparse:optparse, count:0)
128
+ if args.count > 0
129
+ options[:phrase] = args.join(" ")
130
+ end
100
131
  connect(options)
101
132
  begin
102
133
  params.merge!(parse_list_options(options))
@@ -129,7 +160,13 @@ class Morpheus::Cli::Instances
129
160
 
130
161
  params['showDeleted'] = true if options[:showDeleted]
131
162
  params['deleted'] = true if options[:deleted]
132
-
163
+ params['labels'] = options[:labels] if options[:labels]
164
+ if options[:tags]
165
+ options[:tags].each do |k,v|
166
+ params['tags.' + k] = v
167
+ end
168
+ end
169
+
133
170
  @instances_interface.setopts(options)
134
171
  if options[:dry_run]
135
172
  print_dry_run @instances_interface.dry.list(params)
@@ -193,7 +230,7 @@ class Morpheus::Cli::Instances
193
230
  cpu_usage_str = !stats ? "" : generate_usage_bar((stats['usedCpu'] || stats['cpuUsage']).to_f, 100, {max_bars: 10})
194
231
  memory_usage_str = !stats ? "" : generate_usage_bar(stats['usedMemory'], stats['maxMemory'], {max_bars: 10})
195
232
  storage_usage_str = !stats ? "" : generate_usage_bar(stats['usedStorage'], stats['maxStorage'], {max_bars: 10})
196
- if options[:details]
233
+ if options[:details] || options[:stats]
197
234
  if stats['maxMemory'] && stats['maxMemory'].to_i != 0
198
235
  memory_usage_str = memory_usage_str + cyan + format_bytes_short(stats['usedMemory']).strip.rjust(8, ' ') + " / " + format_bytes_short(stats['maxMemory']).strip
199
236
  end
@@ -211,8 +248,9 @@ class Morpheus::Cli::Instances
211
248
  nodes: instance['containers'].count,
212
249
  status: format_instance_status(instance, cyan),
213
250
  type: instance['instanceType']['name'],
214
- group: !instance['group'].nil? ? instance['group']['name'] : nil,
215
- cloud: !instance['cloud'].nil? ? instance['cloud']['name'] : nil,
251
+ group: instance['group'] ? instance['group']['name'] : nil,
252
+ cloud: instance['cloud'] ? instance['cloud']['name'] : nil,
253
+ plan: instance['plan'] ? instance['plan']['name'] : '',
216
254
  version: instance['instanceVersion'] ? instance['instanceVersion'] : '',
217
255
  created: format_local_dt(instance['dateCreated']),
218
256
  cpu: cpu_usage_str + cyan,
@@ -226,12 +264,13 @@ class Morpheus::Cli::Instances
226
264
  {:created => {:display_name => "CREATED"}},
227
265
  # {:tenant => {:display_name => "TENANT"}},
228
266
  {:user => {:display_name => "OWNER", :max_width => 20}},
267
+ :plan,
229
268
  :nodes, {:connection => {:max_width => 30}}, :status, :cpu, :memory, :storage]
230
269
  # custom pretty table columns ... this is handled in as_pretty_table now(),
231
270
  # todo: remove all these.. and try to always pass rows as the json data itself..
232
- # if options[:include_fields]
233
- # columns = options[:include_fields]
234
- # end
271
+ if options[:details] != true
272
+ columns.delete(:plan)
273
+ end
235
274
  print cyan
236
275
  print as_pretty_table(rows, columns, options)
237
276
  print reset
@@ -344,18 +383,16 @@ class Morpheus::Cli::Instances
344
383
  opts.on("--environment ENV", String, "Environment code") do |val|
345
384
  options[:environment] = val.to_s
346
385
  end
347
- opts.on('--metadata LIST', String, "Metadata tags in the format 'name:value, name:value'") do |val|
386
+ opts.on('--tags LIST', String, "Metadata tags in the format 'ping=pong,flash=bang'") do |val|
348
387
  options[:metadata] = val
349
388
  end
350
- opts.on('--labels LIST', String, "Labels (keywords) in the format 'foo, bar'") do |val|
351
- #options[:tags] = val
352
- options[:tags] = val.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
389
+ opts.on('--metadata LIST', String, "Metadata tags in the format 'ping=pong,flash=bang'") do |val|
390
+ options[:metadata] = val
353
391
  end
354
- opts.on('--tags LIST', String, "Tags") do |val|
355
- #options[:tags] = val
356
- options[:tags] = val.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
392
+ opts.add_hidden_option('--metadata')
393
+ opts.on('--labels LIST', String, "Labels (keywords) in the format 'foo, bar'") do |val|
394
+ options[:labels] = val.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
357
395
  end
358
- opts.add_hidden_option('--tags')
359
396
  opts.on("--copies NUMBER", Integer, "Number of copies to provision") do |val|
360
397
  options[:copies] = val.to_i
361
398
  end
@@ -525,18 +562,17 @@ class Morpheus::Cli::Instances
525
562
  opts.on('--group GROUP', String, "Group Name or ID") do |val|
526
563
  options[:group] = val
527
564
  end
528
- opts.on('--metadata LIST', String, "Metadata tags in the format 'name:value, name:value'") do |val|
565
+ opts.on('--tags LIST', String, "Metadata tags in the format 'ping=pong,flash=bang'") do |val|
529
566
  options[:metadata] = val
530
567
  end
531
- opts.on('--labels LIST', String, "Labels (keywords) in the format 'foo, bar'") do |val|
532
- #params['tags'] = val
533
- params['tags'] = val.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
568
+ opts.on('--metadata LIST', String, "Metadata tags in the format 'ping=pong,flash=bang'") do |val|
569
+ options[:metadata] = val
534
570
  end
535
- opts.on('--tags LIST', String, "Tags") do |val|
536
- #params['tags'] = val
571
+ opts.add_hidden_option('--metadata')
572
+ opts.on('--labels LIST', String, "Labels (keywords) in the format 'foo, bar'") do |val|
573
+ # todo switch this from 'tags' to 'labels'
537
574
  params['tags'] = val.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
538
575
  end
539
- opts.add_hidden_option('--tags')
540
576
  opts.on('--power-schedule-type ID', String, "Power Schedule Type ID") do |val|
541
577
  params['powerScheduleType'] = val == "null" ? nil : val
542
578
  end
@@ -607,6 +643,9 @@ class Morpheus::Cli::Instances
607
643
  metadata_list = options[:metadata].split(",").select {|it| !it.to_s.empty? }
608
644
  metadata_list = metadata_list.collect do |it|
609
645
  metadata_pair = it.split(":")
646
+ if metadata_pair.size == 1 && it.include?("=")
647
+ metadata_pair = it.split("=")
648
+ end
610
649
  row = {}
611
650
  row['name'] = metadata_pair[0].to_s.strip
612
651
  row['value'] = metadata_pair[1].to_s.strip
@@ -1148,6 +1187,9 @@ class Morpheus::Cli::Instances
1148
1187
  opts.on( nil, '--scaling', "Display Instance Scaling Settings" ) do
1149
1188
  options[:include_scaling] = true
1150
1189
  end
1190
+ opts.on( nil, '--costs', "Display Cost and Price" ) do
1191
+ options[:include_costs] = true
1192
+ end
1151
1193
  opts.on('--refresh [SECONDS]', String, "Refresh until status is running,failed. Default interval is #{default_refresh_interval} seconds.") do |val|
1152
1194
  options[:refresh_until_status] ||= "running,failed"
1153
1195
  if !val.to_s.empty?
@@ -1211,7 +1253,15 @@ class Morpheus::Cli::Instances
1211
1253
  instance = json_response['instance']
1212
1254
  stats = instance['stats'] || json_response['stats'] || {}
1213
1255
  # load_balancers = json_response['loadBalancers'] || {}
1214
-
1256
+ # metadata tags used to be returned as metadata and are now returned as tags
1257
+ # the problem is tags is what we used to call Labels (keywords)
1258
+ # the api will change to tags and labels, so handle the old format as long as metadata is returned.
1259
+ tags, labels = nil, nil
1260
+ if instance.key?('metadata')
1261
+ tags, labels = instance['metadata'], instance['tags']
1262
+ else
1263
+ tags, labels = instance['tags'], instance['labels']
1264
+ end
1215
1265
  # containers are fetched via separate api call
1216
1266
  containers = nil
1217
1267
  if options[:include_containers]
@@ -1249,9 +1299,11 @@ class Morpheus::Cli::Instances
1249
1299
  "Layout" => lambda {|it| it['layout'] ? it['layout']['name'] : '' },
1250
1300
  "Version" => lambda {|it| it['instanceVersion'] },
1251
1301
  "Plan" => lambda {|it| it['plan'] ? it['plan']['name'] : '' },
1302
+ # "Cost" => lambda {|it| it['hourlyCost'] ? format_money(it['hourlyCost'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
1303
+ # "Price" => lambda {|it| it['hourlyPrice'] ? format_money(it['hourlyPrice'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
1252
1304
  "Environment" => 'instanceContext',
1253
1305
  "Labels" => lambda {|it| it['tags'] ? it['tags'].join(',') : '' },
1254
- "Metadata" => lambda {|it| it['metadata'] ? it['metadata'].collect {|m| "#{m['name']}: #{m['value']}" }.join(', ') : '' },
1306
+ "Tags" => lambda {|it| tags ? tags.collect {|m| "#{m['name']}: #{m['value']}" }.join(', ') : '' },
1255
1307
  "Owner" => lambda {|it|
1256
1308
  if it['owner']
1257
1309
  (it['owner']['username'] || it['owner']['id'])
@@ -1270,11 +1322,14 @@ class Morpheus::Cli::Instances
1270
1322
  "Connection" => lambda {|it| format_instance_connection_string(it) },
1271
1323
  "Status" => lambda {|it| format_instance_status(it) }
1272
1324
  }
1325
+ description_cols.delete("Labels") if labels.nil? || labels.empty?
1326
+ description_cols.delete("Tags") if tags.nil? || tags.empty?
1273
1327
  description_cols.delete("Power Schedule") if instance['powerSchedule'].nil?
1274
1328
  description_cols.delete("Expire Date") if instance['expireDate'].nil?
1275
1329
  description_cols.delete("Shutdown Date") if instance['shutdownDate'].nil?
1276
1330
  description_cols["Removal Date"] = lambda {|it| format_local_dt(it['removalDate'])} if instance['status'] == 'pendingRemoval'
1277
1331
  description_cols.delete("Last Deployment") if instance['lastDeploy'].nil?
1332
+ #description_cols.delete("Environment") if instance['instanceContext'].nil?
1278
1333
  print_description_list(description_cols, instance)
1279
1334
 
1280
1335
  if instance['statusMessage']
@@ -1304,6 +1359,16 @@ class Morpheus::Cli::Instances
1304
1359
  print_h2 "Instance Usage", options
1305
1360
  print_stats_usage(stats)
1306
1361
  end
1362
+
1363
+ if options[:include_costs]
1364
+ print_h2 "Instance Cost"
1365
+ cost_columns = {
1366
+ "Cost" => lambda {|it| it['hourlyCost'] ? format_money(it['hourlyCost'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
1367
+ "Price" => lambda {|it| it['hourlyPrice'] ? format_money(it['hourlyPrice'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
1368
+ }
1369
+ print_description_list(cost_columns, instance)
1370
+ end
1371
+
1307
1372
  print reset, "\n"
1308
1373
 
1309
1374
  # if options[:include_lb]
@@ -2578,6 +2643,48 @@ class Morpheus::Cli::Instances
2578
2643
  end
2579
2644
  end
2580
2645
 
2646
+ def snapshot(args)
2647
+ options = {}
2648
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
2649
+ opts.banner = subcommand_usage("[instance]")
2650
+ opts.on( '--name VALUE', String, "Snapshot Name. Default is server name + timestamp" ) do |val|
2651
+ options[:options]['name'] = val
2652
+ end
2653
+ opts.on( '--description VALUE', String, "Snapshot Description." ) do |val|
2654
+ options[:options]['description'] = val
2655
+ end
2656
+ build_standard_add_options(opts, options, [:auto_confirm])
2657
+ opts.footer = <<-EOT
2658
+ Create a snapshot for an instance.
2659
+ [instance] is required. This is the name or id of an instance
2660
+ EOT
2661
+ end
2662
+ optparse.parse!(args)
2663
+ verify_args!(args:args, optparse:optparse, count:1)
2664
+ connect(options)
2665
+ instance = find_instance_by_name_or_id(args[0])
2666
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to snapshot the instance '#{instance['name']}'?", options)
2667
+ exit 1
2668
+ end
2669
+ payload = {}
2670
+ if options[:payload]
2671
+ payload = options[:payload]
2672
+ payload.deep_merge!({'snapshot' => parse_passed_options(options)})
2673
+ else
2674
+ payload.deep_merge!({'snapshot' => parse_passed_options(options)})
2675
+ end
2676
+ @instances_interface.setopts(options)
2677
+ if options[:dry_run]
2678
+ print_dry_run @instances_interface.dry.snapshot(instance['id'], payload)
2679
+ return
2680
+ end
2681
+ json_response = @instances_interface.snapshot(instance['id'], payload)
2682
+ render_response(json_response, options, 'snapshots') do
2683
+ print_green_success "Snapshot initiated."
2684
+ end
2685
+ return 0, nil
2686
+ end
2687
+
2581
2688
  def remove(args)
2582
2689
  options = {}
2583
2690
  query_params = {}
@@ -2880,6 +2987,56 @@ class Morpheus::Cli::Instances
2880
2987
  end
2881
2988
  end
2882
2989
 
2990
+ def snapshots(args)
2991
+ options = {}
2992
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
2993
+ opts.banner = subcommand_usage("[instance]")
2994
+ # no pagination yet
2995
+ # build_standard_list_options(opts, options)
2996
+ build_standard_get_options(opts, options)
2997
+ opts.footer = <<-EOT
2998
+ List snapshots for an instance.
2999
+ [instance] is required. This is the name or id of an instance
3000
+ EOT
3001
+ end
3002
+ optparse.parse!(args)
3003
+ verify_args!(args:args, optparse:optparse, count:1)
3004
+ connect(options)
3005
+ begin
3006
+ instance = find_instance_by_name_or_id(args[0])
3007
+ params = {}
3008
+ @instances_interface.setopts(options)
3009
+ if options[:dry_run]
3010
+ print_dry_run @instances_interface.dry.snapshots(instance['id'], params)
3011
+ return
3012
+ end
3013
+ json_response = @instances_interface.snapshots(instance['id'], params)
3014
+ snapshots = json_response['snapshots']
3015
+ render_response(json_response, options, 'snapshots') do
3016
+ print_h1 "Snapshots: #{instance['name']} (#{instance['instanceType']['name']})", [], options
3017
+ if snapshots.empty?
3018
+ print cyan,"No snapshots found",reset,"\n"
3019
+ else
3020
+ snapshot_column_definitions = {
3021
+ "ID" => lambda {|it| it['id'] },
3022
+ "Name" => lambda {|it| it['name'] },
3023
+ "Description" => lambda {|it| it['snapshotType'] ? (it['snapshotType']['name'] || it['snapshotType']['code']) : '' },
3024
+ "Date Created" => lambda {|it| format_local_dt(it['snapshotCreated']) },
3025
+ "Status" => lambda {|it| format_snapshot_status(it) }
3026
+ }
3027
+ print cyan
3028
+ print as_pretty_table(snapshots, snapshot_column_definitions.upcase_keys!, options)
3029
+ print_results_pagination({size: snapshots.size, total: snapshots.size})
3030
+ end
3031
+ print reset, "\n"
3032
+ end
3033
+ return 0
3034
+ rescue RestClient::Exception => e
3035
+ print_rest_exception(e, options)
3036
+ exit 1
3037
+ end
3038
+ end
3039
+
2883
3040
  def import_snapshot(args)
2884
3041
  options = {}
2885
3042
  query_params = {}
@@ -3792,51 +3949,6 @@ private
3792
3949
  end
3793
3950
  end
3794
3951
 
3795
- def format_instance_status(instance, return_color=cyan)
3796
- out = ""
3797
- status_string = instance['status'].to_s
3798
- if status_string == 'running'
3799
- out << "#{green}#{status_string.upcase}#{return_color}"
3800
- elsif status_string == 'provisioning'
3801
- out << "#{cyan}#{status_string.upcase}#{return_color}"
3802
- elsif status_string == 'stopped' or status_string == 'failed'
3803
- out << "#{red}#{status_string.upcase}#{return_color}"
3804
- else
3805
- out << "#{yellow}#{status_string.upcase}#{return_color}"
3806
- end
3807
- out
3808
- end
3809
-
3810
- def format_instance_connection_string(instance)
3811
- if !instance['connectionInfo'].nil? && instance['connectionInfo'].empty? == false
3812
- connection_string = "#{instance['connectionInfo'][0]['ip']}:#{instance['connectionInfo'][0]['port']}"
3813
- end
3814
- end
3815
-
3816
- def format_container_status(container, return_color=cyan)
3817
- out = ""
3818
- status_string = container['status'].to_s
3819
- if status_string == 'running'
3820
- out << "#{green}#{status_string.upcase}#{return_color}"
3821
- elsif status_string == 'provisioning'
3822
- out << "#{cyan}#{status_string.upcase}#{return_color}"
3823
- elsif status_string == 'stopped' or status_string == 'failed'
3824
- out << "#{red}#{status_string.upcase}#{return_color}"
3825
- else
3826
- out << "#{yellow}#{status_string.upcase}#{return_color}"
3827
- end
3828
- out
3829
- end
3830
-
3831
- def format_container_connection_string(container)
3832
- if !container['ports'].nil? && container['ports'].empty? == false
3833
- connection_string = "#{container['ip']}:#{container['ports'][0]['external']}"
3834
- else
3835
- # eh? more logic needed here i think, see taglib morph:containerLocationMenu
3836
- connection_string = "#{container['ip']}"
3837
- end
3838
- end
3839
-
3840
3952
  def instance_scaling_option_types(instance=nil)
3841
3953
 
3842
3954
  # Group
@@ -3890,17 +4002,6 @@ private
3890
4002
  list
3891
4003
  end
3892
4004
 
3893
- def format_instance_container_display_name(instance, plural=false)
3894
- #<span class="info-label">${[null,'docker'].contains(instance.layout?.provisionType?.code) ? 'Containers' : 'Virtual Machines'}:</span> <span class="info-value">${instance.containers?.size()}</span>
3895
- v = plural ? "Containers" : "Container"
3896
- if instance && instance['layout'] && instance['layout'].key?("provisionTypeCode")
3897
- if [nil, 'docker'].include?(instance['layout']["provisionTypeCode"])
3898
- v = plural ? "Virtual Machines" : "Virtual Machine"
3899
- end
3900
- end
3901
- return v
3902
- end
3903
-
3904
4005
  def print_instance_threshold_description_list(instance_threshold)
3905
4006
  description_cols = {
3906
4007
  # "Instance" => lambda {|it| "#{instance['id']} - #{instance['name']}" },
@@ -4006,19 +4107,4 @@ private
4006
4107
  }
4007
4108
  end
4008
4109
 
4009
- def format_app_deploy_status(status, return_color=cyan)
4010
- out = ""
4011
- s = status.to_s.downcase
4012
- if s == 'deployed'
4013
- out << "#{green}#{s.upcase}#{return_color}"
4014
- elsif s == 'open' || s == 'archived' || s == 'committed'
4015
- out << "#{cyan}#{s.upcase}#{return_color}"
4016
- elsif s == 'failed'
4017
- out << "#{red}#{s.upcase}#{return_color}"
4018
- else
4019
- out << "#{yellow}#{s.upcase}#{return_color}"
4020
- end
4021
- out
4022
- end
4023
-
4024
4110
  end
@@ -25,16 +25,19 @@ class Morpheus::Cli::InvoicesCommand
25
25
  options = {}
26
26
  params = {}
27
27
  ref_ids = []
28
- query_tags = {}
29
28
  optparse = Morpheus::Cli::OptionParser.new do |opts|
30
29
  opts.banner = subcommand_usage()
31
30
  opts.on('-a', '--all', "Display all details, costs and prices." ) do
32
31
  options[:show_all] = true
32
+ options[:show_dates] = true
33
33
  options[:show_estimates] = true
34
34
  # options[:show_costs] = true
35
35
  options[:show_prices] = true
36
36
  # options[:show_raw_data] = true
37
37
  end
38
+ opts.on('--dates', "Display Ref Start, Ref End, etc.") do |val|
39
+ options[:show_dates] = true
40
+ end
38
41
  opts.on('--estimates', '--estimates', "Display all estimated costs, from usage info: Compute, Storage, Network, Extra" ) do
39
42
  options[:show_estimates] = true
40
43
  end
@@ -107,9 +110,12 @@ class Morpheus::Cli::InvoicesCommand
107
110
  params['accountId'] = val
108
111
  end
109
112
  opts.on('--tags Name=Value',String, "Filter by tags.") do |val|
110
- k,v = val.split("=")
111
- query_tags[k] ||= []
112
- query_tags[k] << v
113
+ val.split(",").each do |value_pair|
114
+ k,v = value_pair.strip.split("=")
115
+ options[:tags] ||= {}
116
+ options[:tags][k] ||= []
117
+ options[:tags][k] << (v || '')
118
+ end
113
119
  end
114
120
  opts.on('--raw-data', '--raw-data', "Display Raw Data, the cost data from the cloud provider's API.") do |val|
115
121
  options[:show_raw_data] = true
@@ -169,8 +175,8 @@ class Morpheus::Cli::InvoicesCommand
169
175
  end
170
176
  params['rawData'] = true if options[:show_raw_data]
171
177
  params['refId'] = ref_ids unless ref_ids.empty?
172
- if query_tags && !query_tags.empty?
173
- query_tags.each do |k,v|
178
+ if options[:tags] && !options[:tags].empty?
179
+ options[:tags].each do |k,v|
174
180
  params['tags.' + k] = v
175
181
  end
176
182
  end
@@ -203,6 +209,7 @@ class Morpheus::Cli::InvoicesCommand
203
209
  {"INVOICE ID" => lambda {|it| it['id'] } },
204
210
  {"TYPE" => lambda {|it| format_invoice_ref_type(it) } },
205
211
  {"REF ID" => lambda {|it| it['refId'] } },
212
+ {"REF UUID" => lambda {|it| it['refUuid'] } },
206
213
  {"REF NAME" => lambda {|it|
207
214
  if options[:show_all]
208
215
  it['refName']
@@ -218,9 +225,11 @@ class Morpheus::Cli::InvoicesCommand
218
225
  {"PERIOD" => lambda {|it| format_invoice_period(it) } },
219
226
  {"START" => lambda {|it| format_date(it['startDate']) } },
220
227
  {"END" => lambda {|it| format_date(it['endDate']) } },
221
- ] + (options[:show_all] ? [
228
+ ] + ((options[:show_dates] || options[:show_all]) ? [
222
229
  {"REF START" => lambda {|it| format_dt(it['refStart']) } },
223
230
  {"REF END" => lambda {|it| format_dt(it['refEnd']) } },
231
+ # {"LAST COST DATE" => lambda {|it| format_local_dt(it['lastCostDate']) } },
232
+ # {"LAST ACTUAL DATE" => lambda {|it| format_local_dt(it['lastActualDate']) } },
224
233
  ] : []) + [
225
234
  {"COMPUTE" => lambda {|it| format_money(it['computeCost'], 'usd', {sigdig:options[:sigdig]}) } },
226
235
  # {"MEMORY" => lambda {|it| format_money(it['memoryCost']) } },
@@ -272,9 +281,15 @@ class Morpheus::Cli::InvoicesCommand
272
281
  {"PROJECT TAGS" => lambda {|it| it['project'] ? truncate_string(format_metadata(it['project']['tags']), 50) : '' } },
273
282
  ]
274
283
  end
284
+ if options[:show_dates]
285
+ columns += [
286
+ {"LAST COST DATE" => lambda {|it| format_local_dt(it['lastCostDate']) } },
287
+ {"LAST ACTUAL DATE" => lambda {|it| format_local_dt(it['lastActualDate']) } },
288
+ ]
289
+ end
275
290
  columns += [
276
291
  {"CREATED" => lambda {|it| format_local_dt(it['dateCreated']) } },
277
- {"UPDATED" => lambda {|it| format_local_dt(it['lastUpdated']) } }
292
+ {"UPDATED" => lambda {|it| format_local_dt(it['lastUpdated']) } },
278
293
  ]
279
294
  if options[:show_raw_data]
280
295
  columns += [{"RAW DATA" => lambda {|it| truncate_string(it['rawData'].to_s, 10) } }]
@@ -656,7 +671,6 @@ EOT
656
671
  options = {}
657
672
  params = {}
658
673
  ref_ids = []
659
- query_tags = {}
660
674
  optparse = Morpheus::Cli::OptionParser.new do |opts|
661
675
  opts.banner = subcommand_usage()
662
676
  opts.on('-a', '--all', "Display all details, costs and prices." ) do
@@ -744,11 +758,14 @@ EOT
744
758
  opts.on('--tenant ID', String, "View invoice line items for a tenant. Default is your own account.") do |val|
745
759
  params['accountId'] = val
746
760
  end
747
- # opts.on('--tags Name=Value',String, "Filter by tags.") do |val|
748
- # k,v = val.split("=")
749
- # query_tags[k] ||= []
750
- # query_tags[k] << v
751
- # end
761
+ opts.on('--tags Name=Value',String, "Filter by tags.") do |val|
762
+ val.split(",").each do |value_pair|
763
+ k,v = value_pair.strip.split("=")
764
+ options[:tags] ||= {}
765
+ options[:tags][k] ||= []
766
+ options[:tags][k] << (v || '')
767
+ end
768
+ end
752
769
  opts.on('--raw-data', '--raw-data', "Display Raw Data, the cost data from the cloud provider's API.") do |val|
753
770
  options[:show_raw_data] = true
754
771
  end
@@ -808,8 +825,8 @@ EOT
808
825
  end
809
826
  params['rawData'] = true if options[:show_raw_data]
810
827
  params['refId'] = ref_ids unless ref_ids.empty?
811
- if query_tags && !query_tags.empty?
812
- query_tags.each do |k,v|
828
+ if options[:tags] && !options[:tags].empty?
829
+ options[:tags].each do |k,v|
813
830
  params['tags.' + k] = v
814
831
  end
815
832
  end