morpheus-cli 4.2.8 → 4.2.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/lib/morpheus/api.rb +1 -1
- data/lib/morpheus/api/activity_interface.rb +9 -0
- data/lib/morpheus/api/api_client.rb +83 -27
- data/lib/morpheus/api/apps_interface.rb +21 -0
- data/lib/morpheus/api/dashboard_interface.rb +5 -21
- data/lib/morpheus/api/instances_interface.rb +3 -10
- data/lib/morpheus/api/invoice_line_items_interface.rb +14 -0
- data/lib/morpheus/api/invoices_interface.rb +7 -12
- data/lib/morpheus/api/library_layouts_interface.rb +8 -0
- data/lib/morpheus/api/ping_interface.rb +20 -0
- data/lib/morpheus/api/projects_interface.rb +33 -0
- data/lib/morpheus/api/setup_interface.rb +19 -36
- data/lib/morpheus/api/user_settings_interface.rb +0 -6
- data/lib/morpheus/api/whoami_interface.rb +4 -8
- data/lib/morpheus/benchmarking.rb +16 -26
- data/lib/morpheus/cli.rb +10 -5
- data/lib/morpheus/cli/access_token_command.rb +5 -8
- data/lib/morpheus/cli/activity_command.rb +146 -0
- data/lib/morpheus/cli/apps.rb +312 -121
- data/lib/morpheus/cli/archives_command.rb +1 -1
- data/lib/morpheus/cli/auth_command.rb +4 -11
- data/lib/morpheus/cli/blueprints_command.rb +196 -137
- data/lib/morpheus/cli/change_password_command.rb +1 -1
- data/lib/morpheus/cli/cli_command.rb +225 -72
- data/lib/morpheus/cli/cli_registry.rb +2 -2
- data/lib/morpheus/cli/cloud_datastores_command.rb +1 -1
- data/lib/morpheus/cli/clouds.rb +5 -20
- data/lib/morpheus/cli/clusters.rb +4 -28
- data/lib/morpheus/cli/commands/standard/alias_command.rb +2 -9
- data/lib/morpheus/cli/commands/standard/benchmark_command.rb +2 -0
- data/lib/morpheus/cli/commands/standard/curl_command.rb +2 -3
- data/lib/morpheus/cli/commands/standard/history_command.rb +3 -6
- data/lib/morpheus/cli/commands/standard/man_command.rb +10 -7
- data/lib/morpheus/cli/commands/standard/ssl_verification_command.rb +10 -9
- data/lib/morpheus/cli/containers_command.rb +3 -3
- data/lib/morpheus/cli/credentials.rb +13 -16
- data/lib/morpheus/cli/error_handler.rb +18 -12
- data/lib/morpheus/cli/errors.rb +45 -0
- data/lib/morpheus/cli/execute_schedules_command.rb +1 -1
- data/lib/morpheus/cli/execution_request_command.rb +4 -4
- data/lib/morpheus/cli/groups.rb +84 -132
- data/lib/morpheus/cli/hosts.rb +6 -16
- data/lib/morpheus/cli/instances.rb +100 -183
- data/lib/morpheus/cli/invoices_command.rb +505 -71
- data/lib/morpheus/cli/library_layouts_command.rb +254 -166
- data/lib/morpheus/cli/library_option_lists_command.rb +0 -87
- data/lib/morpheus/cli/library_option_types_command.rb +0 -96
- data/lib/morpheus/cli/license.rb +3 -0
- data/lib/morpheus/cli/login.rb +17 -37
- data/lib/morpheus/cli/logout.rb +9 -5
- data/lib/morpheus/cli/mixins/accounts_helper.rb +83 -7
- data/lib/morpheus/cli/mixins/operations_helper.rb +41 -0
- data/lib/morpheus/cli/mixins/option_source_helper.rb +255 -0
- data/lib/morpheus/cli/mixins/print_helper.rb +18 -4
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +222 -13
- data/lib/morpheus/cli/mixins/remote_helper.rb +139 -0
- data/lib/morpheus/cli/monitoring_checks_command.rb +11 -3
- data/lib/morpheus/cli/network_groups_command.rb +8 -2
- data/lib/morpheus/cli/option_types.rb +1 -1
- data/lib/morpheus/cli/ping.rb +252 -0
- data/lib/morpheus/cli/price_sets_command.rb +16 -27
- data/lib/morpheus/cli/prices_command.rb +34 -27
- data/lib/morpheus/cli/processes_command.rb +81 -7
- data/lib/morpheus/cli/projects_command.rb +607 -0
- data/lib/morpheus/cli/recent_activity_command.rb +87 -65
- data/lib/morpheus/cli/remote.rb +965 -974
- data/lib/morpheus/cli/reports_command.rb +3 -15
- data/lib/morpheus/cli/roles.rb +8 -31
- data/lib/morpheus/cli/service_plans_command.rb +25 -31
- data/lib/morpheus/cli/setup.rb +392 -0
- data/lib/morpheus/cli/shell.rb +144 -56
- data/lib/morpheus/cli/subnets_command.rb +71 -11
- data/lib/morpheus/cli/tasks.rb +3 -3
- data/lib/morpheus/cli/user_sources_command.rb +4 -4
- data/lib/morpheus/cli/users.rb +135 -109
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/whitelabel_settings_command.rb +7 -7
- data/lib/morpheus/cli/whoami.rb +90 -129
- data/lib/morpheus/cli/wiki_command.rb +2 -14
- data/lib/morpheus/ext/rest_client.rb +36 -0
- data/lib/morpheus/formatters.rb +42 -5
- data/lib/morpheus/rest_client.rb +0 -10
- data/lib/morpheus/terminal.rb +41 -1
- data/lib/morpheus/util.rb +24 -0
- metadata +16 -3
- data/lib/morpheus/cli/command_error.rb +0 -22
@@ -3,14 +3,18 @@ require 'date'
|
|
3
3
|
|
4
4
|
class Morpheus::Cli::InvoicesCommand
|
5
5
|
include Morpheus::Cli::CliCommand
|
6
|
+
include Morpheus::Cli::ProvisioningHelper
|
7
|
+
include Morpheus::Cli::OptionSourceHelper
|
6
8
|
|
7
9
|
set_command_name :'invoices'
|
8
10
|
|
9
|
-
register_subcommands :list, :get
|
11
|
+
register_subcommands :list, :get, :refresh,
|
12
|
+
:list_line_items, :get_line_item
|
10
13
|
|
11
14
|
def connect(opts)
|
12
15
|
@api_client = establish_remote_appliance_connection(opts)
|
13
16
|
@invoices_interface = @api_client.invoices
|
17
|
+
@invoice_line_items_interface = @api_client.invoice_line_items
|
14
18
|
end
|
15
19
|
|
16
20
|
def handle(args)
|
@@ -24,17 +28,17 @@ class Morpheus::Cli::InvoicesCommand
|
|
24
28
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
25
29
|
opts.banner = subcommand_usage()
|
26
30
|
opts.on('-a', '--all', "Display all costs, prices and raw data" ) do
|
27
|
-
options[:
|
28
|
-
options[:show_costs] = true
|
31
|
+
options[:show_estimates] = true
|
32
|
+
# options[:show_costs] = true
|
29
33
|
options[:show_prices] = true
|
30
34
|
options[:show_raw_data] = true
|
31
35
|
end
|
32
|
-
opts.on('--
|
33
|
-
options[:
|
34
|
-
end
|
35
|
-
opts.on('--costs', '--costs', "Display all costs: Compute, Memory, Storage, etc." ) do
|
36
|
-
options[:show_costs] = true
|
36
|
+
opts.on('--estimates', '--estimates', "Display all estimated costs, from usage info: Compute, Memory, Storage, etc." ) do
|
37
|
+
options[:show_estimates] = true
|
37
38
|
end
|
39
|
+
# opts.on('--costs', '--costs', "Display all costs: Compute, Memory, Storage, etc." ) do
|
40
|
+
# options[:show_costs] = true
|
41
|
+
# end
|
38
42
|
opts.on('--prices', '--prices', "Display prices: Total, Compute, Memory, Storage, etc." ) do
|
39
43
|
options[:show_prices] = true
|
40
44
|
end
|
@@ -63,28 +67,32 @@ class Morpheus::Cli::InvoicesCommand
|
|
63
67
|
end
|
64
68
|
opts.add_hidden_option('--ref-id')
|
65
69
|
opts.on('--group ID', String, "Filter by Group") do |val|
|
66
|
-
|
67
|
-
|
70
|
+
options[:groups] ||= []
|
71
|
+
options[:groups] << val
|
68
72
|
end
|
69
|
-
opts.on('--cloud
|
70
|
-
|
71
|
-
|
73
|
+
opts.on( '-c', '--cloud CLOUD', "Filter by Cloud" ) do |val|
|
74
|
+
options[:clouds] ||= []
|
75
|
+
options[:clouds] << val
|
72
76
|
end
|
73
77
|
opts.on('--instance ID', String, "Filter by Instance") do |val|
|
74
|
-
|
75
|
-
|
78
|
+
options[:instances] ||= []
|
79
|
+
options[:instances] << val
|
76
80
|
end
|
77
81
|
opts.on('--container ID', String, "Filter by Container") do |val|
|
78
82
|
params['containerId'] ||= []
|
79
83
|
params['containerId'] << val
|
80
84
|
end
|
81
85
|
opts.on('--server ID', String, "Filter by Server (Host)") do |val|
|
82
|
-
|
83
|
-
|
86
|
+
options[:servers] ||= []
|
87
|
+
options[:servers] << val
|
84
88
|
end
|
85
89
|
opts.on('--user ID', String, "Filter by User") do |val|
|
86
|
-
|
87
|
-
|
90
|
+
options[:users] ||= []
|
91
|
+
options[:users] << val
|
92
|
+
end
|
93
|
+
opts.on('--project PROJECT', String, "View invoices for a project.") do |val|
|
94
|
+
options[:projects] ||= []
|
95
|
+
options[:projects] << val
|
88
96
|
end
|
89
97
|
# opts.on('--cluster ID', String, "Filter by Cluster") do |val|
|
90
98
|
# params['clusterId'] ||= []
|
@@ -116,15 +124,45 @@ class Morpheus::Cli::InvoicesCommand
|
|
116
124
|
end
|
117
125
|
optparse.parse!(args)
|
118
126
|
connect(options)
|
127
|
+
# verify_args!(args:args, optparse:optparse, count:0)
|
119
128
|
if args.count > 0
|
120
|
-
|
121
|
-
puts_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args.join(', ')}\n#{optparse}"
|
122
|
-
return 1
|
129
|
+
options[:phrase] = args.join(" ")
|
123
130
|
end
|
124
131
|
begin
|
132
|
+
# construct params
|
133
|
+
params.merge!(parse_list_options(options))
|
134
|
+
if options[:clouds]
|
135
|
+
cloud_ids = parse_cloud_id_list(options[:clouds])
|
136
|
+
return 1, "clouds not found for #{options[:clouds]}" if cloud_ids.nil?
|
137
|
+
params['zoneId'] = cloud_ids
|
138
|
+
end
|
139
|
+
if options[:groups]
|
140
|
+
group_ids = parse_group_id_list(options[:groups])
|
141
|
+
return 1, "groups not found for #{options[:groups]}" if group_ids.nil?
|
142
|
+
params['siteId'] = group_ids
|
143
|
+
end
|
144
|
+
if options[:instances]
|
145
|
+
instance_ids = parse_instance_id_list(options[:instances])
|
146
|
+
return 1, "instances not found for #{options[:instances]}" if instance_ids.nil?
|
147
|
+
params['instanceId'] = instance_ids
|
148
|
+
end
|
149
|
+
if options[:servers]
|
150
|
+
server_ids = parse_server_id_list(options[:servers])
|
151
|
+
return 1, "servers not found for #{options[:servers]}" if server_ids.nil?
|
152
|
+
params['serverId'] = server_ids
|
153
|
+
end
|
154
|
+
if options[:users]
|
155
|
+
user_ids = parse_user_id_list(options[:users])
|
156
|
+
return 1, "users not found for #{options[:users]}" if user_ids.nil?
|
157
|
+
params['userId'] = user_ids
|
158
|
+
end
|
159
|
+
if options[:projects]
|
160
|
+
project_ids = parse_project_id_list(options[:projects])
|
161
|
+
return 1, "projects not found for #{options[:projects]}" if project_ids.nil?
|
162
|
+
params['projectId'] = project_ids
|
163
|
+
end
|
125
164
|
params['rawData'] = true if options[:show_raw_data]
|
126
165
|
params['refId'] = ref_ids unless ref_ids.empty?
|
127
|
-
params.merge!(parse_list_options(options))
|
128
166
|
@invoices_interface.setopts(options)
|
129
167
|
if options[:dry_run]
|
130
168
|
print_dry_run @invoices_interface.dry.list(params)
|
@@ -136,12 +174,6 @@ class Morpheus::Cli::InvoicesCommand
|
|
136
174
|
invoices = json_response['invoices']
|
137
175
|
title = "Morpheus Invoices"
|
138
176
|
subtitles = []
|
139
|
-
if params['status']
|
140
|
-
subtitles << "Status: #{params['status']}"
|
141
|
-
end
|
142
|
-
if params['alarmStatus'] == 'acknowledged'
|
143
|
-
subtitles << "(Acknowledged)"
|
144
|
-
end
|
145
177
|
if params['startDate']
|
146
178
|
subtitles << "Start Date: #{params['startDate']}"
|
147
179
|
end
|
@@ -155,11 +187,17 @@ class Morpheus::Cli::InvoicesCommand
|
|
155
187
|
else
|
156
188
|
# current_date = Time.now
|
157
189
|
# current_period = "#{current_date.year}#{current_date.month.to_s.rjust(2, '0')}"
|
190
|
+
show_projects = invoices.find {|it| it['project'] } || (params['projectId'] || params['projectName'] || params['projectTag'])
|
158
191
|
columns = [
|
159
192
|
{"INVOICE ID" => lambda {|it| it['id'] } },
|
160
193
|
{"TYPE" => lambda {|it| format_invoice_ref_type(it) } },
|
161
194
|
{"REF ID" => lambda {|it| it['refId'] } },
|
162
|
-
{"REF NAME" => lambda {|it| it['refName'] } }
|
195
|
+
{"REF NAME" => lambda {|it| it['refName'] } }
|
196
|
+
] + (show_projects ? [
|
197
|
+
{"PROJECT ID" => lambda {|it| it['project'] ? it['project']['id'] : '' } },
|
198
|
+
{"PROJECT NAME" => lambda {|it| it['project'] ? it['project']['name'] : '' } },
|
199
|
+
{"PROJECT TAGS" => lambda {|it| it['project'] ? truncate_string(format_metadata(it['project']['tags']), 50) : '' } }
|
200
|
+
] : []) + [
|
163
201
|
#{"INTERVAL" => lambda {|it| it['interval'] } },
|
164
202
|
{"CLOUD" => lambda {|it| it['cloud'] ? it['cloud']['name'] : '' } },
|
165
203
|
{"ACCOUNT" => lambda {|it| it['account'] ? it['account']['name'] : '' } },
|
@@ -176,35 +214,16 @@ class Morpheus::Cli::InvoicesCommand
|
|
176
214
|
else
|
177
215
|
format_money(it['totalCost'])
|
178
216
|
end
|
179
|
-
} },
|
180
|
-
{"ACTUAL MTD" => lambda {|it| format_money(it['actualRunningCost']) } },
|
181
|
-
{"ACTUAL TOTAL" => lambda {|it|
|
182
|
-
if it['runningMultiplier'] && it['runningMultiplier'].to_i != 1 && it['actualTotalCost'].to_f > 0
|
183
|
-
format_money(it['actualTotalCost']) + " (Projected)"
|
184
|
-
else
|
185
|
-
format_money(it['actualTotalCost'])
|
186
|
-
end
|
187
217
|
} }
|
188
218
|
]
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
end
|
198
|
-
if options[:show_actual_costs]
|
199
|
-
columns += [
|
200
|
-
{"ACTUAL COMPUTE" => lambda {|it| format_money(it['actualComputeCost']) } },
|
201
|
-
# {"ACTUAL MEMORY" => lambda {|it| format_money(it['actualMemoryCost']) } },
|
202
|
-
{"ACTUAL STORAGE" => lambda {|it| format_money(it['actualStorageCost']) } },
|
203
|
-
{"ACTUAL NETWORK" => lambda {|it| format_money(it['actualNetworkCost']) } },
|
204
|
-
{"ACTUAL OTHER" => lambda {|it| format_money(it['actualExtraCost']) } },
|
205
|
-
]
|
206
|
-
end
|
207
|
-
|
219
|
+
|
220
|
+
columns += [
|
221
|
+
{"COMPUTE" => lambda {|it| format_money(it['computeCost']) } },
|
222
|
+
# {"MEMORY" => lambda {|it| format_money(it['memoryCost']) } },
|
223
|
+
{"STORAGE" => lambda {|it| format_money(it['storageCost']) } },
|
224
|
+
{"NETWORK" => lambda {|it| format_money(it['networkCost']) } },
|
225
|
+
{"OTHER" => lambda {|it| format_money(it['extraCost']) } },
|
226
|
+
]
|
208
227
|
if options[:show_prices]
|
209
228
|
columns += [
|
210
229
|
{"COMPUTE PRICE" => lambda {|it| format_money(it['computePrice']) } },
|
@@ -222,7 +241,23 @@ class Morpheus::Cli::InvoicesCommand
|
|
222
241
|
} }
|
223
242
|
]
|
224
243
|
end
|
225
|
-
|
244
|
+
if options[:show_estimates]
|
245
|
+
columns += [
|
246
|
+
{"MTD EST." => lambda {|it| format_money(it['estimatedRunningCost']) } },
|
247
|
+
{"TOTAL EST." => lambda {|it|
|
248
|
+
if it['runningMultiplier'] && it['runningMultiplier'].to_i != 1 && it['estimatedTotalCost'].to_f > 0
|
249
|
+
format_money(it['estimatedTotalCost']) + " (Projected)"
|
250
|
+
else
|
251
|
+
format_money(it['estimatedTotalCost'])
|
252
|
+
end
|
253
|
+
} },
|
254
|
+
{"COMPUTE EST." => lambda {|it| format_money(it['estimatedComputeCost']) } },
|
255
|
+
# {"MEMORY EST." => lambda {|it| format_money(it['estimatedMemoryCost']) } },
|
256
|
+
{"STORAGE EST." => lambda {|it| format_money(it['estimatedStorageCost']) } },
|
257
|
+
{"NETWORK EST." => lambda {|it| format_money(it['estimatedNetworkCost']) } },
|
258
|
+
{"OTHER EST." => lambda {|it| format_money(it['estimatedExtraCost']) } },
|
259
|
+
]
|
260
|
+
end
|
226
261
|
if options[:show_raw_data]
|
227
262
|
columns += [{"RAW DATA" => lambda {|it| truncate_string(it['rawData'].to_s, 10) } }]
|
228
263
|
end
|
@@ -240,17 +275,30 @@ class Morpheus::Cli::InvoicesCommand
|
|
240
275
|
options = {}
|
241
276
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
242
277
|
opts.banner = subcommand_usage("[id]")
|
278
|
+
opts.on('-a', '--all', "Display all costs, prices and raw data" ) do
|
279
|
+
options[:show_estimates] = true
|
280
|
+
# options[:show_costs] = true
|
281
|
+
options[:show_prices] = true
|
282
|
+
options[:show_raw_data] = true
|
283
|
+
end
|
284
|
+
opts.on('--estimates', '--estimates', "Display all estimated costs, from usage info: Compute, Memory, Storage, etc." ) do
|
285
|
+
options[:show_estimates] = true
|
286
|
+
end
|
243
287
|
opts.on('--raw-data', '--raw-data', "Display Raw Data, the cost data from the cloud provider's API.") do |val|
|
244
288
|
options[:show_raw_data] = true
|
245
289
|
end
|
290
|
+
opts.on('--no-line-items', '--no-line-items', "Do not display line items.") do |val|
|
291
|
+
options[:hide_line_items] = true
|
292
|
+
end
|
246
293
|
build_standard_get_options(opts, options)
|
247
294
|
opts.footer = "Get details about a specific invoice."
|
295
|
+
opts.footer = <<-EOT
|
296
|
+
Get details about a specific invoice.
|
297
|
+
[id] is required. This is the id of an invoice.
|
298
|
+
EOT
|
248
299
|
end
|
249
300
|
optparse.parse!(args)
|
250
|
-
|
251
|
-
puts optparse
|
252
|
-
return 1
|
253
|
-
end
|
301
|
+
verify_args!(args:args, optparse:optparse, min:1)
|
254
302
|
connect(options)
|
255
303
|
id_list = parse_id_list(args)
|
256
304
|
return run_command_for_each_arg(id_list) do |arg|
|
@@ -284,21 +332,31 @@ class Morpheus::Cli::InvoicesCommand
|
|
284
332
|
"Ref ID" => lambda {|it| it['refId'] },
|
285
333
|
"Ref Name" => lambda {|it| it['refName'] },
|
286
334
|
"Plan" => lambda {|it| it['plan'] ? it['plan']['name'] : '' },
|
335
|
+
"Project ID" => lambda {|it| it['project'] ? it['project']['id'] : '' },
|
336
|
+
"Project Name" => lambda {|it| it['project'] ? it['project']['name'] : '' },
|
337
|
+
"Project Tags" => lambda {|it| it['project'] ? format_metadata(it['project']['tags']) : '' },
|
287
338
|
"Power State" => lambda {|it| format_server_power_state(it) },
|
288
339
|
"Account" => lambda {|it| it['account'] ? it['account']['name'] : '' },
|
289
340
|
"Active" => lambda {|it| format_boolean(it['active']) },
|
290
341
|
"Period" => lambda {|it| format_invoice_period(it) },
|
342
|
+
"Estimate" => lambda {|it| format_boolean(it['estimate']) },
|
291
343
|
#"Interval" => lambda {|it| it['interval'] },
|
292
344
|
"Start" => lambda {|it| format_date(it['startDate']) },
|
293
345
|
"End" => lambda {|it| it['endDate'] ? format_date(it['endDate']) : '' },
|
294
|
-
"
|
346
|
+
"Ref Start" => lambda {|it| format_local_dt(it['refStart']) },
|
347
|
+
"Ref End" => lambda {|it| it['refEnd'] ? format_local_dt(it['refEnd']) : '' },
|
295
348
|
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
296
349
|
"Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
|
297
350
|
}
|
298
351
|
# remove columns that do not apply
|
299
|
-
if
|
352
|
+
if invoice['plan'].nil?
|
300
353
|
description_cols.delete("Plan")
|
301
354
|
end
|
355
|
+
if invoice['project'].nil?
|
356
|
+
description_cols.delete("Project ID")
|
357
|
+
description_cols.delete("Project Name")
|
358
|
+
description_cols.delete("Project Tags")
|
359
|
+
end
|
302
360
|
if !['ComputeServer','Instance','Container'].include?(invoice['refType'])
|
303
361
|
description_cols.delete("Power State")
|
304
362
|
end
|
@@ -337,10 +395,15 @@ class Morpheus::Cli::InvoicesCommand
|
|
337
395
|
print "\n"
|
338
396
|
# print_h2 "Costs"
|
339
397
|
cost_rows = [
|
340
|
-
{label: '
|
341
|
-
{label: '
|
342
|
-
{label: 'Actual Cost'.upcase, compute: invoice['actualComputeCost'], memory: invoice['actualMemoryCost'], storage: invoice['actualStorageCost'], network: invoice['actualNetworkCost'], license: invoice['actualLicenseCost'], extra: invoice['actualExtraCost'], running: invoice['actualRunningCost'], total: invoice['actualTotalCost']},
|
398
|
+
{label: 'Price'.upcase, compute: invoice['computePrice'], memory: invoice['memoryPrice'], storage: invoice['storagePrice'], network: invoice['networkPrice'], license: invoice['licensePrice'], extra: invoice['extraPrice'], running: invoice['runningPrice'], total: invoice['totalPrice']},
|
399
|
+
{label: 'Cost'.upcase, compute: invoice['computeCost'], memory: invoice['memoryCost'], storage: invoice['storageCost'], network: invoice['networkCost'], license: invoice['licenseCost'], extra: invoice['extraCost'], running: invoice['runningCost'], total: invoice['totalCost']},
|
343
400
|
]
|
401
|
+
if options[:show_estimates]
|
402
|
+
cost_rows += [
|
403
|
+
{label: 'Estimated Cost'.upcase, compute: invoice['estimatedComputeCost'], memory: invoice['estimatedMemoryCost'], storage: invoice['estimatedStorageCost'], network: invoice['estimatedNetworkCost'], license: invoice['estimatedLicenseCost'], extra: invoice['estimatedExtraCost'], running: invoice['estimatedRunningCost'], total: invoice['estimatedTotalCost']},
|
404
|
+
{label: 'Estimated Price'.upcase, compute: invoice['estimatedComputeCost'], memory: invoice['estimatedMemoryCost'], storage: invoice['estimatedStorageCost'], network: invoice['estimatedNetworkCost'], license: invoice['estimatedLicenseCost'], extra: invoice['estimatedExtraCost'], running: invoice['estimatedRunningCost'], total: invoice['estimatedTotalCost']},
|
405
|
+
]
|
406
|
+
end
|
344
407
|
cost_columns = {
|
345
408
|
"" => lambda {|it| it[:label] },
|
346
409
|
"Compute".upcase => lambda {|it| format_money(it[:compute]) },
|
@@ -375,6 +438,38 @@ class Morpheus::Cli::InvoicesCommand
|
|
375
438
|
puts invoice['rawData']
|
376
439
|
end
|
377
440
|
|
441
|
+
# Line Items
|
442
|
+
line_items = invoice['lineItems']
|
443
|
+
if line_items && line_items.size > 0 && options[:hide_line_items] != true
|
444
|
+
|
445
|
+
line_items_columns = [
|
446
|
+
{"INVOICE ID" => lambda {|it| it['invoiceId'] } },
|
447
|
+
{"TYPE" => lambda {|it| format_invoice_ref_type(it) } },
|
448
|
+
{"REF ID" => lambda {|it| it['refId'] } },
|
449
|
+
{"REF NAME" => lambda {|it| it['refName'] } },
|
450
|
+
#{"REF CATEGORY" => lambda {|it| it['refCategory'] } },
|
451
|
+
{"START" => lambda {|it| format_date(it['startDate']) } },
|
452
|
+
{"END" => lambda {|it| it['endDate'] ? format_date(it['endDate']) : '' } },
|
453
|
+
{"USAGE TYPE" => lambda {|it| it['usageType'] } },
|
454
|
+
{"USAGE CATEGORY" => lambda {|it| it['usageCategory'] } },
|
455
|
+
{"USAGE" => lambda {|it| it['itemUsage'] } },
|
456
|
+
{"RATE" => lambda {|it| it['itemRate'] } },
|
457
|
+
{"COST" => lambda {|it| format_money(it['itemCost']) } },
|
458
|
+
{"PRICE" => lambda {|it| format_money(it['itemPrice']) } },
|
459
|
+
{"TAX" => lambda {|it| format_money(it['itemTax']) } },
|
460
|
+
# {"TERM" => lambda {|it| it['itemTerm'] } },
|
461
|
+
"CREATED" => lambda {|it| format_local_dt(it['dateCreated']) },
|
462
|
+
"UPDATED" => lambda {|it| format_local_dt(it['lastUpdated']) }
|
463
|
+
]
|
464
|
+
|
465
|
+
if options[:show_raw_data]
|
466
|
+
columns += [{"RAW DATA" => lambda {|it| truncate_string(it['rawData'].to_s, 10) } }]
|
467
|
+
end
|
468
|
+
|
469
|
+
print_h2 "Line Items"
|
470
|
+
print as_pretty_table(line_items, line_items_columns, options)
|
471
|
+
print_results_pagination({total: line_items.size, size: line_items.size})
|
472
|
+
end
|
378
473
|
|
379
474
|
print reset,"\n"
|
380
475
|
return 0
|
@@ -384,6 +479,347 @@ class Morpheus::Cli::InvoicesCommand
|
|
384
479
|
end
|
385
480
|
end
|
386
481
|
|
482
|
+
def refresh(args)
|
483
|
+
options = {}
|
484
|
+
params = {}
|
485
|
+
payload = {}
|
486
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
487
|
+
opts.banner = subcommand_usage("[--daily] [--costing] [--current] [-c CLOUD]")
|
488
|
+
opts.on( '--daily', "Refresh Daily Invoices" ) do
|
489
|
+
payload[:daily] = true
|
490
|
+
end
|
491
|
+
opts.on( '--costing', "Refresh Costing Data" ) do
|
492
|
+
payload[:costing] = true
|
493
|
+
end
|
494
|
+
opts.on( '--current', "Collect the most up to date costing data." ) do
|
495
|
+
payload[:current] = true
|
496
|
+
end
|
497
|
+
opts.on( '--date DATE', String, "Date to collect costing for. By default the cost data is collected for the end of the previous period." ) do |val|
|
498
|
+
payload[:date] = val.to_s
|
499
|
+
end
|
500
|
+
opts.on( '-c', '--cloud CLOUD', "Specify cloud(s) to refresh costing for." ) do |val|
|
501
|
+
payload[:clouds] ||= []
|
502
|
+
payload[:clouds] << val
|
503
|
+
end
|
504
|
+
opts.on( '--all', "Refresh costing for all clouds." ) do
|
505
|
+
payload[:all] = true
|
506
|
+
end
|
507
|
+
# opts.on( '-f', '--force', "Force Refresh" ) do
|
508
|
+
# query_params[:force] = 'true'
|
509
|
+
# end
|
510
|
+
build_standard_update_options(opts, options, [:query, :auto_confirm])
|
511
|
+
opts.footer = <<-EOT
|
512
|
+
Refresh invoices.
|
513
|
+
By default, nothing is changed.
|
514
|
+
Include --daily to regenerate invoice records.
|
515
|
+
Include --costing to refresh actual costing data.
|
516
|
+
Include --current to refresh costing data for the actual current time.
|
517
|
+
To get the latest invoice costing data, include --daily --costing --current --all
|
518
|
+
EOT
|
519
|
+
end
|
520
|
+
optparse.parse!(args)
|
521
|
+
verify_args!(args:args, optparse:optparse, count:0)
|
522
|
+
connect(options)
|
523
|
+
params.merge!(parse_query_options(options))
|
524
|
+
if options[:payload]
|
525
|
+
payload = options[:payload]
|
526
|
+
end
|
527
|
+
payload.deep_merge!(parse_passed_options(options))
|
528
|
+
# --clouds
|
529
|
+
if payload[:clouds]
|
530
|
+
payload[:clouds] = parse_id_list(payload[:clouds]).collect {|cloud_id|
|
531
|
+
if cloud_id.to_s =~ /\A\d{1,}\Z/
|
532
|
+
cloud_id
|
533
|
+
else
|
534
|
+
cloud = find_cloud_option(cloud_id)
|
535
|
+
return 1 if cloud.nil?
|
536
|
+
cloud['id']
|
537
|
+
end
|
538
|
+
}
|
539
|
+
end
|
540
|
+
# are you sure?
|
541
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to refresh invoices?")
|
542
|
+
return 9, "aborted command"
|
543
|
+
end
|
544
|
+
# ok, make the request
|
545
|
+
@invoices_interface.setopts(options)
|
546
|
+
if options[:dry_run]
|
547
|
+
print_dry_run @invoices_interface.dry.refresh(params, payload)
|
548
|
+
return
|
549
|
+
end
|
550
|
+
json_response = @invoices_interface.refresh(params, payload)
|
551
|
+
# render the result
|
552
|
+
render_result = render_with_format(json_response, options)
|
553
|
+
return 0 if render_result
|
554
|
+
# print output
|
555
|
+
print_green_success(json_response['msg'] || "Refreshing invoices")
|
556
|
+
return 0
|
557
|
+
end
|
558
|
+
|
559
|
+
def list_line_items(args)
|
560
|
+
options = {}
|
561
|
+
params = {}
|
562
|
+
ref_ids = []
|
563
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
564
|
+
opts.banner = subcommand_usage()
|
565
|
+
opts.on('-a', '--all', "Display all costs, prices and raw data" ) do
|
566
|
+
options[:show_actual_costs] = true
|
567
|
+
options[:show_costs] = true
|
568
|
+
options[:show_prices] = true
|
569
|
+
options[:show_raw_data] = true
|
570
|
+
end
|
571
|
+
# opts.on('--actuals', '--actuals', "Display all actual costs: Compute, Memory, Storage, etc." ) do
|
572
|
+
# options[:show_actual_costs] = true
|
573
|
+
# end
|
574
|
+
# opts.on('--costs', '--costs', "Display all costs: Compute, Memory, Storage, etc." ) do
|
575
|
+
# options[:show_costs] = true
|
576
|
+
# end
|
577
|
+
# opts.on('--prices', '--prices', "Display prices: Total, Compute, Memory, Storage, etc." ) do
|
578
|
+
# options[:show_prices] = true
|
579
|
+
# end
|
580
|
+
opts.on('--type TYPE', String, "Filter by Ref Type eg. ComputeSite (Group), ComputeZone (Cloud), ComputeServer (Host), Instance, Container, User") do |val|
|
581
|
+
if val.to_s.downcase == 'cloud' || val.to_s.downcase == 'zone'
|
582
|
+
params['refType'] = 'ComputeZone'
|
583
|
+
elsif val.to_s.downcase == 'instance'
|
584
|
+
params['refType'] = 'Instance'
|
585
|
+
elsif val.to_s.downcase == 'server' || val.to_s.downcase == 'host'
|
586
|
+
params['refType'] = 'ComputeServer'
|
587
|
+
elsif val.to_s.downcase == 'cluster'
|
588
|
+
params['refType'] = 'ComputeServerGroup'
|
589
|
+
elsif val.to_s.downcase == 'group'
|
590
|
+
params['refType'] = 'ComputeSite'
|
591
|
+
elsif val.to_s.downcase == 'user'
|
592
|
+
params['refType'] = 'User'
|
593
|
+
else
|
594
|
+
params['refType'] = val
|
595
|
+
end
|
596
|
+
end
|
597
|
+
opts.on('--id ID', String, "Filter by Ref ID") do |val|
|
598
|
+
ref_ids << val
|
599
|
+
end
|
600
|
+
opts.on('--ref-id ID', String, "Filter by Ref ID") do |val|
|
601
|
+
ref_ids << val
|
602
|
+
end
|
603
|
+
opts.add_hidden_option('--ref-id')
|
604
|
+
opts.on('--group ID', String, "Filter by Group") do |val|
|
605
|
+
options[:groups] ||= []
|
606
|
+
options[:groups] << val
|
607
|
+
end
|
608
|
+
opts.on( '-c', '--cloud CLOUD', "Filter by Cloud" ) do |val|
|
609
|
+
options[:clouds] ||= []
|
610
|
+
options[:clouds] << val
|
611
|
+
end
|
612
|
+
opts.on('--instance ID', String, "Filter by Instance") do |val|
|
613
|
+
options[:instances] ||= []
|
614
|
+
options[:instances] << val
|
615
|
+
end
|
616
|
+
opts.on('--container ID', String, "Filter by Container") do |val|
|
617
|
+
params['containerId'] ||= []
|
618
|
+
params['containerId'] << val
|
619
|
+
end
|
620
|
+
opts.on('--server ID', String, "Filter by Server (Host)") do |val|
|
621
|
+
options[:servers] ||= []
|
622
|
+
options[:servers] << val
|
623
|
+
end
|
624
|
+
opts.on('--user ID', String, "Filter by User") do |val|
|
625
|
+
options[:users] ||= []
|
626
|
+
options[:users] << val
|
627
|
+
end
|
628
|
+
opts.on('--project PROJECT', String, "View invoices for a project.") do |val|
|
629
|
+
options[:projects] ||= []
|
630
|
+
options[:projects] << val
|
631
|
+
end
|
632
|
+
# opts.on('--cluster ID', String, "Filter by Cluster") do |val|
|
633
|
+
# params['clusterId'] ||= []
|
634
|
+
# params['clusterId'] << val
|
635
|
+
# end
|
636
|
+
opts.on('--start DATE', String, "Start date in the format YYYY-MM-DD.") do |val|
|
637
|
+
params['startDate'] = val #parse_time(val).utc.iso8601
|
638
|
+
end
|
639
|
+
opts.on('--end DATE', String, "End date in the format YYYY-MM-DD. Default is now.") do |val|
|
640
|
+
params['endDate'] = val #parse_time(val).utc.iso8601
|
641
|
+
end
|
642
|
+
opts.on('--period PERIOD', String, "Period in the format YYYYMM. This can be used instead of start/end.") do |val|
|
643
|
+
params['period'] = parse_period(val)
|
644
|
+
end
|
645
|
+
opts.on('--active [true|false]',String, "Filter by active.") do |val|
|
646
|
+
params['active'] = (val.to_s != 'false' && val.to_s != 'off')
|
647
|
+
end
|
648
|
+
opts.on('--estimate [true|false]',String, "Filter by estimate.") do |val|
|
649
|
+
params['estimate'] = (val.to_s != 'false' && val.to_s != 'off')
|
650
|
+
end
|
651
|
+
opts.on('--tenant ID', String, "View invoice line items for a tenant. Default is your own account.") do |val|
|
652
|
+
params['accountId'] = val
|
653
|
+
end
|
654
|
+
opts.on('--raw-data', '--raw-data', "Display Raw Data, the cost data from the cloud provider's API.") do |val|
|
655
|
+
options[:show_raw_data] = true
|
656
|
+
end
|
657
|
+
build_standard_list_options(opts, options)
|
658
|
+
opts.footer = "List invoice line items."
|
659
|
+
end
|
660
|
+
optparse.parse!(args)
|
661
|
+
connect(options)
|
662
|
+
# verify_args!(args:args, optparse:optparse, count:0)
|
663
|
+
if args.count > 0
|
664
|
+
options[:phrase] = args.join(" ")
|
665
|
+
end
|
666
|
+
|
667
|
+
# construct params
|
668
|
+
params.merge!(parse_list_options(options))
|
669
|
+
if options[:clouds]
|
670
|
+
cloud_ids = parse_cloud_id_list(options[:clouds])
|
671
|
+
return 1, "clouds not found for #{options[:clouds]}" if cloud_ids.nil?
|
672
|
+
params['zoneId'] = cloud_ids
|
673
|
+
end
|
674
|
+
if options[:groups]
|
675
|
+
group_ids = parse_group_id_list(options[:groups])
|
676
|
+
return 1, "groups not found for #{options[:groups]}" if group_ids.nil?
|
677
|
+
params['siteId'] = group_ids
|
678
|
+
end
|
679
|
+
if options[:instances]
|
680
|
+
instance_ids = parse_instance_id_list(options[:instances])
|
681
|
+
return 1, "instances not found for #{options[:instances]}" if instance_ids.nil?
|
682
|
+
params['instanceId'] = instance_ids
|
683
|
+
end
|
684
|
+
if options[:servers]
|
685
|
+
server_ids = parse_server_id_list(options[:servers])
|
686
|
+
return 1, "servers not found for #{options[:servers]}" if server_ids.nil?
|
687
|
+
params['serverId'] = server_ids
|
688
|
+
end
|
689
|
+
if options[:users]
|
690
|
+
user_ids = parse_user_id_list(options[:users])
|
691
|
+
return 1, "users not found for #{options[:users]}" if user_ids.nil?
|
692
|
+
params['userId'] = user_ids
|
693
|
+
end
|
694
|
+
if options[:projects]
|
695
|
+
project_ids = parse_project_id_list(options[:projects])
|
696
|
+
return 1, "projects not found for #{options[:projects]}" if project_ids.nil?
|
697
|
+
params['projectId'] = project_ids
|
698
|
+
end
|
699
|
+
params['rawData'] = true if options[:show_raw_data]
|
700
|
+
params['refId'] = ref_ids unless ref_ids.empty?
|
701
|
+
@invoice_line_items_interface.setopts(options)
|
702
|
+
if options[:dry_run]
|
703
|
+
print_dry_run @invoice_line_items_interface.dry.list(params)
|
704
|
+
return
|
705
|
+
end
|
706
|
+
json_response = @invoice_line_items_interface.list(params)
|
707
|
+
line_items = json_response['lineItems']
|
708
|
+
render_response(json_response, options, 'lineItems') do
|
709
|
+
title = "Morpheus Line Items"
|
710
|
+
subtitles = []
|
711
|
+
if params['startDate']
|
712
|
+
subtitles << "Start Date: #{params['startDate']}"
|
713
|
+
end
|
714
|
+
if params['endDate']
|
715
|
+
subtitles << "End Date: #{params['endDate']}"
|
716
|
+
end
|
717
|
+
subtitles += parse_list_subtitles(options)
|
718
|
+
print_h1 title, subtitles
|
719
|
+
if line_items.empty?
|
720
|
+
print yellow,"No line items found.",reset,"\n"
|
721
|
+
else
|
722
|
+
# current_date = Time.now
|
723
|
+
# current_period = "#{current_date.year}#{current_date.month.to_s.rjust(2, '0')}"
|
724
|
+
columns = [
|
725
|
+
{"INVOICE ID" => lambda {|it| it['invoiceId'] } },
|
726
|
+
{"TYPE" => lambda {|it| format_invoice_ref_type(it) } },
|
727
|
+
{"REF ID" => lambda {|it| it['refId'] } },
|
728
|
+
{"REF NAME" => lambda {|it| it['refName'] } },
|
729
|
+
#{"REF CATEGORY" => lambda {|it| it['refCategory'] } },
|
730
|
+
{"START" => lambda {|it| format_date(it['startDate']) } },
|
731
|
+
{"END" => lambda {|it| it['endDate'] ? format_date(it['endDate']) : '' } },
|
732
|
+
{"USAGE TYPE" => lambda {|it| it['usageType'] } },
|
733
|
+
{"USAGE CATEGORY" => lambda {|it| it['usageCategory'] } },
|
734
|
+
{"USAGE" => lambda {|it| it['itemUsage'] } },
|
735
|
+
{"RATE" => lambda {|it| it['itemRate'] } },
|
736
|
+
{"COST" => lambda {|it| format_money(it['itemCost']) } },
|
737
|
+
{"PRICE" => lambda {|it| format_money(it['itemPrice']) } },
|
738
|
+
{"TAX" => lambda {|it| format_money(it['itemTax']) } },
|
739
|
+
# {"TERM" => lambda {|it| it['itemTerm'] } },
|
740
|
+
"CREATED" => lambda {|it| format_local_dt(it['dateCreated']) },
|
741
|
+
"UPDATED" => lambda {|it| format_local_dt(it['lastUpdated']) }
|
742
|
+
]
|
743
|
+
|
744
|
+
if options[:show_raw_data]
|
745
|
+
columns += [{"RAW DATA" => lambda {|it| truncate_string(it['rawData'].to_s, 10) } }]
|
746
|
+
end
|
747
|
+
print as_pretty_table(line_items, columns, options)
|
748
|
+
print_results_pagination(json_response, {:label => "line item", :n_label => "line items"})
|
749
|
+
end
|
750
|
+
print reset,"\n"
|
751
|
+
end
|
752
|
+
if line_items.empty?
|
753
|
+
return 1, "no line items found"
|
754
|
+
else
|
755
|
+
return 0, nil
|
756
|
+
end
|
757
|
+
end
|
758
|
+
|
759
|
+
def get_line_item(args)
|
760
|
+
options = {}
|
761
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
762
|
+
opts.banner = subcommand_usage("[id]")
|
763
|
+
opts.on('--raw-data', '--raw-data', "Display Raw Data, the cost data from the cloud provider's API.") do |val|
|
764
|
+
options[:show_raw_data] = true
|
765
|
+
end
|
766
|
+
build_standard_get_options(opts, options)
|
767
|
+
opts.footer = "Get details about a specific invoice line item."
|
768
|
+
opts.footer = <<-EOT
|
769
|
+
Get details about a specific invoice line item.
|
770
|
+
[id] is required. This is the id of an invoice line item.
|
771
|
+
EOT
|
772
|
+
end
|
773
|
+
optparse.parse!(args)
|
774
|
+
verify_args!(args:args, optparse:optparse, min:1)
|
775
|
+
connect(options)
|
776
|
+
id_list = parse_id_list(args)
|
777
|
+
return run_command_for_each_arg(id_list) do |arg|
|
778
|
+
_get(arg, options)
|
779
|
+
end
|
780
|
+
end
|
781
|
+
|
782
|
+
def _get_line_item(id, options)
|
783
|
+
params = {}
|
784
|
+
if options[:show_raw_data]
|
785
|
+
params['rawData'] = true
|
786
|
+
end
|
787
|
+
@invoice_line_items_interface.setopts(options)
|
788
|
+
if options[:dry_run]
|
789
|
+
print_dry_run @invoice_line_items_interface.dry.get(id, params)
|
790
|
+
return
|
791
|
+
end
|
792
|
+
json_response = @invoice_line_items_interface.get(id, params)
|
793
|
+
line_item = json_response['lineItem']
|
794
|
+
render_response(json_response, options, 'lineItem') do
|
795
|
+
print_h1 "Line Item Details"
|
796
|
+
print cyan
|
797
|
+
description_cols = {
|
798
|
+
"ID" => lambda {|it| it['id'] },
|
799
|
+
"Invoice ID" => lambda {|it| it['invoiceId'] },
|
800
|
+
"Type" => lambda {|it| format_invoice_ref_type(it) },
|
801
|
+
"Ref ID" => lambda {|it| it['refId'] },
|
802
|
+
"Ref Name" => lambda {|it| it['refName'] },
|
803
|
+
"Start" => lambda {|it| format_date(it['startDate']) },
|
804
|
+
"End" => lambda {|it| it['endDate'] ? format_date(it['endDate']) : '' },
|
805
|
+
"Usage Type" => lambda {|it| it['usageType'] },
|
806
|
+
"Usage Category" => lambda {|it| it['usageCategory'] },
|
807
|
+
"Item Usage" => lambda {|it| it['itemUsage'] },
|
808
|
+
"Item Rate" => lambda {|it| it['itemRate'] },
|
809
|
+
"Item Cost" => lambda {|it| format_money(it['itemCost']) },
|
810
|
+
"Item Price" => lambda {|it| format_money(it['itemrPrice']) },
|
811
|
+
"Item Tax" => lambda {|it| format_money(it['itemTax']) },
|
812
|
+
"Item Term" => lambda {|it| it['itemTerm'] },
|
813
|
+
#"Tax Type" => lambda {|it| it['taxType'] },
|
814
|
+
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
815
|
+
"Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
|
816
|
+
}
|
817
|
+
print_description_list(description_cols, line_item, options)
|
818
|
+
print reset,"\n"
|
819
|
+
end
|
820
|
+
return 0, nil
|
821
|
+
end
|
822
|
+
|
387
823
|
private
|
388
824
|
|
389
825
|
# def find_invoice_by_name_or_id(val)
|
@@ -426,10 +862,8 @@ class Morpheus::Cli::InvoicesCommand
|
|
426
862
|
def format_invoice_ref_type(it)
|
427
863
|
if it['refType'] == 'ComputeZone'
|
428
864
|
"Cloud"
|
429
|
-
elsif it['refType'] == '
|
430
|
-
|
431
|
-
elsif it['refType'] == 'ComputeServer'
|
432
|
-
"Host"
|
865
|
+
# elsif it['refType'] == 'ComputeServer'
|
866
|
+
# "Host"
|
433
867
|
elsif it['refType'] == 'ComputeServerGroup'
|
434
868
|
"Cluster"
|
435
869
|
elsif it['refType'] == 'ComputeSite'
|