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
@@ -52,7 +52,7 @@ class Morpheus::Cli::Groups
52
52
  end
53
53
  json_response = @groups_interface.list(params)
54
54
  exit_code, err = 0, nil
55
- render_response(json_response, options) do
55
+ render_response(json_response, options, "groups") do
56
56
  groups = json_response['groups']
57
57
  subtitles = []
58
58
  subtitles += parse_list_subtitles(options)
@@ -30,6 +30,7 @@ class Morpheus::Cli::HealthCommand
30
30
  opts.on('-a', '--all', "Display all details: CPU, Memory, Database, etc." ) do
31
31
  options[:details] = true
32
32
  options[:show_cpu] = true
33
+ options[:show_threads] = true
33
34
  options[:show_memory] = true
34
35
  options[:show_database] = true
35
36
  options[:show_elastic] = true
@@ -47,6 +48,9 @@ class Morpheus::Cli::HealthCommand
47
48
  opts.on('--cpu', "Display CPU details" ) do
48
49
  options[:show_cpu] = true
49
50
  end
51
+ opts.on('--threads', "Display Thread details" ) do
52
+ options[:show_threads] = true
53
+ end
50
54
  opts.on('--memory', "Display Memory details" ) do
51
55
  options[:show_memory] = true
52
56
  end
@@ -184,6 +188,59 @@ class Morpheus::Cli::HealthCommand
184
188
  end
185
189
  end
186
190
 
191
+ # Threads ()
192
+ if options[:show_threads]
193
+ print_h2 "Threads", options
194
+ if health['threads'].nil?
195
+ print yellow,"No thread information returned.",reset,"\n\n"
196
+ else
197
+ print cyan
198
+
199
+ thread_summary_columns = {
200
+ "Thread Count" => lambda {|it| it['totalThreads'].size rescue '' },
201
+ "Busy Threads" => lambda {|it| it['busyThreads'].size rescue '' },
202
+ "Running Threads" => lambda {|it| it['runningThreads'].size rescue '' },
203
+ "Blocked Threads" => lambda {|it| it['blockedThreads'].size rescue '' },
204
+ }
205
+ print_description_list(thread_summary_columns, health['threads'], options)
206
+
207
+
208
+ thread_columns = [
209
+ {"Name".upcase => lambda {|it| it['name']} },
210
+ {"Status".upcase => lambda {|it|
211
+ # hrmm
212
+ status_string = (it['status'] || it['state']).to_s.downcase
213
+ status_color = cyan
214
+ # if status_string.include?('waiting')
215
+ # status_color = yellow
216
+ # end
217
+ "#{status_color}#{status_string.upcase}#{cyan}"
218
+ } },
219
+ # {"CPU Time" => lambda {|it| it['cpuTime'].to_s } },
220
+ # {"CPU Time" => lambda {|it| format_human_duration(it['cpuTime'].to_f / 1000) rescue '' } },
221
+ {"CPU Percent" => lambda {|it| it['cpuPercent'].to_i.to_s + '%' } }
222
+ ]
223
+
224
+ if health['threads']['busyThreads'] && health['threads']['busyThreads'].size > 0
225
+ print_h2 "Busy Threads"
226
+ print cyan
227
+ print as_pretty_table(health['threads']['busyThreads'], thread_columns, options)
228
+ end
229
+
230
+ if health['threads']['runningThreads'] && health['threads']['runningThreads'].size > 0
231
+ print_h2 "Running Threads"
232
+ print cyan
233
+ print as_pretty_table(health['threads']['runningThreads'], thread_columns, options)
234
+ end
235
+
236
+ if health['threads']['blockedThreads'] && health['threads']['blockedThreads'].size > 0
237
+ print_h2 "Blocked Threads"
238
+ print cyan
239
+ print as_pretty_table(health['threads']['blockedThreads'], thread_columns, options)
240
+ end
241
+ end
242
+ end
243
+
187
244
  # Memory
188
245
  if options[:show_memory]
189
246
  if health['memory'].nil?
@@ -475,8 +532,8 @@ class Morpheus::Cli::HealthCommand
475
532
  opts.footer = "List health logs. These are the logs of the morpheus appliance itself."
476
533
  end
477
534
  optparse.parse!(args)
478
- if args.count != 0
479
- raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args}\n#{optparse}"
535
+ if args.count > 0
536
+ options[:phrase] = args.join(" ")
480
537
  end
481
538
  connect(options)
482
539
  begin
