morpheus-cli 5.0.0 → 5.2.2

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Dockerfile +1 -1
  4. data/lib/morpheus/api/api_client.rb +16 -0
  5. data/lib/morpheus/api/billing_interface.rb +1 -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/invoices_interface.rb +12 -3
  11. data/lib/morpheus/api/search_interface.rb +13 -0
  12. data/lib/morpheus/api/servers_interface.rb +14 -0
  13. data/lib/morpheus/api/service_catalog_interface.rb +89 -0
  14. data/lib/morpheus/api/usage_interface.rb +18 -0
  15. data/lib/morpheus/cli.rb +6 -2
  16. data/lib/morpheus/cli/apps.rb +3 -23
  17. data/lib/morpheus/cli/budgets_command.rb +389 -319
  18. data/lib/morpheus/cli/{catalog_command.rb → catalog_item_types_command.rb} +182 -67
  19. data/lib/morpheus/cli/cli_command.rb +51 -10
  20. data/lib/morpheus/cli/commands/standard/curl_command.rb +26 -13
  21. data/lib/morpheus/cli/commands/standard/history_command.rb +9 -3
  22. data/lib/morpheus/cli/commands/standard/man_command.rb +74 -40
  23. data/lib/morpheus/cli/containers_command.rb +0 -24
  24. data/lib/morpheus/cli/cypher_command.rb +6 -2
  25. data/lib/morpheus/cli/dashboard_command.rb +260 -20
  26. data/lib/morpheus/cli/deploy.rb +199 -90
  27. data/lib/morpheus/cli/deployments.rb +341 -28
  28. data/lib/morpheus/cli/deploys.rb +206 -41
  29. data/lib/morpheus/cli/error_handler.rb +7 -0
  30. data/lib/morpheus/cli/forgot_password.rb +133 -0
  31. data/lib/morpheus/cli/groups.rb +1 -1
  32. data/lib/morpheus/cli/health_command.rb +59 -2
  33. data/lib/morpheus/cli/hosts.rb +271 -39
  34. data/lib/morpheus/cli/instances.rb +228 -129
  35. data/lib/morpheus/cli/invoices_command.rb +100 -20
  36. data/lib/morpheus/cli/jobs_command.rb +94 -92
  37. data/lib/morpheus/cli/library_option_lists_command.rb +1 -1
  38. data/lib/morpheus/cli/library_option_types_command.rb +10 -5
  39. data/lib/morpheus/cli/logs_command.rb +9 -6
  40. data/lib/morpheus/cli/mixins/accounts_helper.rb +5 -1
  41. data/lib/morpheus/cli/mixins/deployments_helper.rb +31 -2
  42. data/lib/morpheus/cli/mixins/print_helper.rb +13 -27
  43. data/lib/morpheus/cli/mixins/provisioning_helper.rb +108 -5
  44. data/lib/morpheus/cli/option_types.rb +271 -22
  45. data/lib/morpheus/cli/remote.rb +35 -10
  46. data/lib/morpheus/cli/reports_command.rb +99 -30
  47. data/lib/morpheus/cli/roles.rb +193 -155
  48. data/lib/morpheus/cli/search_command.rb +182 -0
  49. data/lib/morpheus/cli/service_catalog_command.rb +1474 -0
  50. data/lib/morpheus/cli/setup.rb +1 -1
  51. data/lib/morpheus/cli/shell.rb +33 -11
  52. data/lib/morpheus/cli/tasks.rb +29 -32
  53. data/lib/morpheus/cli/usage_command.rb +64 -11
  54. data/lib/morpheus/cli/version.rb +1 -1
  55. data/lib/morpheus/cli/virtual_images.rb +429 -254
  56. data/lib/morpheus/cli/whoami.rb +6 -6
  57. data/lib/morpheus/cli/workflows.rb +33 -40
  58. data/lib/morpheus/formatters.rb +75 -18
  59. data/lib/morpheus/terminal.rb +6 -2
  60. metadata +10 -4
  61. data/lib/morpheus/cli/mixins/catalog_helper.rb +0 -66
@@ -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,6 +89,37 @@ 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
@@ -132,7 +160,13 @@ class Morpheus::Cli::Instances
132
160
 
133
161
  params['showDeleted'] = true if options[:showDeleted]
134
162
  params['deleted'] = true if options[:deleted]
135
-
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
+
136
170
  @instances_interface.setopts(options)
