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.
- 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
|