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.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api.rb +1 -1
  4. data/lib/morpheus/api/activity_interface.rb +9 -0
  5. data/lib/morpheus/api/api_client.rb +83 -27
  6. data/lib/morpheus/api/apps_interface.rb +21 -0
  7. data/lib/morpheus/api/dashboard_interface.rb +5 -21
  8. data/lib/morpheus/api/instances_interface.rb +3 -10
  9. data/lib/morpheus/api/invoice_line_items_interface.rb +14 -0
  10. data/lib/morpheus/api/invoices_interface.rb +7 -12
  11. data/lib/morpheus/api/library_layouts_interface.rb +8 -0
  12. data/lib/morpheus/api/ping_interface.rb +20 -0
  13. data/lib/morpheus/api/projects_interface.rb +33 -0
  14. data/lib/morpheus/api/setup_interface.rb +19 -36
  15. data/lib/morpheus/api/user_settings_interface.rb +0 -6
  16. data/lib/morpheus/api/whoami_interface.rb +4 -8
  17. data/lib/morpheus/benchmarking.rb +16 -26
  18. data/lib/morpheus/cli.rb +10 -5
  19. data/lib/morpheus/cli/access_token_command.rb +5 -8
  20. data/lib/morpheus/cli/activity_command.rb +146 -0
  21. data/lib/morpheus/cli/apps.rb +312 -121
  22. data/lib/morpheus/cli/archives_command.rb +1 -1
  23. data/lib/morpheus/cli/auth_command.rb +4 -11
  24. data/lib/morpheus/cli/blueprints_command.rb +196 -137
  25. data/lib/morpheus/cli/change_password_command.rb +1 -1
  26. data/lib/morpheus/cli/cli_command.rb +225 -72
  27. data/lib/morpheus/cli/cli_registry.rb +2 -2
  28. data/lib/morpheus/cli/cloud_datastores_command.rb +1 -1
  29. data/lib/morpheus/cli/clouds.rb +5 -20
  30. data/lib/morpheus/cli/clusters.rb +4 -28
  31. data/lib/morpheus/cli/commands/standard/alias_command.rb +2 -9
  32. data/lib/morpheus/cli/commands/standard/benchmark_command.rb +2 -0
  33. data/lib/morpheus/cli/commands/standard/curl_command.rb +2 -3
  34. data/lib/morpheus/cli/commands/standard/history_command.rb +3 -6
  35. data/lib/morpheus/cli/commands/standard/man_command.rb +10 -7
  36. data/lib/morpheus/cli/commands/standard/ssl_verification_command.rb +10 -9
  37. data/lib/morpheus/cli/containers_command.rb +3 -3
  38. data/lib/morpheus/cli/credentials.rb +13 -16
  39. data/lib/morpheus/cli/error_handler.rb +18 -12
  40. data/lib/morpheus/cli/errors.rb +45 -0
  41. data/lib/morpheus/cli/execute_schedules_command.rb +1 -1
  42. data/lib/morpheus/cli/execution_request_command.rb +4 -4
  43. data/lib/morpheus/cli/groups.rb +84 -132
  44. data/lib/morpheus/cli/hosts.rb +6 -16
  45. data/lib/morpheus/cli/instances.rb +100 -183
  46. data/lib/morpheus/cli/invoices_command.rb +505 -71
  47. data/lib/morpheus/cli/library_layouts_command.rb +254 -166
  48. data/lib/morpheus/cli/library_option_lists_command.rb +0 -87
  49. data/lib/morpheus/cli/library_option_types_command.rb +0 -96
  50. data/lib/morpheus/cli/license.rb +3 -0
  51. data/lib/morpheus/cli/login.rb +17 -37
  52. data/lib/morpheus/cli/logout.rb +9 -5
  53. data/lib/morpheus/cli/mixins/accounts_helper.rb +83 -7
  54. data/lib/morpheus/cli/mixins/operations_helper.rb +41 -0
  55. data/lib/morpheus/cli/mixins/option_source_helper.rb +255 -0
  56. data/lib/morpheus/cli/mixins/print_helper.rb +18 -4
  57. data/lib/morpheus/cli/mixins/provisioning_helper.rb +222 -13
  58. data/lib/morpheus/cli/mixins/remote_helper.rb +139 -0
  59. data/lib/morpheus/cli/monitoring_checks_command.rb +11 -3
  60. data/lib/morpheus/cli/network_groups_command.rb +8 -2
  61. data/lib/morpheus/cli/option_types.rb +1 -1
  62. data/lib/morpheus/cli/ping.rb +252 -0
  63. data/lib/morpheus/cli/price_sets_command.rb +16 -27
  64. data/lib/morpheus/cli/prices_command.rb +34 -27
  65. data/lib/morpheus/cli/processes_command.rb +81 -7
  66. data/lib/morpheus/cli/projects_command.rb +607 -0
  67. data/lib/morpheus/cli/recent_activity_command.rb +87 -65
  68. data/lib/morpheus/cli/remote.rb +965 -974
  69. data/lib/morpheus/cli/reports_command.rb +3 -15
  70. data/lib/morpheus/cli/roles.rb +8 -31
  71. data/lib/morpheus/cli/service_plans_command.rb +25 -31
  72. data/lib/morpheus/cli/setup.rb +392 -0
  73. data/lib/morpheus/cli/shell.rb +144 -56
  74. data/lib/morpheus/cli/subnets_command.rb +71 -11
  75. data/lib/morpheus/cli/tasks.rb +3 -3
  76. data/lib/morpheus/cli/user_sources_command.rb +4 -4
  77. data/lib/morpheus/cli/users.rb +135 -109
  78. data/lib/morpheus/cli/version.rb +1 -1
  79. data/lib/morpheus/cli/whitelabel_settings_command.rb +7 -7
  80. data/lib/morpheus/cli/whoami.rb +90 -129
  81. data/lib/morpheus/cli/wiki_command.rb +2 -14
  82. data/lib/morpheus/ext/rest_client.rb +36 -0
  83. data/lib/morpheus/formatters.rb +42 -5
  84. data/lib/morpheus/rest_client.rb +0 -10
  85. data/lib/morpheus/terminal.rb +41 -1
  86. data/lib/morpheus/util.rb +24 -0
  87. metadata +16 -3
  88. 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[:show_actual_costs] = true
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('--actuals', '--actuals', "Display all actual costs: Compute, Memory, Storage, etc." ) do
33
- options[:show_actual_costs] = true
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
- params['siteId'] ||= []
67
- params['siteId'] << val
70
+ options[:groups] ||= []
71
+ options[:groups] << val
68
72
  end