137
171
  if options[:dry_run]
138
172
  print_dry_run @instances_interface.dry.list(params)
@@ -196,7 +230,7 @@ class Morpheus::Cli::Instances
196
230
  cpu_usage_str = !stats ? "" : generate_usage_bar((stats['usedCpu'] || stats['cpuUsage']).to_f, 100, {max_bars: 10})
197
231
  memory_usage_str = !stats ? "" : generate_usage_bar(stats['usedMemory'], stats['maxMemory'], {max_bars: 10})
198
232
  storage_usage_str = !stats ? "" : generate_usage_bar(stats['usedStorage'], stats['maxStorage'], {max_bars: 10})
199
- if options[:details]
233
+ if options[:details] || options[:stats]
200
234
  if stats['maxMemory'] && stats['maxMemory'].to_i != 0
201
235
  memory_usage_str = memory_usage_str + cyan + format_bytes_short(stats['usedMemory']).strip.rjust(8, ' ') + " / " + format_bytes_short(stats['maxMemory']).strip
202
236
  end
@@ -214,8 +248,9 @@ class Morpheus::Cli::Instances
214
248
  nodes: instance['containers'].count,
215
249
  status: format_instance_status(instance, cyan),
216
250
  type: instance['instanceType']['name'],
217
- group: !instance['group'].nil? ? instance['group']['name'] : nil,
218
- 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'] : '',
219
254
  version: instance['instanceVersion'] ? instance['instanceVersion'] : '',
220
255
  created: format_local_dt(instance['dateCreated']),
221
256
  cpu: cpu_usage_str + cyan,
@@ -229,12 +264,13 @@ class Morpheus::Cli::Instances
229
264
  {:created => {:display_name => "CREATED"}},
230
265
  # {:tenant => {:display_name => "TENANT"}},
231
266
  {:user => {:display_name => "OWNER", :max_width => 20}},
267
+ :plan,
232
268
  :nodes, {:connection => {:max_width => 30}}, :status, :cpu, :memory, :storage]
233
269
  # custom pretty table columns ... this is handled in as_pretty_table now(),
234
270
  # todo: remove all these.. and try to always pass rows as the json data itself..
235
- # if options[:include_fields]
236
- # columns = options[:include_fields]
237
- # end
271
+ if options[:details] != true
272
+ columns.delete(:plan)
273
+ end
238
274
  print cyan
239
275
  print as_pretty_table(rows, columns, options)
240
276
  print reset
@@ -347,18 +383,16 @@ class Morpheus::Cli::Instances
347
383
  opts.on("--environment ENV", String, "Environment code") do |val|
348
384
  options[:environment] = val.to_s
349
385
  end
350
- 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|
351
387
  options[:metadata] = val
352
388
  end
353
- opts.on('--labels LIST', String, "Labels (keywords) in the format 'foo, bar'") do |val|
354
- #options[:tags] = val
355
- 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
356
391
  end
357
- opts.on('--tags LIST', String, "Tags") do |val|
358
- #options[:tags] = val
359
- 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(',')
360
395
  end
361
- opts.add_hidden_option('--tags')
362
396
  opts.on("--copies NUMBER", Integer, "Number of copies to provision") do |val|
363
397
  options[:copies] = val.to_i
364
398
  end
@@ -425,17 +459,58 @@ class Morpheus::Cli::Instances
425
459
  options[:instance_name] = args[0]
426
460
  end
427
461
 
428
- # use active group by default
429
- options[:group] ||= @active_group_id
430
- options[:select_datastore] = true
431
- options[:name_required] = true
432
462
  begin
433
463
  payload = nil
434
464
  if options[:payload]
435
465
  payload = options[:payload]
436
466
  # support -O OPTION switch on top of --payload
437
467
  payload.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
