morpheus-cli 4.2.16 → 4.2.17
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/README.md +8 -6
- data/lib/morpheus/api/api_client.rb +32 -14
- data/lib/morpheus/api/auth_interface.rb +4 -2
- data/lib/morpheus/api/backup_jobs_interface.rb +9 -0
- data/lib/morpheus/api/backups_interface.rb +16 -0
- data/lib/morpheus/api/deploy_interface.rb +25 -56
- data/lib/morpheus/api/deployments_interface.rb +43 -54
- data/lib/morpheus/api/doc_interface.rb +57 -0
- data/lib/morpheus/api/instances_interface.rb +5 -0
- data/lib/morpheus/api/rest_interface.rb +40 -0
- data/lib/morpheus/api/user_sources_interface.rb +0 -15
- data/lib/morpheus/api/users_interface.rb +2 -3
- data/lib/morpheus/benchmarking.rb +2 -2
- data/lib/morpheus/cli.rb +3 -1
- data/lib/morpheus/cli/access_token_command.rb +27 -10
- data/lib/morpheus/cli/apps.rb +21 -15
- data/lib/morpheus/cli/backup_jobs_command.rb +276 -0
- data/lib/morpheus/cli/backups_command.rb +271 -0
- data/lib/morpheus/cli/boot_scripts_command.rb +1 -1
- data/lib/morpheus/cli/cli_command.rb +92 -41
- data/lib/morpheus/cli/clusters.rb +0 -18
- data/lib/morpheus/cli/commands/standard/benchmark_command.rb +7 -7
- data/lib/morpheus/cli/commands/standard/man_command.rb +1 -1
- data/lib/morpheus/cli/credentials.rb +13 -9
- data/lib/morpheus/cli/deploy.rb +374 -0
- data/lib/morpheus/cli/deployments.rb +521 -197
- data/lib/morpheus/cli/deploys.rb +271 -126
- data/lib/morpheus/cli/doc.rb +182 -0
- data/lib/morpheus/cli/error_handler.rb +23 -8
- data/lib/morpheus/cli/errors.rb +3 -2
- data/lib/morpheus/cli/image_builder_command.rb +2 -2
- data/lib/morpheus/cli/instances.rb +136 -17
- data/lib/morpheus/cli/invoices_command.rb +51 -38
- data/lib/morpheus/cli/library_layouts_command.rb +1 -1
- data/lib/morpheus/cli/login.rb +9 -3
- data/lib/morpheus/cli/mixins/accounts_helper.rb +158 -100
- data/lib/morpheus/cli/mixins/backups_helper.rb +115 -0
- data/lib/morpheus/cli/mixins/deployments_helper.rb +135 -0
- data/lib/morpheus/cli/mixins/option_source_helper.rb +1 -1
- data/lib/morpheus/cli/mixins/print_helper.rb +110 -74
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +2 -2
- data/lib/morpheus/cli/mixins/whoami_helper.rb +19 -6
- data/lib/morpheus/cli/network_routers_command.rb +1 -1
- data/lib/morpheus/cli/option_parser.rb +48 -5
- data/lib/morpheus/cli/option_types.rb +1 -1
- data/lib/morpheus/cli/remote.rb +3 -2
- data/lib/morpheus/cli/roles.rb +49 -92
- data/lib/morpheus/cli/security_groups.rb +7 -1
- data/lib/morpheus/cli/service_plans_command.rb +10 -10
- data/lib/morpheus/cli/setup.rb +1 -1
- data/lib/morpheus/cli/shell.rb +7 -6
- data/lib/morpheus/cli/subnets_command.rb +1 -1
- data/lib/morpheus/cli/tenants_command.rb +133 -163
- data/lib/morpheus/cli/user_groups_command.rb +20 -65
- data/lib/morpheus/cli/user_settings_command.rb +115 -13
- data/lib/morpheus/cli/user_sources_command.rb +57 -24
- data/lib/morpheus/cli/users.rb +210 -186
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/whitelabel_settings_command.rb +29 -5
- data/lib/morpheus/cli/whoami.rb +113 -6
- data/lib/morpheus/cli/workflows.rb +1 -1
- data/lib/morpheus/ext/hash.rb +21 -0
- data/lib/morpheus/terminal.rb +1 -0
- metadata +12 -3
- data/lib/morpheus/cli/auth_command.rb +0 -105
@@ -31,18 +31,31 @@ class Morpheus::Cli::ErrorHandler
|
|
31
31
|
# raise err
|
32
32
|
# @stderr.puts "#{red}#{err.message}#{reset}"
|
33
33
|
puts_angry_error err.message
|
34
|
-
@stderr.puts
|
34
|
+
@stderr.puts err.optparse.banner if err.optparse && err.optparse.banner
|
35
|
+
@stderr.puts "Try --help for more usage information"
|
35
36
|
do_print_stacktrace = false
|
36
37
|
# exit_code = 127
|
37
|
-
|
38
|
+
when Morpheus::Cli::CommandArgumentsError
|
39
|
+
puts_angry_error err.message
|
40
|
+
@stderr.puts err.optparse.banner if err.optparse && err.optparse.banner
|
41
|
+
@stderr.puts "Try --help for more usage information"
|
42
|
+
do_print_stacktrace = false
|
43
|
+
if err.exit_code
|
44
|
+
exit_code = err.exit_code
|
45
|
+
end
|
46
|
+
|
38
47
|
when Morpheus::Cli::CommandError
|
39
|
-
#
|
40
|
-
#
|
48
|
+
# this should probably always print the whole thing as red, but just does the first line for now.
|
49
|
+
# until verify_args! replaces raise_command_error where the full parser help is in the error message..
|
41
50
|
message_lines = err.message.split(/\r?\n/)
|
42
51
|
first_line = message_lines.shift
|
43
52
|
puts_angry_error first_line
|
44
|
-
|
45
|
-
|
53
|
+
if !message_lines.empty?
|
54
|
+
@stderr.puts message_lines.join("\n") unless message_lines.empty?
|
55
|
+
else
|
56
|
+
@stderr.puts err.optparse.banner if err.optparse && err.optparse.banner && message_lines.empty?
|
57
|
+
@stderr.puts "Try --help for more usage information"
|
58
|
+
end
|
46
59
|
do_print_stacktrace = false
|
47
60
|
if err.exit_code
|
48
61
|
exit_code = err.exit_code
|
@@ -82,7 +95,7 @@ class Morpheus::Cli::ErrorHandler
|
|
82
95
|
@stderr.puts err.to_s
|
83
96
|
end
|
84
97
|
else
|
85
|
-
@stderr.puts "Use --debug for more information."
|
98
|
+
@stderr.puts "Use -V or --debug for more verbose debugging information."
|
86
99
|
end
|
87
100
|
end
|
88
101
|
|
@@ -127,6 +140,8 @@ class Morpheus::Cli::ErrorHandler
|
|
127
140
|
begin
|
128
141
|
print_rest_errors(JSON.parse(err.response.to_s), options)
|
129
142
|
rescue TypeError, JSON::ParserError => ex
|
143
|
+
# not json, just 404
|
144
|
+
@stderr.print red, "Error Communicating with the remote appliance. #{e}", reset, "\n"
|
130
145
|
end
|
131
146
|
else
|
132
147
|
@stderr.print red, "Error Communicating with the remote appliance. #{e}", reset, "\n"
|
@@ -145,7 +160,7 @@ class Morpheus::Cli::ErrorHandler
|
|
145
160
|
@stderr.print reset
|
146
161
|
end
|
147
162
|
else
|
148
|
-
@stderr.puts "Use --debug for more information."
|
163
|
+
@stderr.puts "Use -V or --debug for more verbose debugging information."
|
149
164
|
end
|
150
165
|
end
|
151
166
|
else
|
data/lib/morpheus/cli/errors.rb
CHANGED
@@ -3,7 +3,7 @@ module Morpheus::Cli
|
|
3
3
|
# A standard error to raise in your CliCommand classes.
|
4
4
|
class CommandError < StandardError
|
5
5
|
|
6
|
-
attr_reader :args, :exit_code
|
6
|
+
attr_reader :args, :optparse, :exit_code
|
7
7
|
|
8
8
|
def initialize(msg, args=[], optparse=nil, exit_code=nil)
|
9
9
|
@args = args
|
@@ -11,6 +11,7 @@ module Morpheus::Cli
|
|
11
11
|
@exit_code = exit_code # || 1
|
12
12
|
super(msg)
|
13
13
|
end
|
14
|
+
|
14
15
|
end
|
15
16
|
|
16
17
|
# An error indicating the command was not recoginzed
|
@@ -24,7 +25,7 @@ module Morpheus::Cli
|
|
24
25
|
|
25
26
|
# An error for wrong number of arguments
|
26
27
|
# could use ::OptionParser::MissingArgument, ::OptionParser::NeedlessArgument
|
27
|
-
# maybe return an error code
|
28
|
+
# maybe return an error code other than 1?
|
28
29
|
class CommandArgumentsError < CommandError
|
29
30
|
|
30
31
|
def initialize(msg, args=[], optparse=nil, exit_code=nil)
|
@@ -518,7 +518,7 @@ class Morpheus::Cli::ImageBuilderCommand
|
|
518
518
|
opts.on( '-K', '--keep-virtual-images', "Preserve associated virtual images" ) do
|
519
519
|
query_params['keepVirtualImages'] = 'on'
|
520
520
|
end
|
521
|
-
build_common_options(opts, options, [:
|
521
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
|
522
522
|
end
|
523
523
|
optparse.parse!(args)
|
524
524
|
|
@@ -565,7 +565,7 @@ class Morpheus::Cli::ImageBuilderCommand
|
|
565
565
|
query_params = {}
|
566
566
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
567
567
|
opts.banner = subcommand_usage("[image-build]")
|
568
|
-
build_common_options(opts, options, [:
|
568
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
|
569
569
|
end
|
570
570
|
optparse.parse!(args)
|
571
571
|
|
@@ -9,14 +9,22 @@ class Morpheus::Cli::Instances
|
|
9
9
|
include Morpheus::Cli::AccountsHelper # needed? replace with OptionSourceHelper
|
10
10
|
include Morpheus::Cli::OptionSourceHelper
|
11
11
|
include Morpheus::Cli::ProvisioningHelper
|
12
|
+
include Morpheus::Cli::DeploymentsHelper
|
12
13
|
include Morpheus::Cli::ProcessesHelper
|
13
14
|
include Morpheus::Cli::LogsHelper
|
14
15
|
|
15
16
|
set_command_name :instances
|
16
17
|
set_command_description "View and manage instances."
|
17
|
-
register_subcommands :list, :count, :get, :view, :add, :update, :remove, :cancel_removal, :logs,
|
18
|
-
|
19
|
-
|
18
|
+
register_subcommands :list, :count, :get, :view, :add, :update, :remove, :cancel_removal, :logs,
|
19
|
+
:history, {:'history-details' => :history_details}, {:'history-event' => :history_event_details},
|
20
|
+
:stats, :stop, :start, :restart, :actions, :action, :suspend, :eject, :stop_service, :start_service, :restart_service,
|
21
|
+
:backup, :backups, :resize, :clone, :envs, :setenv, :delenv,
|
22
|
+
:security_groups, :apply_security_groups, :run_workflow, :import_snapshot,
|
23
|
+
:console, :status_check, {:containers => :list_containers},
|
24
|
+
:scaling, {:'scaling-update' => :scaling_update},
|
25
|
+
:wiki, :update_wiki,
|
26
|
+
{:exec => :execution_request},
|
27
|
+
:deploys
|
20
28
|
#register_subcommands :firewall_disable, :firewall_enable
|
21
29
|
# register_subcommands {:'lb-update' => :load_balancer_update}
|
22
30
|
alias_subcommand :details, :get
|
@@ -43,6 +51,8 @@ class Morpheus::Cli::Instances
|
|
43
51
|
@options_interface = @api_client.options
|
44
52
|
@active_group_id = Morpheus::Cli::Groups.active_groups[@appliance_name]
|
45
53
|
@execution_request_interface = @api_client.execution_request
|
54
|
+
@deploy_interface = @api_client.deploy
|
55
|
+
@deployments_interface = @api_client.deployments
|
46
56
|
end
|
47
57
|
|
48
58
|
def handle(args)
|
@@ -546,18 +556,12 @@ class Morpheus::Cli::Instances
|
|
546
556
|
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
|
547
557
|
end
|
548
558
|
optparse.parse!(args)
|
549
|
-
|
550
|
-
puts optparse
|
551
|
-
exit 1
|
552
|
-
end
|
559
|
+
verify_args!(args:args, optparse:optparse, count:1)
|
553
560
|
connect(options)
|
554
561
|
|
555
562
|
begin
|
556
563
|
instance = find_instance_by_name_or_id(args[0])
|
557
564
|
return 1 if instance.nil?
|
558
|
-
new_group = nil
|
559
|
-
|
560
|
-
|
561
565
|
if options[:payload]
|
562
566
|
payload = options[:payload]
|
563
567
|
end
|
@@ -579,10 +583,40 @@ class Morpheus::Cli::Instances
|
|
579
583
|
params['ownerId'] = owner_id
|
580
584
|
#payload['createdById'] = options[:owner].to_i # pre 4.2.1 api
|
581
585
|
end
|
582
|
-
if
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
+
if options[:group]
|
587
|
+
group = find_group_by_name_or_id_for_provisioning(options[:group])
|
588
|
+
if group.nil?
|
589
|
+
return 1, "group not found"
|
590
|
+
end
|
591
|
+
payload['instance']['site'] = {'id' => group['id']}
|
592
|
+
end
|
593
|
+
# metadata tags
|
594
|
+
# if options[:options]['metadata'].is_a?(Array) && !options[:metadata]
|
595
|
+
# options[:metadata] = options[:options]['metadata']
|
596
|
+
# end
|
597
|
+
if options[:metadata]
|
598
|
+
metadata = []
|
599
|
+
if options[:metadata] == "[]" || options[:metadata] == "null"
|
600
|
+
payload['instance']['metadata'] = []
|
601
|
+
elsif options[:metadata].is_a?(Array)
|
602
|
+
payload['instance']['metadata'] = options[:metadata]
|
603
|
+
else
|
604
|
+
# parse string into format name:value, name:value
|
605
|
+
# merge IDs from current metadata
|
606
|
+
# todo: should allow quoted semicolons..
|
607
|
+
metadata_list = options[:metadata].split(",").select {|it| !it.to_s.empty? }
|
608
|
+
metadata_list = metadata_list.collect do |it|
|
609
|
+
metadata_pair = it.split(":")
|
610
|
+
row = {}
|
611
|
+
row['name'] = metadata_pair[0].to_s.strip
|
612
|
+
row['value'] = metadata_pair[1].to_s.strip
|
613
|
+
row
|
614
|
+
end
|
615
|
+
payload['instance']['metadata'] = metadata_list
|
616
|
+
end
|
617
|
+
end
|
618
|
+
if payload['instance'].empty? && params.empty? && options[:owner].nil?
|
619
|
+
raise_command_error "Specify at least one option to update.\n#{optparse}"
|
586
620
|
end
|
587
621
|
if !params.empty?
|
588
622
|
payload['instance'].deep_merge!(params)
|
@@ -1218,7 +1252,6 @@ class Morpheus::Cli::Instances
|
|
1218
1252
|
"Environment" => 'instanceContext',
|
1219
1253
|
"Labels" => lambda {|it| it['tags'] ? it['tags'].join(',') : '' },
|
1220
1254
|
"Metadata" => lambda {|it| it['metadata'] ? it['metadata'].collect {|m| "#{m['name']}: #{m['value']}" }.join(', ') : '' },
|
1221
|
-
"Power Schedule" => lambda {|it| (it['powerSchedule'] && it['powerSchedule']['type']) ? it['powerSchedule']['type']['name'] : '' },
|
1222
1255
|
"Owner" => lambda {|it|
|
1223
1256
|
if it['owner']
|
1224
1257
|
(it['owner']['username'] || it['owner']['id'])
|
@@ -1228,13 +1261,20 @@ class Morpheus::Cli::Instances
|
|
1228
1261
|
},
|
1229
1262
|
#"Tenant" => lambda {|it| it['tenant'] ? it['tenant']['name'] : '' },
|
1230
1263
|
"Date Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
1264
|
+
# "Last Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
|
1265
|
+
"Power Schedule" => lambda {|it| (it['powerSchedule'] && it['powerSchedule']['type']) ? it['powerSchedule']['type']['name'] : '' },
|
1266
|
+
"Last Deployment" => lambda {|it| (it['lastDeploy'] ? "#{it['lastDeploy']['deployment']['name']} #{it['lastDeploy']['deploymentVersion']['userVersion']} at #{format_local_dt it['lastDeploy']['deployDate']}" : nil) rescue "" },
|
1267
|
+
"Expire Date" => lambda {|it| it['expireDate'] ? format_local_dt(it['expireDate']) : '' },
|
1268
|
+
"Shutdown Date" => lambda {|it| it['shutdownDate'] ? format_local_dt(it['shutdownDate']) : '' },
|
1231
1269
|
"Nodes" => lambda {|it| it['containers'] ? it['containers'].count : 0 },
|
1232
1270
|
"Connection" => lambda {|it| format_instance_connection_string(it) },
|
1233
1271
|
"Status" => lambda {|it| format_instance_status(it) }
|
1234
1272
|
}
|
1235
|
-
|
1273
|
+
description_cols.delete("Power Schedule") if instance['powerSchedule'].nil?
|
1274
|
+
description_cols.delete("Expire Date") if instance['expireDate'].nil?
|
1275
|
+
description_cols.delete("Shutdown Date") if instance['shutdownDate'].nil?
|
1236
1276
|
description_cols["Removal Date"] = lambda {|it| format_local_dt(it['removalDate'])} if instance['status'] == 'pendingRemoval'
|
1237
|
-
|
1277
|
+
description_cols.delete("Last Deployment") if instance['lastDeploy'].nil?
|
1238
1278
|
print_description_list(description_cols, instance)
|
1239
1279
|
|
1240
1280
|
if instance['statusMessage']
|
@@ -3606,6 +3646,60 @@ class Morpheus::Cli::Instances
|
|
3606
3646
|
end
|
3607
3647
|
end
|
3608
3648
|
|
3649
|
+
def deploys(args)
|
3650
|
+
params = {}
|
3651
|
+
options = {}
|
3652
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
3653
|
+
opts.banner = subcommand_usage("[instance] [search]")
|
3654
|
+
build_standard_list_options(opts, options)
|
3655
|
+
opts.footer = <<-EOT
|
3656
|
+
List deployments for an instance.
|
3657
|
+
[instance] is required. This is the name or id of an instance
|
3658
|
+
[search] is optional. Filters on deployment version identifier
|
3659
|
+
EOT
|
3660
|
+
end
|
3661
|
+
optparse.parse!(args)
|
3662
|
+
verify_args!(args:args, optparse:optparse, min:1)
|
3663
|
+
connect(options)
|
3664
|
+
if args.count > 1
|
3665
|
+
options[:phrase] = args.join(" ")
|
3666
|
+
end
|
3667
|
+
params.merge!(parse_list_options(options))
|
3668
|
+
instance = find_instance_by_name_or_id(args[0])
|
3669
|
+
return 1 if instance.nil?
|
3670
|
+
# @deploy_interface.setopts(options)
|
3671
|
+
# if options[:dry_run]
|
3672
|
+
# print_dry_run @deploy_interface.dry.list(instance['id'], params)
|
3673
|
+
# return
|
3674
|
+
# end
|
3675
|
+
# json_response = @deploy_interface.list(instance['id'], params)
|
3676
|
+
|
3677
|
+
@instances_interface.setopts(options)
|
3678
|
+
if options[:dry_run]
|
3679
|
+
print_dry_run @instances_interface.dry.deploys(instance['id'], params)
|
3680
|
+
return
|
3681
|
+
end
|
3682
|
+
json_response = @instances_interface.deploys(instance['id'], params)
|
3683
|
+
|
3684
|
+
app_deploys = json_response['appDeploys']
|
3685
|
+
render_response(json_response, options, 'appDeploys') do
|
3686
|
+
print_h1 "Instance Deploys", ["#{instance['name']}"] + parse_list_subtitles(options), options
|
3687
|
+
if app_deploys.empty?
|
3688
|
+
print cyan,"No deployments found.",reset,"\n"
|
3689
|
+
else
|
3690
|
+
print as_pretty_table(app_deploys, app_deploy_column_definitions.upcase_keys!, options)
|
3691
|
+
if json_response['meta']
|
3692
|
+
print_results_pagination(json_response)
|
3693
|
+
else
|
3694
|
+
print_results_pagination({size:app_deploys.size,total:app_deploys.size.to_i})
|
3695
|
+
end
|
3696
|
+
|
3697
|
+
end
|
3698
|
+
print reset,"\n"
|
3699
|
+
end
|
3700
|
+
return 0
|
3701
|
+
end
|
3702
|
+
|
3609
3703
|
private
|
3610
3704
|
|
3611
3705
|
def find_zone_by_name_or_id(group_id, val)
|
@@ -3902,4 +3996,29 @@ private
|
|
3902
3996
|
]
|
3903
3997
|
end
|
3904
3998
|
|
3999
|
+
def app_deploy_column_definitions
|
4000
|
+
{
|
4001
|
+
"ID" => 'id',
|
4002
|
+
"Deployment" => lambda {|it| it['deployment']['name'] rescue '' },
|
4003
|
+
"Version" => lambda {|it| (it['deploymentVersion']['userVersion'] || it['deploymentVersion']['version']) rescue '' },
|
4004
|
+
"Deploy Date" => lambda {|it| format_local_dt(it['deployDate']) },
|
4005
|
+
"Status" => lambda {|it| format_app_deploy_status(it['status']) },
|
4006
|
+
}
|
4007
|
+
end
|
4008
|
+
|
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
|
+
|
3905
4024
|
end
|
@@ -25,6 +25,7 @@ class Morpheus::Cli::InvoicesCommand
|
|
25
25
|
options = {}
|
26
26
|
params = {}
|
27
27
|
ref_ids = []
|
28
|
+
query_tags = {}
|
28
29
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
29
30
|
opts.banner = subcommand_usage()
|
30
31
|
opts.on('-a', '--all', "Display all details, costs and prices." ) do
|
@@ -34,13 +35,13 @@ class Morpheus::Cli::InvoicesCommand
|
|
34
35
|
options[:show_prices] = true
|
35
36
|
# options[:show_raw_data] = true
|
36
37
|
end
|
37
|
-
opts.on('--estimates', '--estimates', "Display all estimated costs, from usage info: Compute, Storage, Network,
|
38
|
+
opts.on('--estimates', '--estimates', "Display all estimated costs, from usage info: Compute, Storage, Network, Extra" ) do
|
38
39
|
options[:show_estimates] = true
|
39
40
|
end
|
40
|
-
# opts.on('--costs', '--costs', "Display all costs: Compute, Storage, Network,
|
41
|
+
# opts.on('--costs', '--costs', "Display all costs: Compute, Storage, Network, Extra" ) do
|
41
42
|
# options[:show_costs] = true
|
42
43
|
# end
|
43
|
-
opts.on('--prices', '--prices', "Display prices: Total, Compute, Storage, Network,
|
44
|
+
opts.on('--prices', '--prices', "Display prices: Total, Compute, Storage, Network, Extra" ) do
|
44
45
|
options[:show_prices] = true
|
45
46
|
end
|
46
47
|
opts.on('--type TYPE', String, "Filter by Ref Type eg. ComputeSite (Group), ComputeZone (Cloud), ComputeServer (Host), Instance, Container, User") do |val|
|
@@ -105,6 +106,11 @@ class Morpheus::Cli::InvoicesCommand
|
|
105
106
|
opts.on('--tenant ID', String, "View invoices for a tenant. Default is your own account.") do |val|
|
106
107
|
params['accountId'] = val
|
107
108
|
end
|
109
|
+
opts.on('--tags Name=Value',String, "Filter by tags.") do |val|
|
110
|
+
k,v = val.split("=")
|
111
|
+
query_tags[k] ||= []
|
112
|
+
query_tags[k] << v
|
113
|
+
end
|
108
114
|
opts.on('--raw-data', '--raw-data', "Display Raw Data, the cost data from the cloud provider's API.") do |val|
|
109
115
|
options[:show_raw_data] = true
|
110
116
|
end
|
@@ -163,6 +169,11 @@ class Morpheus::Cli::InvoicesCommand
|
|
163
169
|
end
|
164
170
|
params['rawData'] = true if options[:show_raw_data]
|
165
171
|
params['refId'] = ref_ids unless ref_ids.empty?
|
172
|
+
if query_tags && !query_tags.empty?
|
173
|
+
query_tags.each do |k,v|
|
174
|
+
params['tags.' + k] = v
|
175
|
+
end
|
176
|
+
end
|
166
177
|
@invoices_interface.setopts(options)
|
167
178
|
if options[:dry_run]
|
168
179
|
print_dry_run @invoices_interface.dry.list(params)
|
@@ -216,7 +227,7 @@ class Morpheus::Cli::InvoicesCommand
|
|
216
227
|
# {"MEMORY" => lambda {|it| format_money(it['memoryCost']) } },
|
217
228
|
{"STORAGE" => lambda {|it| format_money(it['storageCost'], 'usd', {sigdig:options[:sigdig]}) } },
|
218
229
|
{"NETWORK" => lambda {|it| format_money(it['networkCost'], 'usd', {sigdig:options[:sigdig]}) } },
|
219
|
-
{"
|
230
|
+
{"EXTRA" => lambda {|it| format_money(it['extraCost'], 'usd', {sigdig:options[:sigdig]}) } },
|
220
231
|
{"MTD" => lambda {|it| format_money(it['runningCost'], 'usd', {sigdig:options[:sigdig]}) } },
|
221
232
|
{"TOTAL" => lambda {|it|
|
222
233
|
format_money(it['totalCost'], 'usd', {sigdig:options[:sigdig]}) + ((it['totalCost'].to_f > 0 && it['totalCost'] != it['runningCost']) ? " (Projected)" : "")
|
@@ -229,7 +240,7 @@ class Morpheus::Cli::InvoicesCommand
|
|
229
240
|
# {"MEMORY PRICE" => lambda {|it| format_money(it['memoryPrice'], 'usd', {sigdig:options[:sigdig]}) } },
|
230
241
|
{"STORAGE PRICE" => lambda {|it| format_money(it['storagePrice'], 'usd', {sigdig:options[:sigdig]}) } },
|
231
242
|
{"NETWORK PRICE" => lambda {|it| format_money(it['networkPrice'], 'usd', {sigdig:options[:sigdig]}) } },
|
232
|
-
{"
|
243
|
+
{"EXTRA PRICE" => lambda {|it| format_money(it['extraPrice'], 'usd', {sigdig:options[:sigdig]}) } },
|
233
244
|
{"MTD PRICE" => lambda {|it| format_money(it['runningPrice'], 'usd', {sigdig:options[:sigdig]}) } },
|
234
245
|
{"TOTAL PRICE" => lambda {|it|
|
235
246
|
format_money(it['totalPrice'], 'usd', {sigdig:options[:sigdig]}) + ((it['totalCost'].to_f > 0 && it['totalCost'] != it['runningCost']) ? " (Projected)" : "")
|
@@ -242,7 +253,7 @@ class Morpheus::Cli::InvoicesCommand
|
|
242
253
|
# {"MEMORY EST." => lambda {|it| format_money(it['estimatedMemoryCost'], 'usd', {sigdig:options[:sigdig]}) } },
|
243
254
|
{"STORAGE EST." => lambda {|it| format_money(it['estimatedStorageCost'], 'usd', {sigdig:options[:sigdig]}) } },
|
244
255
|
{"NETWORK EST." => lambda {|it| format_money(it['estimatedNetworkCost'], 'usd', {sigdig:options[:sigdig]}) } },
|
245
|
-
{"
|
256
|
+
{"EXTRA EST." => lambda {|it| format_money(it['estimatedExtraCost'], 'usd', {sigdig:options[:sigdig]}) } },
|
246
257
|
{"MTD EST." => lambda {|it| format_money(it['estimatedRunningCost'], 'usd', {sigdig:options[:sigdig]}) } },
|
247
258
|
{"TOTAL EST." => lambda {|it|
|
248
259
|
format_money(it['estimatedTotalCost'], 'usd', {sigdig:options[:sigdig]}) + ((it['estimatedTotalCost'].to_f > 0 && it['estimatedTotalCost'] != it['estimatedRunningCost']) ? " (Projected)" : "")
|
@@ -253,6 +264,7 @@ class Morpheus::Cli::InvoicesCommand
|
|
253
264
|
{"ESTIMATE" => lambda {|it| format_boolean(it['estimate']) } },
|
254
265
|
{"ACTIVE" => lambda {|it| format_boolean(it['active']) } },
|
255
266
|
{"ITEMS" => lambda {|it| it['lineItems'].size rescue '' } },
|
267
|
+
{"TAGS" => lambda {|it| it['metadata'] ? it['metadata'].collect {|m| "#{m['name']}: #{m['value']}" }.join(', ') : '' } },
|
256
268
|
]
|
257
269
|
if show_projects
|
258
270
|
columns += [
|
@@ -273,34 +285,17 @@ class Morpheus::Cli::InvoicesCommand
|
|
273
285
|
print_results_pagination(json_response, {:label => "invoice", :n_label => "invoices"})
|
274
286
|
end
|
275
287
|
|
276
|
-
if options[:show_invoice_totals]
|
277
|
-
invoice_totals = json_response['invoiceTotals']
|
278
|
-
print_h2 "Line Item Totals" unless options[:totals_only]
|
279
|
-
invoice_totals_columns = [
|
280
|
-
{"Invoices" => lambda {|it| format_number(json_response['meta']['total']) rescue '' } },
|
281
|
-
{"Compute" => lambda {|it| format_money(it['actualComputeCost'], 'usd', {sigdig:options[:sigdig]}) } },
|
282
|
-
{"Storage" => lambda {|it| format_money(it['actualStorageCost'], 'usd', {sigdig:options[:sigdig]}) } },
|
283
|
-
{"Network" => lambda {|it| format_money(it['actualNetworkCost'], 'usd', {sigdig:options[:sigdig]}) } },
|
284
|
-
{"Extra" => lambda {|it| format_money(it['actualExtraCost'], 'usd', {sigdig:options[:sigdig]}) } },
|
285
|
-
] + (options[:show_prices] ? [
|
286
|
-
{"Compute Price" => lambda {|it| format_money(it['actualComputePrice'], 'usd', {sigdig:options[:sigdig]}) } },
|
287
|
-
{"Storage Price" => lambda {|it| format_money(it['actualStoragePrice'], 'usd', {sigdig:options[:sigdig]}) } },
|
288
|
-
{"Network Price" => lambda {|it| format_money(it['actualNetworkPrice'], 'usd', {sigdig:options[:sigdig]}) } },
|
289
|
-
{"Extra Price" => lambda {|it| format_money(it['actualExtraPrice'], 'usd', {sigdig:options[:sigdig]}) } },
|
290
|
-
] : [])
|
291
|
-
print_description_list(invoice_totals_columns, line_item_totals)
|
292
|
-
end
|
293
288
|
if options[:show_invoice_totals]
|
294
289
|
invoice_totals = json_response['invoiceTotals']
|
295
290
|
print_h2 "Invoice Totals (#{format_number(json_response['meta']['total']) rescue ''})"
|
296
291
|
|
297
292
|
if invoice_totals
|
298
293
|
cost_rows = [
|
299
|
-
{label: 'Cost', compute: invoice_totals['actualComputeCost'], memory: invoice_totals['actualMemoryCost'], storage: invoice_totals['actualStorageCost'], network: invoice_totals['actualNetworkCost'], license: invoice_totals['actualLicenseCost'], extra: invoice_totals['actualExtraCost'], running: invoice_totals['actualRunningCost'], total: invoice_totals['actualTotalCost']},
|
294
|
+
{label: 'Cost'.upcase, compute: invoice_totals['actualComputeCost'], memory: invoice_totals['actualMemoryCost'], storage: invoice_totals['actualStorageCost'], network: invoice_totals['actualNetworkCost'], license: invoice_totals['actualLicenseCost'], extra: invoice_totals['actualExtraCost'], running: invoice_totals['actualRunningCost'], total: invoice_totals['actualTotalCost']},
|
300
295
|
]
|
301
296
|
if options[:show_prices]
|
302
297
|
cost_rows += [
|
303
|
-
{label: 'Price', compute: invoice_totals['actualComputePrice'], memory: invoice_totals['actualMemoryPrice'], storage: invoice_totals['actualStoragePrice'], network: invoice_totals['actualNetworkPrice'], license: invoice_totals['actualLicensePrice'], extra: invoice_totals['actualExtraPrice'], running: invoice_totals['actualRunningPrice'], total: invoice_totals['actualTotalPrice']},
|
298
|
+
{label: 'Price'.upcase, compute: invoice_totals['actualComputePrice'], memory: invoice_totals['actualMemoryPrice'], storage: invoice_totals['actualStoragePrice'], network: invoice_totals['actualNetworkPrice'], license: invoice_totals['actualLicensePrice'], extra: invoice_totals['actualExtraPrice'], running: invoice_totals['actualRunningPrice'], total: invoice_totals['actualTotalPrice']},
|
304
299
|
]
|
305
300
|
end
|
306
301
|
if options[:show_estimates]
|
@@ -316,8 +311,8 @@ class Morpheus::Cli::InvoicesCommand
|
|
316
311
|
"Storage".upcase => lambda {|it| format_money(it[:storage], 'usd', {sigdig:options[:sigdig]}) },
|
317
312
|
"Network".upcase => lambda {|it| format_money(it[:network], 'usd', {sigdig:options[:sigdig]}) },
|
318
313
|
"License".upcase => lambda {|it| format_money(it[:license], 'usd', {sigdig:options[:sigdig]}) },
|
319
|
-
"
|
320
|
-
"MTD" => lambda {|it| format_money(it[:running], 'usd', {sigdig:options[:sigdig]}) },
|
314
|
+
"Extra".upcase => lambda {|it| format_money(it[:extra], 'usd', {sigdig:options[:sigdig]}) },
|
315
|
+
"MTD".upcase => lambda {|it| format_money(it[:running], 'usd', {sigdig:options[:sigdig]}) },
|
321
316
|
"Total".upcase => lambda {|it|
|
322
317
|
format_money(it[:total], 'usd', {sigdig:options[:sigdig]}) + ((it[:total].to_f > 0 && it[:total] != it[:running]) ? " (Projected)" : "")
|
323
318
|
},
|
@@ -330,7 +325,7 @@ class Morpheus::Cli::InvoicesCommand
|
|
330
325
|
cost_columns.delete("License".upcase)
|
331
326
|
end
|
332
327
|
if cost_rows.sum { |it| it[:extra].to_f } == 0
|
333
|
-
cost_columns.delete("
|
328
|
+
cost_columns.delete("Extra".upcase)
|
334
329
|
end
|
335
330
|
print as_pretty_table(cost_rows, cost_columns, options)
|
336
331
|
else
|
@@ -338,8 +333,8 @@ class Morpheus::Cli::InvoicesCommand
|
|
338
333
|
print yellow, "No invoice totals data", reset, "\n"
|
339
334
|
end
|
340
335
|
end
|
336
|
+
print reset,"\n"
|
341
337
|
end
|
342
|
-
print reset,"\n"
|
343
338
|
return 0, nil
|
344
339
|
end
|
345
340
|
end
|
@@ -355,10 +350,10 @@ class Morpheus::Cli::InvoicesCommand
|
|
355
350
|
# options[:show_raw_data] = true
|
356
351
|
options[:max_line_items] = 10000
|
357
352
|
end
|
358
|
-
opts.on('--prices', '--prices', "Display prices: Total, Compute, Storage, Network,
|
353
|
+
opts.on('--prices', '--prices', "Display prices: Total, Compute, Storage, Network, Extra" ) do
|
359
354
|
options[:show_prices] = true
|
360
355
|
end
|
361
|
-
opts.on('--estimates', '--estimates', "Display all estimated costs, from usage info: Compute, Storage, Network,
|
356
|
+
opts.on('--estimates', '--estimates', "Display all estimated costs, from usage info: Compute, Storage, Network, Extra" ) do
|
362
357
|
options[:show_estimates] = true
|
363
358
|
end
|
364
359
|
opts.on('--raw-data', '--raw-data', "Display Raw Data, the cost data from the cloud provider's API.") do |val|
|
@@ -415,6 +410,7 @@ EOT
|
|
415
410
|
"Type" => lambda {|it| format_invoice_ref_type(it) },
|
416
411
|
"Ref ID" => lambda {|it| it['refId'] },
|
417
412
|
"Ref Name" => lambda {|it| it['refName'] },
|
413
|
+
"Cloud" => lambda {|it| it['cloud'] ? it['cloud']['name'] : '' },
|
418
414
|
"Plan" => lambda {|it| it['plan'] ? it['plan']['name'] : '' },
|
419
415
|
"Power State" => lambda {|it| format_server_power_state(it) },
|
420
416
|
"Tenant" => lambda {|it| it['account'] ? it['account']['name'] : '' },
|
@@ -428,6 +424,7 @@ EOT
|
|
428
424
|
"Ref Start" => lambda {|it| format_dt(it['refStart']) },
|
429
425
|
"Ref End" => lambda {|it| format_dt(it['refEnd']) },
|
430
426
|
"Items" => lambda {|it| it['lineItems'].size rescue '' },
|
427
|
+
"Tags" => lambda {|it| it['metadata'] ? it['metadata'].collect {|m| "#{m['name']}: #{m['value']}" }.join(', ') : '' },
|
431
428
|
"Project ID" => lambda {|it| it['project'] ? it['project']['id'] : '' },
|
432
429
|
"Project Name" => lambda {|it| it['project'] ? it['project']['name'] : '' },
|
433
430
|
"Project Tags" => lambda {|it| it['project'] ? format_metadata(it['project']['tags']) : '' },
|
@@ -443,6 +440,9 @@ EOT
|
|
443
440
|
description_cols.delete("Project Name")
|
444
441
|
description_cols.delete("Project Tags")
|
445
442
|
end
|
443
|
+
if invoice['metadata'].nil? || invoice['metadata'].empty?
|
444
|
+
description_cols.delete("Tags")
|
445
|
+
end
|
446
446
|
if !['ComputeServer','Instance','Container'].include?(invoice['refType'])
|
447
447
|
description_cols.delete("Power State")
|
448
448
|
end
|
@@ -455,7 +455,7 @@ EOT
|
|
455
455
|
"Storage" => lambda {|it| format_money(it['storageCost'], 'usd', {sigdig:options[:sigdig]}) },
|
456
456
|
"Network" => lambda {|it| format_money(it['networkCost'], 'usd', {sigdig:options[:sigdig]}) },
|
457
457
|
"License" => lambda {|it| format_money(it['licenseCost'], 'usd', {sigdig:options[:sigdig]}) },
|
458
|
-
"
|
458
|
+
"Extra" => lambda {|it| format_money(it['extraCost'], 'usd', {sigdig:options[:sigdig]}) },
|
459
459
|
"Running" => lambda {|it| format_money(it['runningCost'], 'usd', {sigdig:options[:sigdig]}) },
|
460
460
|
"Total Cost" => lambda {|it| format_money(it['totalCost'], 'usd', {sigdig:options[:sigdig]}) },
|
461
461
|
}
|
@@ -468,7 +468,7 @@ EOT
|
|
468
468
|
"Storage" => lambda {|it| format_money(it['storagePrice'], 'usd', {sigdig:options[:sigdig]}) },
|
469
469
|
"Network" => lambda {|it| format_money(it['networkPrice'], 'usd', {sigdig:options[:sigdig]}) },
|
470
470
|
"License" => lambda {|it| format_money(it['licensePrice'], 'usd', {sigdig:options[:sigdig]}) },
|
471
|
-
"
|
471
|
+
"Extra" => lambda {|it| format_money(it['extraPrice'], 'usd', {sigdig:options[:sigdig]}) },
|
472
472
|
"Running" => lambda {|it| format_money(it['runningPrice'], 'usd', {sigdig:options[:sigdig]}) },
|
473
473
|
"Total Price" => lambda {|it| format_money(it['totalPrice'], 'usd', {sigdig:options[:sigdig]}) },
|
474
474
|
}
|
@@ -495,6 +495,7 @@ EOT
|
|
495
495
|
{"USAGE CATEGORY" => lambda {|it| it['usageCategory'] } },
|
496
496
|
{"USAGE" => lambda {|it| it['itemUsage'] } },
|
497
497
|
{"RATE" => lambda {|it| it['itemRate'] } },
|
498
|
+
{"UNIT" => lambda {|it| it['rateUnit'] } },
|
498
499
|
{"COST" => lambda {|it| format_money(it['itemCost'], 'usd', {sigdig:options[:sigdig]}) } },
|
499
500
|
{"PRICE" => lambda {|it| format_money(it['itemPrice'], 'usd', {sigdig:options[:sigdig]}) } },
|
500
501
|
#{"TAX" => lambda {|it| format_money(it['itemTax'], 'usd', {sigdig:options[:sigdig]}) } },
|
@@ -544,7 +545,7 @@ EOT
|
|
544
545
|
"Storage".upcase => lambda {|it| format_money(it[:storage], 'usd', {sigdig:options[:sigdig]}) },
|
545
546
|
"Network".upcase => lambda {|it| format_money(it[:network], 'usd', {sigdig:options[:sigdig]}) },
|
546
547
|
"License".upcase => lambda {|it| format_money(it[:license], 'usd', {sigdig:options[:sigdig]}) },
|
547
|
-
"
|
548
|
+
"Extra".upcase => lambda {|it| format_money(it[:extra], 'usd', {sigdig:options[:sigdig]}) },
|
548
549
|
"MTD" => lambda {|it| format_money(it[:running], 'usd', {sigdig:options[:sigdig]}) },
|
549
550
|
"Total".upcase => lambda {|it|
|
550
551
|
format_money(it[:total], 'usd', {sigdig:options[:sigdig]}) + ((it[:total].to_f > 0 && it[:total] != it[:running]) ? " (Projected)" : "")
|
@@ -558,7 +559,7 @@ EOT
|
|
558
559
|
cost_columns.delete("License".upcase)
|
559
560
|
end
|
560
561
|
if cost_rows.sum { |it| it[:extra].to_f } == 0
|
561
|
-
cost_columns.delete("
|
562
|
+
cost_columns.delete("Extra".upcase)
|
562
563
|
end
|
563
564
|
print as_pretty_table(cost_rows, cost_columns, options)
|
564
565
|
|
@@ -656,6 +657,7 @@ EOT
|
|
656
657
|
options = {}
|
657
658
|
params = {}
|
658
659
|
ref_ids = []
|
660
|
+
query_tags = {}
|
659
661
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
660
662
|
opts.banner = subcommand_usage()
|
661
663
|
opts.on('-a', '--all', "Display all details, costs and prices." ) do
|
@@ -664,13 +666,13 @@ EOT
|
|
664
666
|
options[:show_prices] = true
|
665
667
|
# options[:show_raw_data] = true
|
666
668
|
end
|
667
|
-
# opts.on('--actuals', '--actuals', "Display all actual costs: Compute, Storage, Network,
|
669
|
+
# opts.on('--actuals', '--actuals', "Display all actual costs: Compute, Storage, Network, Extra" ) do
|
668
670
|
# options[:show_actual_costs] = true
|
669
671
|
# end
|
670
|
-
# opts.on('--costs', '--costs', "Display all costs: Compute, Storage, Network,
|
672
|
+
# opts.on('--costs', '--costs', "Display all costs: Compute, Storage, Network, Extra" ) do
|
671
673
|
# options[:show_costs] = true
|
672
674
|
# end
|
673
|
-
opts.on('--prices', '--prices', "Display prices: Total, Compute, Storage, Network,
|
675
|
+
opts.on('--prices', '--prices', "Display prices: Total, Compute, Storage, Network, Extra" ) do
|
674
676
|
options[:show_prices] = true
|
675
677
|
end
|
676
678
|
opts.on('--invoice-id ID', String, "Filter by Invoice ID") do |val|
|
@@ -743,6 +745,11 @@ EOT
|
|
743
745
|
opts.on('--tenant ID', String, "View invoice line items for a tenant. Default is your own account.") do |val|
|
744
746
|
params['accountId'] = val
|
745
747
|
end
|
748
|
+
# opts.on('--tags Name=Value',String, "Filter by tags.") do |val|
|
749
|
+
# k,v = val.split("=")
|
750
|
+
# query_tags[k] ||= []
|
751
|
+
# query_tags[k] << v
|
752
|
+
# end
|
746
753
|
opts.on('--raw-data', '--raw-data', "Display Raw Data, the cost data from the cloud provider's API.") do |val|
|
747
754
|
options[:show_raw_data] = true
|
748
755
|
end
|
@@ -802,6 +809,11 @@ EOT
|
|
802
809
|
end
|
803
810
|
params['rawData'] = true if options[:show_raw_data]
|
804
811
|
params['refId'] = ref_ids unless ref_ids.empty?
|
812
|
+
if query_tags && !query_tags.empty?
|
813
|
+
query_tags.each do |k,v|
|
814
|
+
params['tags.' + k] = v
|
815
|
+
end
|
816
|
+
end
|
805
817
|
@invoice_line_items_interface.setopts(options)
|
806
818
|
if options[:dry_run]
|
807
819
|
print_dry_run @invoice_line_items_interface.dry.list(params)
|
@@ -838,6 +850,7 @@ EOT
|
|
838
850
|
{"USAGE CATEGORY" => lambda {|it| it['usageCategory'] } },
|
839
851
|
{"USAGE" => lambda {|it| it['itemUsage'] } },
|
840
852
|
{"RATE" => lambda {|it| it['itemRate'] } },
|
853
|
+
{"UNIT" => lambda {|it| it['rateUnit'] } },
|
841
854
|
{"COST" => lambda {|it| format_money(it['itemCost'], 'usd', {sigdig:options[:sigdig]}) } },
|
842
855
|
] + (options[:show_prices] ? [
|
843
856
|
{"PRICE" => lambda {|it| format_money(it['itemPrice'], 'usd', {sigdig:options[:sigdig]}) } },
|