@@ -16,10 +16,11 @@ class Morpheus::Cli::Hosts
16
16
  include Morpheus::Cli::LogsHelper
17
17
  set_command_name :hosts
18
18
  set_command_description "View and manage hosts (servers)."
19
- register_subcommands :list, :count, :get, :view, :stats, :add, :update, :remove, :logs, :start, :stop, :resize, :run_workflow, :make_managed, :upgrade_agent
20
- register_subcommands :'types' => :list_types
21
- register_subcommands :exec => :execution_request
22
- register_subcommands :wiki, :update_wiki
19
+ register_subcommands :list, :count, :get, :view, :stats, :add, :update, :remove, :logs, :start, :stop, :resize,
20
+ :run_workflow, :make_managed, :upgrade_agent, :snapshots, :software,
21
+ {:'types' => :list_types},
22
+ {:exec => :execution_request},
23
+ :wiki, :update_wiki
23
24
  alias_subcommand :details, :get
24
25
  set_default_subcommand :list
25
26
 
@@ -53,9 +54,6 @@ class Morpheus::Cli::Hosts
53
54
  params = {}
54
55
  optparse = Morpheus::Cli::OptionParser.new do |opts|
55
56
  opts.banner = subcommand_usage()
56
- opts.on( '-a', '--account ACCOUNT', "Account Name or ID" ) do |val|
57
- options[:account] = val
58
- end
59
57
  opts.on( '-g', '--group GROUP', "Group Name or ID" ) do |val|
60
58
  options[:group] = val
61
59
  end
@@ -81,6 +79,17 @@ class Morpheus::Cli::Hosts
81
79
  # params[:clusterId] = val
82
80
  options[:cluster] = val
83
81
  end
82
+ opts.on( '--plan NAME', String, "Filter by Plan name(s)" ) do |val|
83
+ # commas used in names a lot so use --plan one --plan two
84
+ params['plan'] ||= []
85
+ params['plan'] << val
86
+ end
87
+ opts.on( '--plan-id ID', String, "Filter by Plan id(s)" ) do |val|
88
+ params['planId'] = parse_id_list(val)
89
+ end
90
+ opts.on( '--plan-code CODE', String, "Filter by Plan code(s)" ) do |val|
91
+ params['planCode'] = parse_id_list(val)
92
+ end
84
93
  opts.on( '', '--vm', "Show only virtual machines" ) do |val|
85
94
  params[:vm] = true
86
95
  end
@@ -105,8 +114,16 @@ class Morpheus::Cli::Hosts
105
114
  opts.on( '--created-by USER', "Created By User Username or ID" ) do |val|
106
115
  options[:created_by] = val
107
116
  end
108
- opts.on('--details', "Display more details: memory and storage usage used / max values." ) do
109
- options[:details] = true
117
+ opts.on( '--tenant TENANT', "Tenant Name or ID" ) do |val|
118
+ options[:account] = val
119
+ end
120
+ opts.on('--tags Name=Value',String, "Filter by tags.") do |val|
121
+ val.split(",").each do |value_pair|
122
+ k,v = value_pair.strip.split("=")
123
+ options[:tags] ||= {}
124
+ options[:tags][k] ||= []
125
+ options[:tags][k] << (v || '')
126
+ end
110
127
  end
111
128
  opts.on('--tag-compliant', "Displays only servers that are valid according to applied tag policies. Does not show servers that do not have tag policies." ) do
112
129
  params[:tagCompliant] = true
@@ -114,11 +131,21 @@ class Morpheus::Cli::Hosts
114
131
  opts.on('--non-tag-compliant', "Displays only servers with tag compliance warnings." ) do
115
132
  params[:tagCompliant] = false
116
133
  end
117
- build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
134
+ opts.on('--stats', "Display values for memory and storage usage used / max values." ) do
135
+ options[:stats] = true
136
+ end
137
+ opts.on('-a', '--details', "Display all details: hostname, private ip, plan, stats, etc." ) do
138
+ options[:details] = true
139
+ end
140
+ build_standard_list_options(opts, options)
118
141
  opts.footer = "List hosts."
119
142
  end
120
143
  optparse.parse!(args)
121
144
  connect(options)
145
+ # verify_args!(args:args, optparse:optparse, count:0)
146
+ if args.count > 0
147
+ options[:phrase] = args.join(" ")
148
+ end
122
149
  begin
123
150
  params.merge!(parse_list_options(options))
124
151
  account = nil
@@ -159,6 +186,11 @@ class Morpheus::Cli::Hosts
159
186
  params['clusterId'] = cluster['id']