468
+ # obviously should support every option that prompt supports on top of -- payload as well
469
+ # group, cloud and type for now
470
+ # todo: also support :layout, service_plan, :resource_pool, etc.
471
+ group = nil
472
+ if options[:group]
473
+ group = find_group_by_name_or_id_for_provisioning(options[:group])
474
+ if group.nil?
475
+ return 1, "group not found by #{options[:group]}"
476
+ end
477
+ #payload["siteId"] = group["id"]
478
+ payload.deep_merge!({"instance" => {"site" => {"id" => group["id"]} } })
479
+ end
480
+ if options[:cloud]
481
+ group_id = group ? group["id"] : ((payload["instance"] && payload["instance"]["site"].is_a?(Hash)) ? payload["instance"]["site"]["id"] : nil)
482
+ cloud = find_cloud_by_name_or_id_for_provisioning(group_id, options[:cloud])
483
+ if cloud.nil?
484
+ return 1, "cloud not found by #{options[:cloud]}"
485
+ end
486
+ payload["zoneId"] = cloud["id"]
487
+ payload.deep_merge!({"instance" => {"cloud" => cloud["name"] } })
488
+ end
489
+ if options[:cloud]
490
+ group_id = group ? group["id"] : ((payload["instance"] && payload["instance"]["site"].is_a?(Hash)) ? payload["instance"]["site"]["id"] : nil)
491
+ cloud = find_cloud_by_name_or_id_for_provisioning(group_id, options[:cloud])
492
+ if cloud.nil?
493
+ return 1, "cloud not found by #{options[:cloud]}"
494
+ end
495
+ payload["zoneId"] = cloud["id"]
496
+ payload.deep_merge!({"instance" => {"cloud" => cloud["name"] } })
497
+ end
498
+ if options[:instance_type_code]
499
+ # should just use find_instance_type_by_name_or_id
500
+ # note that the api actually will match name name or code
501
+ instance_type = (options[:instance_type_code].to_s =~ /\A\d{1,}\Z/) ? find_instance_type_by_id(options[:instance_type_code]) : find_instance_type_by_code(options[:instance_type_code])
502
+ if instance_type.nil?
503
+ return 1, "instance type not found by #{options[:cloud]}"
504
+ end
505
+ payload.deep_merge!({"instance" => {"type" => instance_type["code"] } })
506
+ payload.deep_merge!({"instance" => {"instanceType" => {"code" => instance_type["code"]} } })
507
+ end
508
+
438
509
  else
510
+ # use active group by default
511
+ options[:group] ||= @active_group_id
512
+ options[:select_datastore] = true
513
+ options[:name_required] = true
439
514
  # prompt for all the instance configuration options
440
515
  # this provisioning helper method handles all (most) of the parsing and prompting
441
516
  # and it relies on the method to exit non-zero on error, like a bad CLOUD or TYPE value
@@ -528,18 +603,18 @@ class Morpheus::Cli::Instances
528
603
  opts.on('--group GROUP', String, "Group Name or ID") do |val|
529
604
  options[:group] = val
530
605
  end
531
- opts.on('--metadata LIST', String, "Metadata tags in the format 'name:value, name:value'") do |val|
532
- options[:metadata] = val
533
- end
534
606
  opts.on('--labels LIST', String, "Labels (keywords) in the format 'foo, bar'") do |val|
535
- #params['tags'] = val
536
- params['tags'] = val.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
607
+ params['labels'] = val.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
608
+ end
609
+ opts.on('--tags LIST', String, "Tags in the format 'name:value, name:value'. This will add and remove tags.") do |val|
610
+ options[:tags] = val
537
611
  end
538
- opts.on('--tags LIST', String, "Tags") do |val|
539
- #params['tags'] = val
540
- params['tags'] = val.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
612
+ opts.on('--add-tags TAGS', String, "Add Tags in the format 'name:value, name:value'. This will only add/update tags.") do |val|
613
+ options[:add_tags] = val
614
+ end
615
+ opts.on('--remove-tags TAGS', String, "Remove Tags in the format 'name, name:value'. This removes tags, the :value component is optional and must match if passed.") do |val|
616
+ options[:remove_tags] = val
541
617
  end
542
- opts.add_hidden_option('--tags')
543
618
  opts.on('--power-schedule-type ID', String, "Power Schedule Type ID") do |val|
544
619
  params['powerScheduleType'] = val == "null" ? nil : val
545
620
  end
@@ -594,29 +669,17 @@ class Morpheus::Cli::Instances
594
669
  payload['instance']['site'] = {'id' => group['id']}
595
670
  end
596
671
  # metadata tags
