morpheus-cli 5.0.0 → 5.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Dockerfile +1 -1
- data/lib/morpheus/api/api_client.rb +16 -0
- data/lib/morpheus/api/billing_interface.rb +1 -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/invoices_interface.rb +12 -3
- 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 +6 -2
- data/lib/morpheus/cli/apps.rb +3 -23
- data/lib/morpheus/cli/budgets_command.rb +389 -319
- data/lib/morpheus/cli/{catalog_command.rb → catalog_item_types_command.rb} +182 -67
- data/lib/morpheus/cli/cli_command.rb +51 -10
- data/lib/morpheus/cli/commands/standard/curl_command.rb +26 -13
- data/lib/morpheus/cli/commands/standard/history_command.rb +9 -3
- data/lib/morpheus/cli/commands/standard/man_command.rb +74 -40
- data/lib/morpheus/cli/containers_command.rb +0 -24
- data/lib/morpheus/cli/cypher_command.rb +6 -2
- data/lib/morpheus/cli/dashboard_command.rb +260 -20
- 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 +271 -39
- data/lib/morpheus/cli/instances.rb +228 -129
- data/lib/morpheus/cli/invoices_command.rb +100 -20
- data/lib/morpheus/cli/jobs_command.rb +94 -92
- data/lib/morpheus/cli/library_option_lists_command.rb +1 -1
- data/lib/morpheus/cli/library_option_types_command.rb +10 -5
- data/lib/morpheus/cli/logs_command.rb +9 -6
- data/lib/morpheus/cli/mixins/accounts_helper.rb +5 -1
- data/lib/morpheus/cli/mixins/deployments_helper.rb +31 -2
- data/lib/morpheus/cli/mixins/print_helper.rb +13 -27
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +108 -5
- data/lib/morpheus/cli/option_types.rb +271 -22
- data/lib/morpheus/cli/remote.rb +35 -10
- data/lib/morpheus/cli/reports_command.rb +99 -30
- data/lib/morpheus/cli/roles.rb +193 -155
- data/lib/morpheus/cli/search_command.rb +182 -0
- data/lib/morpheus/cli/service_catalog_command.rb +1474 -0
- data/lib/morpheus/cli/setup.rb +1 -1
- data/lib/morpheus/cli/shell.rb +33 -11
- data/lib/morpheus/cli/tasks.rb +29 -32
- data/lib/morpheus/cli/usage_command.rb +64 -11
- 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 +33 -40
- data/lib/morpheus/formatters.rb +75 -18
- data/lib/morpheus/terminal.rb +6 -2
- metadata +10 -4
- 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:
|
218
|
-
cloud:
|
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
|
-
|
236
|
-
|
237
|
-
|
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('--
|
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('--
|
354
|
-
|
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.
|
358
|
-
|
359
|
-
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(',')
|
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
|
-
|
536
|
-
|
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
|
539
|
-
|
540
|
-
|
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
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
metadata = []
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
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|
|
1262
|
-
"
|
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
|