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.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/lib/morpheus/api/api_client.rb +30 -0
- data/lib/morpheus/api/billing_interface.rb +34 -0
- data/lib/morpheus/api/catalog_item_types_interface.rb +9 -0
- data/lib/morpheus/api/deploy_interface.rb +1 -1
- data/lib/morpheus/api/deployments_interface.rb +20 -1
- data/lib/morpheus/api/forgot_password_interface.rb +17 -0
- data/lib/morpheus/api/instances_interface.rb +16 -2
- data/lib/morpheus/api/rest_interface.rb +0 -6
- data/lib/morpheus/api/roles_interface.rb +14 -0
- data/lib/morpheus/api/search_interface.rb +13 -0
- data/lib/morpheus/api/servers_interface.rb +14 -0
- data/lib/morpheus/api/service_catalog_interface.rb +89 -0
- data/lib/morpheus/api/usage_interface.rb +18 -0
- data/lib/morpheus/cli.rb +7 -3
- data/lib/morpheus/cli/apps.rb +6 -27
- data/lib/morpheus/cli/backup_jobs_command.rb +3 -0
- data/lib/morpheus/cli/backups_command.rb +3 -0
- data/lib/morpheus/cli/catalog_item_types_command.rb +622 -0
- data/lib/morpheus/cli/cli_command.rb +70 -21
- data/lib/morpheus/cli/commands/standard/curl_command.rb +26 -12
- data/lib/morpheus/cli/commands/standard/history_command.rb +3 -1
- data/lib/morpheus/cli/commands/standard/man_command.rb +74 -40
- data/lib/morpheus/cli/commands/standard/source_command.rb +1 -1
- data/lib/morpheus/cli/commands/standard/update_command.rb +76 -0
- data/lib/morpheus/cli/containers_command.rb +14 -24
- data/lib/morpheus/cli/cypher_command.rb +6 -2
- data/lib/morpheus/cli/deploy.rb +199 -90
- data/lib/morpheus/cli/deployments.rb +341 -28
- data/lib/morpheus/cli/deploys.rb +206 -41
- data/lib/morpheus/cli/error_handler.rb +7 -0
- data/lib/morpheus/cli/forgot_password.rb +133 -0
- data/lib/morpheus/cli/groups.rb +1 -1
- data/lib/morpheus/cli/health_command.rb +59 -2
- data/lib/morpheus/cli/hosts.rb +265 -34
- data/lib/morpheus/cli/instances.rb +186 -100
- data/lib/morpheus/cli/invoices_command.rb +33 -16
- data/lib/morpheus/cli/jobs_command.rb +28 -6
- data/lib/morpheus/cli/library_option_lists_command.rb +15 -7
- data/lib/morpheus/cli/library_option_types_command.rb +5 -2
- data/lib/morpheus/cli/logs_command.rb +9 -6
- data/lib/morpheus/cli/mixins/accounts_helper.rb +12 -7
- data/lib/morpheus/cli/mixins/backups_helper.rb +2 -4
- data/lib/morpheus/cli/mixins/deployments_helper.rb +31 -3
- data/lib/morpheus/cli/mixins/option_source_helper.rb +1 -1
- data/lib/morpheus/cli/mixins/print_helper.rb +46 -21
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +100 -4
- data/lib/morpheus/cli/network_pools_command.rb +14 -6
- data/lib/morpheus/cli/option_types.rb +271 -22
- data/lib/morpheus/cli/ping.rb +0 -1
- data/lib/morpheus/cli/remote.rb +35 -12
- data/lib/morpheus/cli/reports_command.rb +99 -30
- data/lib/morpheus/cli/roles.rb +453 -113
- data/lib/morpheus/cli/search_command.rb +182 -0
- data/lib/morpheus/cli/service_catalog_command.rb +1474 -0
- data/lib/morpheus/cli/service_plans_command.rb +2 -2
- data/lib/morpheus/cli/setup.rb +1 -1
- data/lib/morpheus/cli/shell.rb +33 -11
- data/lib/morpheus/cli/storage_providers_command.rb +40 -56
- data/lib/morpheus/cli/tasks.rb +29 -32
- data/lib/morpheus/cli/usage_command.rb +203 -0
- data/lib/morpheus/cli/user_settings_command.rb +1 -0
- data/lib/morpheus/cli/users.rb +12 -1
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/virtual_images.rb +429 -254
- data/lib/morpheus/cli/whoami.rb +6 -6
- data/lib/morpheus/cli/workflows.rb +34 -41
- data/lib/morpheus/formatters.rb +75 -7
- data/lib/morpheus/terminal.rb +6 -2
- 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,
|
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:
|
215
|
-
cloud:
|
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
|
-
|
233
|
-
|
234
|
-
|
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('--
|
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('--
|
351
|
-
|
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.
|
355
|
-
|
356
|
-
options[:
|
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('--
|
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('--
|
532
|
-
|
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.
|
536
|
-
|
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
|
-
"
|
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
|
-
|
111
|
-
|
112
|
-
|
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
|
173
|
-
|
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
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
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
|
812
|
-
|
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
|