597
- # if options[:options]['metadata'].is_a?(Array) && !options[:metadata]
598
- # options[:metadata] = options[:options]['metadata']
599
- # end
600
- if options[:metadata]
601
- metadata = []
602
- if options[:metadata] == "[]" || options[:metadata] == "null"
603
- payload['instance']['metadata'] = []
604
- elsif options[:metadata].is_a?(Array)
605
- payload['instance']['metadata'] = options[:metadata]
606
- else
607
- # parse string into format name:value, name:value
608
- # merge IDs from current metadata
609
- # todo: should allow quoted semicolons..
610
- metadata_list = options[:metadata].split(",").select {|it| !it.to_s.empty? }
611
- metadata_list = metadata_list.collect do |it|
612
- metadata_pair = it.split(":")
613
- row = {}
614
- row['name'] = metadata_pair[0].to_s.strip
615
- row['value'] = metadata_pair[1].to_s.strip
616
- row
617
- end
618
- payload['instance']['metadata'] = metadata_list
619
- end
672
+ if options[:tags]
673
+ # api version 4.2.5 and later supports tags, older versions expect metadata
674
+ # todo: use tags instead like everywhere else
675
+ # payload['instance']['tags'] = parse_metadata(options[:tags])
676
+ payload['instance']['metadata'] = parse_metadata(options[:tags])
677
+ end
678
+ if options[:add_tags]
679
+ payload['instance']['addTags'] = parse_metadata(options[:add_tags])
680
+ end
681
+ if options[:remove_tags]
682
+ payload['instance']['removeTags'] = parse_metadata(options[:remove_tags])
620
683
  end
621
684
  if payload['instance'].empty? && params.empty? && options[:owner].nil?
622
685
  raise_command_error "Specify at least one option to update.\n#{optparse}"
@@ -1217,7 +1280,18 @@ class Morpheus::Cli::Instances
1217
1280
  instance = json_response['instance']
1218
1281
  stats = instance['stats'] || json_response['stats'] || {}
1219
1282
  # load_balancers = json_response['loadBalancers'] || {}
1220
-
1283
+ # metadata tags used to be returned as metadata and are now returned as tags
1284
+ # the problem is tags is what we used to call Labels (keywords)
1285
+ # the api will change to tags and labels, so handle the old format as long as metadata is returned.
1286
+ labels = nil
1287
+ tags = nil
1288
+ if instance.key?('labels')
1289
+ labels = instance['labels']
1290
+ tags = instance['tags']
1291
+ else
1292
+ labels = instance['tags']
1293
+ tags = instance['metadata']
1294
+ end
1221
1295
  # containers are fetched via separate api call
1222
1296
  containers = nil
1223
1297
  if options[:include_containers]
@@ -1258,8 +1332,8 @@ class Morpheus::Cli::Instances
1258
1332
  # "Cost" => lambda {|it| it['hourlyCost'] ? format_money(it['hourlyCost'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
1259
1333
  # "Price" => lambda {|it| it['hourlyPrice'] ? format_money(it['hourlyPrice'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
1260
1334
  "Environment" => 'instanceContext',
1261
- "Labels" => lambda {|it| it['tags'] ? it['tags'].join(',') : '' },
1262
- "Metadata" => lambda {|it| it['metadata'] ? it['metadata'].collect {|m| "#{m['name']}: #{m['value']}" }.join(', ') : '' },
1335
+ "Labels" => lambda {|it| labels ? labels.join(',') : '' },
1336
+ "Tags" => lambda {|it| tags ? tags.collect {|m| "#{m['name']}: #{m['value']}" }.join(', ') : '' },
1263
1337
  "Owner" => lambda {|it|
1264
1338
  if it['owner']
1265
1339
  (it['owner']['username'] || it['owner']['id'])
@@ -1278,11 +1352,14 @@ class Morpheus::Cli::Instances
1278
1352
  "Connection" => lambda {|it| format_instance_connection_string(it) },
1279
1353
  "Status" => lambda {|it| format_instance_status(it) }
1280
1354
  }
1355
+ description_cols.delete("Labels") if labels.nil? || labels.empty?
1356
+ description_cols.delete("Tags") if tags.nil? || tags.empty?
1281
1357
  description_cols.delete("Power Schedule") if instance['powerSchedule'].nil?
1282
1358
  description_cols.delete("Expire Date") if instance['expireDate'].nil?
1283
1359
  description_cols.delete("Shutdown Date") if instance['shutdownDate'].nil?
1284
1360
  description_cols["Removal Date"] = lambda {|it| format_local_dt(it['removalDate'])} if instance['status'] == 'pendingRemoval'
1285
1361
  description_cols.delete("Last Deployment") if instance['lastDeploy'].nil?