160
187
  end
161
188
  end
189
+ if options[:tags] && !options[:tags].empty?
190
+ options[:tags].each do |k,v|
191
+ params['tags.' + k] = v
192
+ end
193
+ end
162
194
 
163
195
  @servers_interface.setopts(options)
164
196
  if options[:dry_run]
@@ -190,6 +222,9 @@ class Morpheus::Cli::Hosts
190
222
  multi_tenant = json_response['multiTenant'] == true
191
223
  title = "Morpheus Hosts"
192
224
  subtitles = []
225
+ if account
226
+ subtitles << "Tenant: #{account['name']}".strip
227
+ end
193
228
  if group
194
229
  subtitles << "Group: #{group['name']}".strip
195
230
  end
@@ -230,7 +265,7 @@ class Morpheus::Cli::Hosts
230
265
  cpu_usage_str = !stats ? "" : generate_usage_bar((stats['usedCpu'] || stats['cpuUsage']).to_f, 100, {max_bars: 10})
231
266
  memory_usage_str = !stats ? "" : generate_usage_bar(stats['usedMemory'], stats['maxMemory'], {max_bars: 10})
232
267
  storage_usage_str = !stats ? "" : generate_usage_bar(stats['usedStorage'], stats['maxStorage'], {max_bars: 10})
233
- if options[:details]
268
+ if options[:details] || options[:stats]
234
269
  if stats['maxMemory'] && stats['maxMemory'].to_i != 0
235
270
  memory_usage_str = memory_usage_str + cyan + format_bytes_short(stats['usedMemory']).strip.rjust(8, ' ') + " / " + format_bytes_short(stats['maxMemory']).strip
236
271
  end
@@ -240,31 +275,76 @@ class Morpheus::Cli::Hosts
240
275
  end
241
276
  row = {
242
277
  id: server['id'],
243
- tenant: server['account'] ? server['account']['name'] : server['accountId'],
244
278
  name: server['name'],
279
+ external_name: server['externalName'],
280
+ hostname: server['hostname'],
245
281
  platform: server['serverOs'] ? server['serverOs']['name'].upcase : 'N/A',
246
- cloud: server['zone'] ? server['zone']['name'] : '',
247
282
  type: server['computeServerType'] ? server['computeServerType']['name'] : 'unmanaged',
283
+ tenant: server['account'] ? server['account']['name'] : server['accountId'],
284
+ owner: server['owner'] ? server['owner']['username'] : server['owner'],
285
+ cloud: server['zone'] ? server['zone']['name'] : '',
286
+ plan: server['plan'] ? server['plan']['name'] : '',
287
+ ip: server['externalIp'],
288
+ internal_ip: server['internalIp'],
248
289
  nodes: server['containers'] ? server['containers'].size : '',
249
- status: format_server_status(server, cyan),
290
+ # status: format_server_status(server, cyan),
291
+ status: (options[:details]||options[:all_fields]) ? format_server_status(server, cyan) : format_server_status_friendly(server, cyan),
250
292
  power: format_server_power_state(server, cyan),
251
293
  cpu: cpu_usage_str + cyan,
252
294
  memory: memory_usage_str + cyan,
253
- storage: storage_usage_str + cyan
295
+ storage: storage_usage_str + cyan,
296
+ created: format_local_dt(server['dateCreated']),
297
+ updated: format_local_dt(server['lastUpdated']),
254
298
  }
255
299
  row
256
300
  }
257
- columns = [:id, :name, :type, :cloud, :nodes, :status, :power]
258
- if multi_tenant
259
- columns.insert(4, :tenant)
301
+ # columns = [:id, :name, :type, :cloud, :ip, :internal_ip, :nodes, :status, :power]
302
+ columns = {
303
+ "ID" => :id,
304
+ "Name" => :name,
305
+ "External Name" => :external_name,
306
+ "Hostname" => :hostname,
307
+ "Type" => :type,
308
+ "Owner" => :owner,
309
+ "Tenant" => :tenant,
310
+ "Cloud" => :cloud,
311
+ "Plan" => :plan,
312
+ "IP" => :ip,
313
+ "Private IP" => :internal_ip,
314
+ "Nodes" => :nodes,
315
+ "Status" => :status,
316
+ "Power" => :power,
317
+ "CPU" => :cpu,
318
+ "Memory" => :memory,
319
+ "Storage" => :storage,
320
+ "Created" => :created,
321
+ "Updated" => :updated,
322
+ }
323
+ if options[:details] != true
324
+ columns.delete("External Name")
325
+ columns.delete("Hostname")
326
+ columns.delete("Plan")
327
+ columns.delete("Private IP")
328
+ columns.delete("Owner")
329
+ columns.delete("Tenant")
330
+ columns.delete("Power")
331
+ columns.delete("Created")
332
+ columns.delete("Updated")
333
+ end
334
+ # hide External Name if there are none
335
+ if !servers.find {|it| it['externalName'] && it['externalName'] != it['name']}
336
+ columns.delete("External Name")
260
337
  end