69
- opts.on('--cloud ID', String, "Filter by Cloud") do |val|
70
- params['zoneId'] ||= []
71
- params['zoneId'] << val
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
- params['instanceId'] ||= []
75
- params['instanceId'] << val
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
- params['serverId'] ||= []
83
- params['serverId'] << val
86
+ options[:servers] ||= []
87
+ options[:servers] << val
84
88
  end
85
89
  opts.on('--user ID', String, "Filter by User") do |val|
86
- params['userId'] ||= []
87
- params['userId'] << val
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
- print_error Morpheus::Terminal.angry_prompt
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
- if options[:show_costs]
190
- columns += [
191
- {"COMPUTE" => lambda {|it| format_money(it['computeCost']) } },
192
- # {"MEMORY" => lambda {|it| format_money(it['memoryCost']) } },
193
- {"STORAGE" => lambda {|it| format_money(it['storageCost']) } },
194
- {"NETWORK" => lambda {|it| format_money(it['networkCost']) } },
195
- {"OTHER" => lambda {|it| format_money(it['extraCost']) } },
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
- if args.count < 1
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
- "Estimate" => lambda {|it| format_boolean(it['estimate']) },
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 !invoice['plan']
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: 'Usage 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']},
341
- {label: 'Usage 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']},
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'] == 'Instance'
430
- "Instance"
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'