1362
+ #description_cols.delete("Environment") if instance['instanceContext'].nil?
1286
1363
  print_description_list(description_cols, instance)
1287
1364
 
1288
1365
  if instance['statusMessage']
@@ -2596,6 +2673,48 @@ class Morpheus::Cli::Instances
2596
2673
  end
2597
2674
  end
2598
2675
 
2676
+ def snapshot(args)
2677
+ options = {}
2678
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
2679
+ opts.banner = subcommand_usage("[instance]")
2680
+ opts.on( '--name VALUE', String, "Snapshot Name. Default is server name + timestamp" ) do |val|
2681
+ options[:options]['name'] = val
2682
+ end
2683
+ opts.on( '--description VALUE', String, "Snapshot Description." ) do |val|
2684
+ options[:options]['description'] = val
2685
+ end
2686
+ build_standard_add_options(opts, options, [:auto_confirm])
2687
+ opts.footer = <<-EOT
2688
+ Create a snapshot for an instance.
2689
+ [instance] is required. This is the name or id of an instance
2690
+ EOT
2691
+ end
2692
+ optparse.parse!(args)
2693
+ verify_args!(args:args, optparse:optparse, count:1)
2694
+ connect(options)
2695
+ instance = find_instance_by_name_or_id(args[0])
2696
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to snapshot the instance '#{instance['name']}'?", options)
2697
+ exit 1
2698
+ end
2699
+ payload = {}
2700
+ if options[:payload]
2701
+ payload = options[:payload]
2702
+ payload.deep_merge!({'snapshot' => parse_passed_options(options)})
2703
+ else
2704
+ payload.deep_merge!({'snapshot' => parse_passed_options(options)})
2705
+ end
2706
+ @instances_interface.setopts(options)
2707
+ if options[:dry_run]
2708
+ print_dry_run @instances_interface.dry.snapshot(instance['id'], payload)
2709
+ return
2710
+ end
2711
+ json_response = @instances_interface.snapshot(instance['id'], payload)
2712
+ render_response(json_response, options, 'snapshots') do
2713
+ print_green_success "Snapshot initiated."
2714
+ end
2715
+ return 0, nil
2716
+ end
2717
+
2599
2718
  def remove(args)
2600
2719
  options = {}
2601
2720
  query_params = {}
@@ -2898,6 +3017,57 @@ class Morpheus::Cli::Instances
2898
3017
  end
2899
3018
  end
2900
3019
 
3020
+ def snapshots(args)
3021
+ options = {}
3022
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
3023
+ opts.banner = subcommand_usage("[instance]")
3024
+ # no pagination yet
3025
+ # build_standard_list_options(opts, options)
3026
+ build_standard_get_options(opts, options)
3027
+ opts.footer = <<-EOT
3028
+ List snapshots for an instance.
3029
+ [instance] is required. This is the name or id of an instance
3030
+ EOT
3031
+ end
3032
+ optparse.parse!(args)
3033
+ verify_args!(args:args, optparse:optparse, count:1)
3034
+ connect(options)
3035
+ begin
3036
+ instance = find_instance_by_name_or_id(args[0])
3037
+ params = {}
3038
+ @instances_interface.setopts(options)
3039
+ if options[:dry_run]
3040
+ print_dry_run @instances_interface.dry.snapshots(instance['id'], params)
3041
+ return
3042
+ end
3043
+ json_response = @instances_interface.snapshots(instance['id'], params)
3044
+ snapshots = json_response['snapshots']
3045
+ render_response(json_response, options, 'snapshots') do
3046
+ print_h1 "Snapshots: #{instance['name']} (#{instance['instanceType']['name']})", [], options
3047
+ if snapshots.empty?
3048
+ print cyan,"No snapshots found",reset,"\n"
3049
+ else
3050
+ snapshot_column_definitions = {
3051
+ "ID" => lambda {|it| it['id'] },
3052
+ "Name" => lambda {|it| it['name'] },
3053
+ "Description" => lambda {|it| it['description'] },
3054
+ # "Type" => lambda {|it| it['snapshotType'] },
3055
+ "Date Created" => lambda {|it| format_local_dt(it['snapshotCreated']) },
3056
+ "Status" => lambda {|it| format_snapshot_status(it) }
3057
+ }
3058
+ print cyan
3059
+ print as_pretty_table(snapshots, snapshot_column_definitions.upcase_keys!, options)
3060
+ print_results_pagination({size: snapshots.size, total: snapshots.size})
3061
+ end
3062
+ print reset, "\n"
3063
+ end
3064
+ return 0
3065
+ rescue RestClient::Exception => e
3066
+ print_rest_exception(e, options)
3067
+ exit 1
3068
+ end
3069
+ end
3070
+
2901
3071
  def import_snapshot(args)