261
- columns += [:cpu, :memory, :storage]
262
- # custom pretty table columns ...
263
- if options[:include_fields]
264
- columns = options[:include_fields]
338
+ if !multi_tenant
339
+ columns.delete("Tenant")
265
340
  end
341
+ # columns += [:cpu, :memory, :storage]
342
+ # # custom pretty table columns ...
343
+ # if options[:include_fields]
344
+ # columns = options[:include_fields]
345
+ # end
266
346
  print cyan
267
- print as_pretty_table(rows, columns, options)
347
+ print as_pretty_table(rows, columns.upcase_keys!, options)
268
348
  print reset
269
349
  print_results_pagination(json_response)
270
350
  end
@@ -281,7 +361,7 @@ class Morpheus::Cli::Hosts
281
361
  options = {}
282
362
  optparse = Morpheus::Cli::OptionParser.new do |opts|
283
363
  opts.banner = subcommand_usage("[options]")
284
- opts.on( '-a', '--account ACCOUNT', "Account Name or ID" ) do |val|
364
+ opts.on( '--tenant TENANT', "Tenant Name or ID" ) do |val|
285
365
  options[:account] = val
286
366
  end
287
367
  opts.on( '-g', '--group GROUP', "Group Name or ID" ) do |val|
@@ -392,6 +472,9 @@ class Morpheus::Cli::Hosts
392
472
  options = {}
393
473
  optparse = Morpheus::Cli::OptionParser.new do |opts|
394
474
  opts.banner = subcommand_usage("[name]")
475
+ opts.on( nil, '--costs', "Display Cost and Price" ) do
476
+ options[:include_costs] = true
477
+ end
395
478
  opts.on('--refresh [SECONDS]', String, "Refresh until status is provisioned,failed. Default interval is #{default_refresh_interval} seconds.") do |val|
396
479
  options[:refresh_until_status] ||= "provisioned,failed"
397
480
  if !val.to_s.empty?
@@ -453,22 +536,37 @@ class Morpheus::Cli::Hosts
453
536
  title = "Host Details"
454
537
  print_h1 title, [], options
455
538
  print cyan
456
- print_description_list({
539
+ server_columns = {
457
540
  "ID" => 'id',
458
541
  "Name" => 'name',
542
+ "Hostname" => 'hostname',
459
543
  "Description" => 'description',
460
- "Account" => lambda {|it| it['account'] ? it['account']['name'] : '' },
544
+ "Owner" => lambda {|it| it['owner'] ? it['owner']['username'] : '' },
545
+ "Tenant" => lambda {|it| it['account'] ? it['account']['name'] : '' },
461
546
  #"Group" => lambda {|it| it['group'] ? it['group']['name'] : '' },
462
547
  "Cloud" => lambda {|it| it['zone'] ? it['zone']['name'] : '' },
548
+ "IP" => lambda {|it| it['externalIp'] },
549
+ "Private IP" => lambda {|it| it['internalIp'] },
463
550
  "Type" => lambda {|it| it['computeServerType'] ? it['computeServerType']['name'] : 'unmanaged' },
464
551
  "Platform" => lambda {|it| it['serverOs'] ? it['serverOs']['name'].upcase : 'N/A' },
465
552
  "Plan" => lambda {|it| it['plan'] ? it['plan']['name'] : '' },
553
+ "Cost" => lambda {|it| it['hourlyCost'] ? format_money(it['hourlyCost'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
554
+ "Price" => lambda {|it| it['hourlyPrice'] ? format_money(it['hourlyPrice'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
466
555
  "Agent" => lambda {|it| it['agentInstalled'] ? "#{server['agentVersion'] || ''} updated at #{format_local_dt(server['lastAgentUpdate'])}" : '(not installed)' },
467
- "Status" => lambda {|it| format_server_status(it) },
468
556
  "Nodes" => lambda {|it| it['containers'] ? it['containers'].size : 0 },
469
- "Power" => lambda {|it| format_server_power_state(it) },
470
- }, server)
471
-
557
+ # "Status" => lambda {|it| format_server_status(it) },
558
+ # "Power" => lambda {|it| format_server_power_state(it) },
559
+ "Status" => lambda {|it| format_server_status_friendly(it) }, # combo
560
+ }
561
+ server_columns.delete("Hostname") if server['hostname'].to_s.empty? || server['hostname'] == server['name']
562
+ server_columns.delete("IP") if server['externalIp'].to_s.empty?
563
+ server_columns.delete("Private IP") if server['internalIp'].to_s.empty?
564
+ # server_columns.delete("Tenant") if multi_tenant != true
565
+ server_columns.delete("Cost") if server['hourlyCost'].to_f == 0
566
+ server_columns.delete("Price") if server['hourlyPrice'].to_f == 0 || server['hourlyPrice'] == server['hourlyCost']
567
+
568
+ print_description_list(server_columns, server)
569
+
472
570
  if server['statusMessage']
473
571
  print_h2 "Status Message", options
474
572
  if server['status'] == 'failed'
@@ -485,6 +583,16 @@ class Morpheus::Cli::Hosts
485
583
 
486
584
  print_h2 "Host Usage", options
487
585
  print_stats_usage(stats)
586
+
587
+ if options[:include_costs]
588
+ print_h2 "Host Cost"
589
+ cost_columns = {
590
+ "Cost" => lambda {|it| it['hourlyCost'] ? format_money(it['hourlyCost'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
591
+ "Price" => lambda {|it| it['hourlyPrice'] ? format_money(it['hourlyPrice'], (it['currency'] || 'USD'), {sigdig:15}).to_s + ' per hour' : '' },
592
+ }
593
+ print_description_list(cost_columns, server)
594
+ end
595
+
488
596
  print reset, "\n"
489
597
 
490
598
 
@@ -1781,6 +1889,102 @@ class Morpheus::Cli::Hosts
1781
1889
  end
1782
1890
  end
1783
1891
 
1892
+ def snapshots(args)
1893
+ options = {}
1894
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
1895
+ opts.banner = subcommand_usage("[host]")
1896
+ # no pagination yet
1897
+ # build_standard_list_options(opts, options)
1898
+ build_standard_get_options(opts, options)
1899
+ end
1900
+ optparse.parse!(args)
1901
+ verify_args!(args:args, optparse:optparse, count:1)
1902
+ connect(options)
1903
+ begin
1904
+ server = find_host_by_name_or_id(args[0])
1905
+ return 1 if server.nil?
1906
+ params = {}
1907
+ @servers_interface.setopts(options)
1908
+ if options[:dry_run]
1909
+ print_dry_run @servers_interface.dry.snapshots(server['id'], params)
1910
+ return
1911
+ end
1912
+ json_response = @servers_interface.snapshots(server['id'], params)
1913
+ snapshots = json_response['snapshots']
1914
+ render_response(json_response, options, 'snapshots') do
1915
+ print_h1 "Snapshots: #{server['name']}", [], options
1916
+ if snapshots.empty?
1917
+ print cyan,"No snapshots found",reset,"\n"
1918
+ else
1919
+ snapshot_column_definitions = {
1920
+ "ID" => lambda {|it| it['id'] },
1921
+ "Name" => lambda {|it| it['name'] },
1922
+ "Description" => lambda {|it| it['snapshotType'] ? (it['snapshotType']['name'] || it['snapshotType']['code']) : '' },
1923
+ "Date Created" => lambda {|it| format_local_dt(it['snapshotCreated']) },
1924
+ "Status" => lambda {|it| format_snapshot_status(it) }
1925
+ }
1926
+ print cyan
1927
+ print as_pretty_table(snapshots, snapshot_column_definitions.upcase_keys!, options)
1928
+ print_results_pagination({size: snapshots.size, total: snapshots.size})
1929
+ end
1930
+ print reset, "\n"
1931
+ end
1932
+ return 0
1933
+ rescue RestClient::Exception => e
1934
+ print_rest_exception(e, options)
1935
+ exit 1
1936
+ end
1937
+ end
1938
+
1939
+ def software(args)
1940
+ options = {}
1941
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
1942
+ opts.banner = subcommand_usage("[host]")
1943
+ build_standard_list_options(opts, options)
1944
+ end
1945
+ optparse.parse!(args)
1946
+ verify_args!(args:args, optparse:optparse, count:1)
1947
+ connect(options)
1948
+ begin
1949
+ server = find_host_by_name_or_id(args[0])
1950
+ return 1 if server.nil?
1951
+ params = {}
1952
+ params.merge!(parse_list_options(options))
1953
+ @servers_interface.setopts(options)
1954
+ if options[:dry_run]
1955
+ print_dry_run @servers_interface.dry.software(server['id'], params)
1956
+ return
1957
+ end
1958
+ json_response = @servers_interface.software(server['id'], params)
1959
+ software = json_response['software']
1960
+ render_response(json_response, options, 'software') do
1961
+ print_h1 "Software: #{server['name']}", [], options
1962
+ if software.empty?
1963
+ print cyan,"No software found",reset,"\n"
1964
+ else
1965
+ software_column_definitions = {
1966
+ # "ID" => lambda {|it| it['id'] },
1967
+ "Name" => lambda {|it| it['name'] },
1968
+ "Version" => lambda {|it| it['packageVersion'] },
1969
+ "Publisher" => lambda {|it| it['packagePublisher'] },
1970
+ # "Release" => lambda {|it| it['packageRelease'] },
1971
+ # "Type" => lambda {|it| it['packageType'] },
1972
+ # "Architecture" => lambda {|it| it['architecture'] },
1973
+ # "Install Date" => lambda {|it| format_local_dt(it['installDate']) },
1974
+ }
1975
+ print cyan
1976
+ print as_pretty_table(software, software_column_definitions.upcase_keys!, options)
1977
+ print_results_pagination({size: software.size, total: software.size})
1978
+ end
1979
+ print reset, "\n"
1980
+ end
1981
+ return 0
1982
+ rescue RestClient::Exception => e
1983
+ print_rest_exception(e, options)
1984
+ exit 1
1985
+ end
1986
+ end
1987
+
1784
1988
  private
1785
1989
 
1786
1990
  def find_host_by_id(id)
@@ -1897,16 +2101,43 @@ class Morpheus::Cli::Hosts
1897
2101
 
1898
2102
  def format_server_status(server, return_color=cyan)
1899
2103
  out = ""
1900
- status_string = server['status']
1901
- # todo: colorize, upcase?
1902
- out << status_string.to_s
2104
+ status_string = server['status'].to_s.downcase
2105
+ if status_string == 'provisioned'
2106
+ out = "#{cyan}#{status_string.upcase}#{return_color}"
2107
+ elsif status_string == 'provisioning'
2108
+ out = "#{cyan}#{status_string.upcase}#{cyan}"
2109
+ elsif status_string == 'failed' or status_string == 'error'
2110
+ out = "#{red}#{status_string.upcase}#{return_color}"
2111
+ else
2112
+ out = "#{yellow}#{status_string.upcase}#{return_color}"
2113
+ end
2114
+ out
2115
+ end
2116
+
2117
+ def format_server_status_friendly(server, return_color=cyan)
2118
+ out = ""
2119
+ status_string = server['status'].to_s.downcase
2120
+ if status_string == 'provisioned'
2121
+ # out = format_server_power_state(server, return_color)
2122
+ # make it looks like format_instance_status
2123
+ if server['powerState'] == 'on'
2124
+ out << "#{green}RUNNING#{return_color}"
2125
+ elsif server['powerState'] == 'off'
2126
+ out << "#{red}STOPPED#{return_color}"
2127
+ else
2128
+ out << "#{white}#{server['powerState'].to_s.upcase}#{return_color}"
2129
+ end
2130
+ else
2131
+ out = format_server_status(server, return_color)
2132
+ end
1903
2133
  out
1904
2134
  end
2135
+
1905
2136
 
1906
2137
  def make_managed_option_types(connected=true)
1907
2138
  [
1908
2139
  #{'fieldName' => 'account', 'fieldLabel' => 'Account', 'type' => 'select', 'optionSource' => 'accounts', 'required' => true},
1909
- {'fieldName' => 'sshUsername', 'fieldLabel' => 'SSH Username', 'type' => 'text', 'required' => true},
2140
+ {'fieldName' => 'sshUsername', 'fieldLabel' => 'SSH Username', 'type' => 'text'},
1910
2141
  {'fieldName' => 'sshPassword', 'fieldLabel' => 'SSH Password', 'type' => 'password', 'required' => false},
1911
2142
  {'fieldName' => 'serverOs', 'fieldLabel' => 'OS Type', 'type' => 'select', 'optionSource' => 'osTypes', 'required' => false},
1912
2143
  ]