2902
3072
  options = {}
2903
3073
  query_params = {}
@@ -3810,51 +3980,6 @@ private
3810
3980
  end
3811
3981
  end
3812
3982
 
3813
- def format_instance_status(instance, return_color=cyan)
3814
- out = ""
3815
- status_string = instance['status'].to_s
3816
- if status_string == 'running'
3817
- out << "#{green}#{status_string.upcase}#{return_color}"
3818
- elsif status_string == 'provisioning'
3819
- out << "#{cyan}#{status_string.upcase}#{return_color}"
3820
- elsif status_string == 'stopped' or status_string == 'failed'
3821
- out << "#{red}#{status_string.upcase}#{return_color}"
3822
- else
3823
- out << "#{yellow}#{status_string.upcase}#{return_color}"
3824
- end
3825
- out
3826
- end
3827
-
3828
- def format_instance_connection_string(instance)
3829
- if !instance['connectionInfo'].nil? && instance['connectionInfo'].empty? == false
3830
- connection_string = "#{instance['connectionInfo'][0]['ip']}:#{instance['connectionInfo'][0]['port']}"
3831
- end
3832
- end
3833
-
3834
- def format_container_status(container, return_color=cyan)
3835
- out = ""
3836
- status_string = container['status'].to_s
3837
- if status_string == 'running'
3838
- out << "#{green}#{status_string.upcase}#{return_color}"
3839
- elsif status_string == 'provisioning'
3840
- out << "#{cyan}#{status_string.upcase}#{return_color}"
3841
- elsif status_string == 'stopped' or status_string == 'failed'
3842
- out << "#{red}#{status_string.upcase}#{return_color}"
3843
- else
3844
- out << "#{yellow}#{status_string.upcase}#{return_color}"
3845
- end
3846
- out
3847
- end
3848
-
3849
- def format_container_connection_string(container)
3850
- if !container['ports'].nil? && container['ports'].empty? == false
3851
- connection_string = "#{container['ip']}:#{container['ports'][0]['external']}"
3852
- else
3853
- # eh? more logic needed here i think, see taglib morph:containerLocationMenu
3854
- connection_string = "#{container['ip']}"
3855
- end
3856
- end
3857
-
3858
3983
  def instance_scaling_option_types(instance=nil)
3859
3984
 
3860
3985
  # Group
@@ -3908,17 +4033,6 @@ private
3908
4033
  list
3909
4034
  end
3910
4035
 
3911
- def format_instance_container_display_name(instance, plural=false)
3912
- #<span class="info-label">${[null,'docker'].contains(instance.layout?.provisionType?.code) ? 'Containers' : 'Virtual Machines'}:</span> <span class="info-value">${instance.containers?.size()}</span>
3913
- v = plural ? "Containers" : "Container"
3914
- if instance && instance['layout'] && instance['layout'].key?("provisionTypeCode")
3915
- if [nil, 'docker'].include?(instance['layout']["provisionTypeCode"])
3916
- v = plural ? "Virtual Machines" : "Virtual Machine"
3917
- end
3918
- end
3919
- return v
3920
- end
3921
-
3922
4036
  def print_instance_threshold_description_list(instance_threshold)
3923
4037
  description_cols = {
3924
4038
  # "Instance" => lambda {|it| "#{instance['id']} - #{instance['name']}" },
@@ -4024,19 +4138,4 @@ private
4024
4138
  }
4025
4139
  end
4026
4140
 
4027
- def format_app_deploy_status(status, return_color=cyan)
4028
- out = ""
4029
- s = status.to_s.downcase
4030
- if s == 'deployed'
4031
- out << "#{green}#{s.upcase}#{return_color}"
4032
- elsif s == 'open' || s == 'archived' || s == 'committed'
4033
- out << "#{cyan}#{s.upcase}#{return_color}"
4034
- elsif s == 'failed'
4035
- out << "#{red}#{s.upcase}#{return_color}"
4036
- else
4037
- out << "#{yellow}#{s.upcase}#{return_color}"
4038
- end
4039
- out
4040
- end
4041
-
4042
4141
  end