morpheus-cli 5.3.0.3 → 5.3.1

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/README.md +1 -3
  4. data/lib/morpheus/api/api_client.rb +48 -14
  5. data/lib/morpheus/api/certificate_types_interface.rb +14 -0
  6. data/lib/morpheus/api/certificates_interface.rb +9 -0
  7. data/lib/morpheus/api/integration_types_interface.rb +14 -0
  8. data/lib/morpheus/api/integrations_interface.rb +7 -22
  9. data/lib/morpheus/api/network_services_interface.rb +14 -0
  10. data/lib/morpheus/api/read_interface.rb +23 -0
  11. data/lib/morpheus/api/rest_interface.rb +12 -10
  12. data/lib/morpheus/api/roles_interface.rb +7 -0
  13. data/lib/morpheus/api/servers_interface.rb +7 -0
  14. data/lib/morpheus/api/user_settings_interface.rb +38 -18
  15. data/lib/morpheus/api/vdi_allocations_interface.rb +9 -0
  16. data/lib/morpheus/api/vdi_apps_interface.rb +9 -0
  17. data/lib/morpheus/api/vdi_gateways_interface.rb +9 -0
  18. data/lib/morpheus/api/vdi_interface.rb +28 -0
  19. data/lib/morpheus/api/vdi_pools_interface.rb +19 -0
  20. data/lib/morpheus/cli.rb +9 -2
  21. data/lib/morpheus/cli/apps.rb +59 -75
  22. data/lib/morpheus/cli/catalog_item_types_command.rb +13 -13
  23. data/lib/morpheus/cli/certificates_command.rb +575 -0
  24. data/lib/morpheus/cli/cli_command.rb +61 -6
  25. data/lib/morpheus/cli/clouds.rb +1 -0
  26. data/lib/morpheus/cli/clusters.rb +1 -1
  27. data/lib/morpheus/cli/commands/standard/man_command.rb +4 -5
  28. data/lib/morpheus/cli/hosts.rb +245 -224
  29. data/lib/morpheus/cli/instances.rb +150 -167
  30. data/lib/morpheus/cli/integrations_command.rb +588 -41
  31. data/lib/morpheus/cli/login.rb +7 -0
  32. data/lib/morpheus/cli/mixins/print_helper.rb +33 -18
  33. data/lib/morpheus/cli/mixins/provisioning_helper.rb +3 -3
  34. data/lib/morpheus/cli/mixins/vdi_helper.rb +246 -0
  35. data/lib/morpheus/cli/network_routers_command.rb +22 -9
  36. data/lib/morpheus/cli/networks_command.rb +2 -2
  37. data/lib/morpheus/cli/option_types.rb +34 -33
  38. data/lib/morpheus/cli/remote.rb +1 -1
  39. data/lib/morpheus/cli/reports_command.rb +4 -1
  40. data/lib/morpheus/cli/roles.rb +215 -55
  41. data/lib/morpheus/cli/subnets_command.rb +11 -2
  42. data/lib/morpheus/cli/user_settings_command.rb +268 -57
  43. data/lib/morpheus/cli/vdi_allocations_command.rb +159 -0
  44. data/lib/morpheus/cli/vdi_apps_command.rb +317 -0
  45. data/lib/morpheus/cli/vdi_command.rb +359 -0
  46. data/lib/morpheus/cli/vdi_gateways_command.rb +290 -0
  47. data/lib/morpheus/cli/vdi_pools_command.rb +571 -0
  48. data/lib/morpheus/cli/version.rb +1 -1
  49. data/lib/morpheus/rest_client.rb +30 -0
  50. data/lib/morpheus/terminal.rb +15 -7
  51. metadata +18 -2
@@ -204,7 +204,10 @@ module Morpheus
204
204
  # elsif option['type'] == 'select'
205
205
  end
206
206
  full_option = "--#{full_field_name} #{value_label}"
207
- shorthand_option = option_type['shorthand']
207
+ # switch is an alias for the full option name, fieldName is the default
208
+ if option_type['switch']
209
+ full_option = "--#{option_type['switch']} #{value_label}"
210
+ end
208
211
  arg1, arg2 = full_option, String
209
212
  if option_type['shorthand']
210
213
  arg1, arg2 = full_option, option_type['shorthand']
@@ -243,7 +246,7 @@ module Morpheus
243
246
  ## the standard options for a command that makes api requests (most of them)
244
247
 
245
248
  def build_standard_get_options(opts, options, includes=[], excludes=[])
246
- build_common_options(opts, options, includes + [:query, :json, :yaml, :csv, :fields, :quiet, :dry_run, :remote], excludes)
249
+ build_common_options(opts, options, includes + [:query, :json, :yaml, :csv, :fields, :select, :delim, :quiet, :dry_run, :remote], excludes)
247
250
  end
248
251
 
249
252
  def build_standard_post_options(opts, options, includes=[], excludes=[])
@@ -688,36 +691,50 @@ module Morpheus
688
691
  options[:format] = :csv
689
692
  #options[:csv_delim] = options[:csv_delim] || ","
690
693
  end
691
-
694
+ # deprecated --csv-delim, use --delimiter instead
692
695
  opts.on('--csv-delim CHAR', String, "Delimiter for CSV Output values. Default: ','") do |val|
693
696
  options[:csv] = true
694
697
  options[:format] = :csv
695
698
  val = val.gsub("\\n", "\n").gsub("\\r", "\r").gsub("\\t", "\t") if val.include?("\\")
696
699
  options[:csv_delim] = val
697
700
  end
701
+ opts.add_hidden_option('--csv-delim') if opts.is_a?(Morpheus::Cli::OptionParser)
698
702
 
703
+ # deprecated --csv-newline, use --newline instead
699
704
  opts.on('--csv-newline [CHAR]', String, "Delimiter for CSV Output rows. Default: '\\n'") do |val|
700
705
  options[:csv] = true
701
706
  options[:format] = :csv
702
707
  if val == "no" || val == "none"
703
708
  options[:csv_newline] = ""
704
709
  else
705
- val = val.gsub("\\n", "\n").gsub("\\r", "\r").gsub("\\t", "\t") if val.include?("\\")
710
+ val = val.to_s.gsub("\\n", "\n").gsub("\\r", "\r").gsub("\\t", "\t") if val.include?("\\")
706
711
  options[:csv_newline] = val
707
712
  end
708
713
  end
714
+ opts.add_hidden_option('--csv-newline') if opts.is_a?(Morpheus::Cli::OptionParser)
709
715
 
710
716
  opts.on(nil, '--csv-quotes', "Wrap CSV values with \". Default: false") do
711
717
  options[:csv] = true
712
718
  options[:format] = :csv
713
719
  options[:csv_quotes] = true
714
720
  end
721
+ opts.add_hidden_option('--csv-quotes') if opts.is_a?(Morpheus::Cli::OptionParser)
715
722
 
716
723
  opts.on(nil, '--csv-no-header', "Exclude header for CSV Output.") do
717
724
  options[:csv] = true
718
725
  options[:format] = :csv
719
726
  options[:csv_no_header] = true
720
727
  end
728
+ opts.add_hidden_option('--csv-no-header') if opts.is_a?(Morpheus::Cli::OptionParser)
729
+
730
+ opts.on(nil, '--quotes', "Wrap CSV values with \". Default: false") do
731
+ options[:csv_quotes] = true
732
+ end
733
+ opts.add_hidden_option('--csv-quotes') if opts.is_a?(Morpheus::Cli::OptionParser)
734
+
735
+ opts.on(nil, '--no-header', "Exclude header for CSV Output.") do
736
+ options[:csv_no_header] = true
737
+ end
721
738
 
722
739
  when :fields
723
740
  opts.on('-f', '--fields x,y,z', Array, "Filter Output to a limited set of fields. Default is all fields for json,csv,yaml.") do |val|
@@ -738,10 +755,35 @@ module Morpheus
738
755
  opts.on(nil, '--all-fields', "Show all fields present in the data.") do
739
756
  options[:all_fields] = true
740
757
  end
741
- #opts.add_hidden_option('--all-fields') if opts.is_a?(Morpheus::Cli::OptionParser)
742
758
  opts.on(nil, '--wrap', "Wrap table columns instead hiding them when terminal is not wide enough.") do
743
759
  options[:wrap] = true
744
760
  end
761
+ when :select
762
+ #opts.add_hidden_option('--all-fields') if opts.is_a?(Morpheus::Cli::OptionParser)
763
+ opts.on('--select x,y,z', String, "Filter Output to just print the value(s) of specific fields.") do |val|
764
+ options[:select_fields] = val.split(',').collect {|r| r.strip}
765
+ end
766
+
767
+ when :delim
768
+ opts.on('--delimiter [CHAR]', String, "Delimiter for output values. Default: ',', use with --select and --csv") do |val|
769
+ options[:csv] = true
770
+ options[:format] = :csv
771
+ val = val.to_s
772
+ val = val.gsub("\\n", "\n").gsub("\\r", "\r").gsub("\\t", "\t") if val.include?("\\")
773
+ options[:delim] = val
774
+ end
775
+
776
+ opts.on('--newline [CHAR]', String, "Delimiter for output rows. Default: '\\n', use with --select and --csv") do |val|
777
+ options[:csv] = true
778
+ options[:format] = :csv
779
+ val = val.to_s
780
+ if val == "no" || val == "none"
781
+ options[:newline] = ""
782
+ else
783
+ val = val.to_s.gsub("\\n", "\n").gsub("\\r", "\r").gsub("\\t", "\t") if val.include?("\\")
784
+ options[:newline] = val
785
+ end
786
+ end
745
787
  when :thin
746
788
  opts.on( '--thin', '--thin', "Format headers and columns with thin borders." ) do |val|
747
789
  options[:border_style] = :thin
@@ -1285,7 +1327,20 @@ module Morpheus
1285
1327
  # returns the string rendered, or nil if nothing was rendered.
1286
1328
  def render_response(json_response, options, object_key=nil, &block)
1287
1329
  output = nil
1288
- if options[:json]
1330
+ if options[:select_fields]
1331
+ row = object_key ? json_response[object_key] : json_response
1332
+ row = [row].flatten()
1333
+ if row.is_a?(Array)
1334
+ output = [row].flatten.collect { |record|
1335
+ options[:select_fields].collect { |field|
1336
+ value = get_object_value(record, field)
1337
+ value.is_a?(String) ? value : JSON.fast_generate(value)
1338
+ }.join(options[:delim] || ",")
1339
+ }.join(options[:newline] || "\n")
1340
+ else
1341
+ output = records_as_csv([row], options)
1342
+ end
1343
+ elsif options[:json]
1289
1344
  output = as_json(json_response, options, object_key)
1290
1345
  elsif options[:yaml]
1291
1346
  output = as_yaml(json_response, options, object_key)
@@ -313,6 +313,7 @@ class Morpheus::Cli::Clouds
313
313
  end
314
314
 
315
315
  all_option_types = add_cloud_option_types(cloud_type)
316
+
316
317
  params = Morpheus::Cli::OptionTypes.prompt(all_option_types, options[:options], @api_client, {zoneTypeId: cloud_type['id']})
317
318
  # some optionTypes have fieldContext='zone', so move those to the root level of the zone payload
318
319
  if params['zone'].is_a?(Hash)
@@ -515,7 +515,7 @@ class Morpheus::Cli::Clusters
515
515
  tags = options[:tags]
516
516
 
517
517
  if !tags && !options[:no_prompt]
518
- tags = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'tags', 'type' => 'text', 'fieldLabel' => 'Resource Tags', 'required' => false, 'description' => 'Resource Tags.'}],options[:options],@api_client,{})['tags']
518
+ tags = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'tags', 'type' => 'text', 'fieldLabel' => 'Resource Labels', 'required' => false, 'description' => 'Resource Tags.'}],options[:options],@api_client,{})['tags']
519
519
  end
520
520
 
521
521
  server_payload['tags'] = tags if tags
@@ -341,12 +341,12 @@ You can use this to create isolated environments (sandboxes), within which to ex
341
341
 
342
342
  ```shell
343
343
  export MORPHEUS_CLI_HOME=~/morpheus_test
344
- morpheus remote add demo https://demo.mymorpheus.com --insecure
344
+ morpheus remote add demo https://demo-morpheus --insecure
345
345
  morpheus instances list
346
346
  ```
347
347
 
348
348
  Morpheus saves the remote appliance information, including api access tokens,
349
- to the $MORPHEUS_HOME_DIRECTORY. These files are saved with file permissions **6000**.
349
+ to the CLI home directory. These files are saved with file permissions **6000**.
350
350
  So, only one system user should be allowed to execute morpheus with that home directory.
351
351
  See [Configuration](#Configuration) for more information on the files morpheus reads and writes.
352
352
 
@@ -363,10 +363,9 @@ The `appliances` YAML file contains a list of known appliances, keyed by name.
363
363
  Example:
364
364
  ```yaml
365
365
  :qa:
366
- :host: https://qa.mymorpheus.com
367
- :active: true
366
+ :host: https://qa-morpheus
368
367
  :production:
369
- :host: https://mymorpheus.com
368
+ :host: https://production-morpheus
370
369
  ```
371
370
 
372
371
  ### credentials file
@@ -17,7 +17,7 @@ class Morpheus::Cli::Hosts
17
17
  set_command_name :hosts
18
18
  set_command_description "View and manage hosts (servers)."
19
19
  register_subcommands :list, :count, :get, :view, :stats, :add, :update, :remove, :logs, :start, :stop, :resize,
20
- :run_workflow, :make_managed, :upgrade_agent, :snapshots, :software,
20
+ :run_workflow, :make_managed, :upgrade_agent, :snapshots, :software, :software_sync,
21
21
  {:'types' => :list_types},
22
22
  {:exec => :execution_request},
23
23
  :wiki, :update_wiki
@@ -152,215 +152,206 @@ class Morpheus::Cli::Hosts
152
152
  if args.count > 0
153
153
  options[:phrase] = args.join(" ")
154
154
  end
155
- begin
156
- params.merge!(parse_list_options(options))
157
- account = nil
158
- if options[:account]
159
- account = find_account_by_name_or_id(options[:account])
160
- if account.nil?
161
- return 1
162
- else
163
- params['accountId'] = account['id']
164
- end
165
- end
166
- group = options[:group] ? find_group_by_name_or_id_for_provisioning(options[:group]) : nil
167
- if group
168
- params['siteId'] = group['id']
155
+
156
+ params.merge!(parse_list_options(options))
157
+ account = nil
158
+ if options[:account]
159
+ account = find_account_by_name_or_id(options[:account])
160
+ if account.nil?
161
+ return 1
162
+ else
163
+ params['accountId'] = account['id']
169
164
  end
165
+ end
166
+ group = options[:group] ? find_group_by_name_or_id_for_provisioning(options[:group]) : nil
167
+ if group
168
+ params['siteId'] = group['id']
169
+ end
170
170
 
171
- # argh, this doesn't work because group_id is required for options/clouds
172
- # cloud = options[:cloud] ? find_cloud_by_name_or_id_for_provisioning(group_id, options[:cloud]) : nil
173
- cloud = options[:cloud] ? find_zone_by_name_or_id(nil, options[:cloud]) : nil
174
- if cloud
175
- params['zoneId'] = cloud['id']
176
- end
171
+ # argh, this doesn't work because group_id is required for options/clouds
172
+ # cloud = options[:cloud] ? find_cloud_by_name_or_id_for_provisioning(group_id, options[:cloud]) : nil
173
+ cloud = options[:cloud] ? find_zone_by_name_or_id(nil, options[:cloud]) : nil
174
+ if cloud
175
+ params['zoneId'] = cloud['id']
176
+ end
177
177
 
178
- if options[:created_by]
179
- created_by_ids = find_all_user_ids(account ? account['id'] : nil, options[:created_by])
180
- return if created_by_ids.nil?
181
- params['createdBy'] = created_by_ids
182
- # params['ownerId'] = created_by_ids # 4.2.1+
183
- end
184
-
185
- cluster = nil
186
- if options[:cluster]
187
- if options[:cluster].to_s =~ /\A\d{1,}\Z/
188
- params['clusterId'] = options[:cluster]
189
- else
190
- cluster = find_cluster_by_name_or_id(options[:cluster])
191
- return 1 if cluster.nil?
192
- params['clusterId'] = cluster['id']
193
- end
178
+ if options[:created_by]
179
+ created_by_ids = find_all_user_ids(account ? account['id'] : nil, options[:created_by])
180
+ return if created_by_ids.nil?
181
+ params['createdBy'] = created_by_ids
182
+ # params['ownerId'] = created_by_ids # 4.2.1+
183
+ end
184
+
185
+ cluster = nil
186
+ if options[:cluster]
187
+ if options[:cluster].to_s =~ /\A\d{1,}\Z/
188
+ params['clusterId'] = options[:cluster]
189
+ else
190
+ cluster = find_cluster_by_name_or_id(options[:cluster])
191
+ return 1 if cluster.nil?
192
+ params['clusterId'] = cluster['id']
194
193
  end
195
- params['labels'] = options[:labels] if options[:labels]
196
- if options[:tags] && !options[:tags].empty?
197
- options[:tags].each do |k,v|
198
- params['tags.' + k] = v
199
- end
194
+ end
195
+ params['labels'] = options[:labels] if options[:labels]
196
+ if options[:tags] && !options[:tags].empty?
197
+ options[:tags].each do |k,v|
198
+ params['tags.' + k] = v
200
199
  end
200
+ end
201
201
 
202
- @servers_interface.setopts(options)
203
- if options[:dry_run]
204
- print_dry_run @servers_interface.dry.list(params)
205
- return
206
- end
207
- json_response = @servers_interface.list(params)
202
+ @servers_interface.setopts(options)
203
+ if options[:dry_run]
204
+ print_dry_run @servers_interface.dry.list(params)
205
+ return
206
+ end
207
+ json_response = @servers_interface.list(params)
208
208
 
209
- if options[:json]
210
- json_response.delete('stats') if options[:include_fields]
211
- puts as_json(json_response, options, "servers")
212
- return 0
213
- elsif options[:yaml]
214
- json_response.delete('stats') if options[:include_fields]
215
- puts as_yaml(json_response, options, "servers")
216
- return 0
217
- elsif options[:csv]
218
- # merge stats to be nice here..
219
- if json_response['servers']
220
- all_stats = json_response['stats'] || {}
209
+ # merge stats to be nice here..
210
+ all_stats = json_response['stats']
211
+ if options[:include_fields] || options[:all_fields]
212
+ if json_response['servers']
213
+ if all_stats
221
214
  json_response['servers'].each do |it|
222
215
  it['stats'] ||= all_stats[it['id'].to_s] || all_stats[it['id']]
223
216
  end
224
217
  end
225
- puts records_as_csv(json_response['servers'], options)
226
- return 0
218
+ end
219
+ end
220
+ render_response(json_response, options, "servers") do
221
+
222
+ servers = json_response['servers']
223
+ multi_tenant = json_response['multiTenant'] == true
224
+ title = "Morpheus Hosts"
225
+ subtitles = []
226
+ if account
227
+ subtitles << "Tenant: #{account['name']}".strip
228
+ end
229
+ if group
230
+ subtitles << "Group: #{group['name']}".strip
231
+ end
232
+ if cloud
233
+ subtitles << "Cloud: #{cloud['name']}".strip
234
+ end
235
+ if cluster
236
+ subtitles << "Cluster: #{cluster['name']}".strip
237
+ elsif params['clusterId']
238
+ subtitles << "Cluster: #{params['clusterId']}".strip
239
+ end
240
+ subtitles += parse_list_subtitles(options)
241
+ print_h1 title, subtitles, options
242
+ if servers.empty?
243
+ print cyan,"No hosts found.",reset,"\n"
227
244
  else
228
- servers = json_response['servers']
229
- multi_tenant = json_response['multiTenant'] == true
230
- title = "Morpheus Hosts"
231
- subtitles = []
232
- if account
233
- subtitles << "Tenant: #{account['name']}".strip
234
- end
235
- if group
236
- subtitles << "Group: #{group['name']}".strip
237
- end
238
- if cloud
239
- subtitles << "Cloud: #{cloud['name']}".strip
240
- end
241
- if cluster
242
- subtitles << "Cluster: #{cluster['name']}".strip
243
- elsif params['clusterId']
244
- subtitles << "Cluster: #{params['clusterId']}".strip
245
- end
246
- subtitles += parse_list_subtitles(options)
247
- print_h1 title, subtitles, options
248
- if servers.empty?
249
- print cyan,"No hosts found.",reset,"\n"
250
- else
251
- # print_servers_table(servers)
252
- # server returns stats in a separate key stats => {"id" => {} }
253
- # the id is a string right now..for some reason..
254
- all_stats = json_response['stats'] || {}
255
- servers.each do |it|
256
- found_stats = all_stats[it['id'].to_s] || all_stats[it['id']]
257
- if found_stats
258
- if !it['stats']
259
- it['stats'] = found_stats # || {}
260
- else
261
- it['stats'] = found_stats.merge!(it['stats'])
262
- end
245
+ # print_servers_table(servers)
246
+ # server returns stats in a separate key stats => {"id" => {} }
247
+ # the id is a string right now..for some reason..
248
+ all_stats = json_response['stats'] || {}
249
+ servers.each do |it|
250
+ found_stats = all_stats[it['id'].to_s] || all_stats[it['id']]
251
+ if found_stats
252
+ if !it['stats']
253
+ it['stats'] = found_stats # || {}
254
+ else
255
+ it['stats'] = found_stats.merge!(it['stats'])
263
256
  end
264
257
  end
258
+ end
265
259
 
266
- rows = servers.collect {|server|
267
- stats = server['stats']
268
-
269
- if !stats['maxMemory']
270
- stats['maxMemory'] = stats['usedMemory'] + stats['freeMemory']
260
+ rows = servers.collect {|server|
261
+ stats = server['stats']
262
+
263
+ if !stats['maxMemory']
264
+ stats['maxMemory'] = stats['usedMemory'] + stats['freeMemory']
265
+ end
266
+ cpu_usage_str = !stats ? "" : generate_usage_bar((stats['usedCpu'] || stats['cpuUsage']).to_f, 100, {max_bars: 10})
267
+ memory_usage_str = !stats ? "" : generate_usage_bar(stats['usedMemory'], stats['maxMemory'], {max_bars: 10})
268
+ storage_usage_str = !stats ? "" : generate_usage_bar(stats['usedStorage'], stats['maxStorage'], {max_bars: 10})
269
+ if options[:details] || options[:stats]
270
+ if stats['maxMemory'] && stats['maxMemory'].to_i != 0
271
+ memory_usage_str = memory_usage_str + cyan + format_bytes_short(stats['usedMemory']).strip.rjust(8, ' ') + " / " + format_bytes_short(stats['maxMemory']).strip
271
272
  end
272
- cpu_usage_str = !stats ? "" : generate_usage_bar((stats['usedCpu'] || stats['cpuUsage']).to_f, 100, {max_bars: 10})
273
- memory_usage_str = !stats ? "" : generate_usage_bar(stats['usedMemory'], stats['maxMemory'], {max_bars: 10})
274
- storage_usage_str = !stats ? "" : generate_usage_bar(stats['usedStorage'], stats['maxStorage'], {max_bars: 10})
275
- if options[:details] || options[:stats]
276
- if stats['maxMemory'] && stats['maxMemory'].to_i != 0
277
- memory_usage_str = memory_usage_str + cyan + format_bytes_short(stats['usedMemory']).strip.rjust(8, ' ') + " / " + format_bytes_short(stats['maxMemory']).strip
278
- end
279
- if stats['maxStorage'] && stats['maxStorage'].to_i != 0
280
- storage_usage_str = storage_usage_str + cyan + format_bytes_short(stats['usedStorage']).strip.rjust(8, ' ') + " / " + format_bytes_short(stats['maxStorage']).strip
281
- end
273
+ if stats['maxStorage'] && stats['maxStorage'].to_i != 0
274
+ storage_usage_str = storage_usage_str + cyan + format_bytes_short(stats['usedStorage']).strip.rjust(8, ' ') + " / " + format_bytes_short(stats['maxStorage']).strip
282
275
  end
283
- row = {
284
- id: server['id'],
285
- name: server['name'],
286
- external_name: server['externalName'],
287
- hostname: server['hostname'],
288
- platform: server['serverOs'] ? server['serverOs']['name'].upcase : 'N/A',
289
- type: server['computeServerType'] ? server['computeServerType']['name'] : 'unmanaged',
290
- tenant: server['account'] ? server['account']['name'] : server['accountId'],
291
- owner: server['owner'] ? server['owner']['username'] : server['owner'],
292
- cloud: server['zone'] ? server['zone']['name'] : '',
293
- plan: server['plan'] ? server['plan']['name'] : '',
294
- ip: server['externalIp'],
295
- internal_ip: server['internalIp'],
296
- nodes: server['containers'] ? server['containers'].size : '',
297
- # status: format_server_status(server, cyan),
298
- status: (options[:details]||options[:all_fields]) ? format_server_status(server, cyan) : format_server_status_friendly(server, cyan),
299
- power: format_server_power_state(server, cyan),
300
- cpu: cpu_usage_str + cyan,
301
- memory: memory_usage_str + cyan,
302
- storage: storage_usage_str + cyan,
303
- created: format_local_dt(server['dateCreated']),
304
- updated: format_local_dt(server['lastUpdated']),
305
- }
306
- row
307
- }
308
- # columns = [:id, :name, :type, :cloud, :ip, :internal_ip, :nodes, :status, :power]
309
- columns = {
310
- "ID" => :id,
311
- "Name" => :name,
312
- "External Name" => :external_name,
313
- "Hostname" => :hostname,
314
- "Type" => :type,
315
- "Owner" => :owner,
316
- "Tenant" => :tenant,
317
- "Cloud" => :cloud,
318
- "Plan" => :plan,
319
- "IP" => :ip,
320
- "Private IP" => :internal_ip,
321
- "Nodes" => :nodes,
322
- "Status" => :status,
323
- "Power" => :power,
324
- "CPU" => :cpu,
325
- "Memory" => :memory,
326
- "Storage" => :storage,
327
- "Created" => :created,
328
- "Updated" => :updated,
329
- }
330
- if options[:details] != true
331
- columns.delete("External Name")
332
- columns.delete("Hostname")
333
- columns.delete("Plan")
334
- columns.delete("Private IP")
335
- columns.delete("Owner")
336
- columns.delete("Tenant")
337
- columns.delete("Power")
338
- columns.delete("Created")
339
- columns.delete("Updated")
340
- end
341
- # hide External Name if there are none
342
- if !servers.find {|it| it['externalName'] && it['externalName'] != it['name']}
343
- columns.delete("External Name")
344
276
  end
345
- if !multi_tenant
346
- columns.delete("Tenant")
347
- end
348
- # columns += [:cpu, :memory, :storage]
349
- # # custom pretty table columns ...
350
- # if options[:include_fields]
351
- # columns = options[:include_fields]
352
- # end
353
- print cyan
354
- print as_pretty_table(rows, columns.upcase_keys!, options)
355
- print reset
356
- print_results_pagination(json_response)
277
+ row = {
278
+ id: server['id'],
279
+ name: server['name'],
280
+ external_name: server['externalName'],
281
+ hostname: server['hostname'],
282
+ platform: server['serverOs'] ? server['serverOs']['name'].upcase : 'N/A',
283
+ type: server['computeServerType'] ? server['computeServerType']['name'] : 'unmanaged',
284
+ tenant: server['account'] ? server['account']['name'] : server['accountId'],
285
+ owner: server['owner'] ? server['owner']['username'] : server['owner'],
286
+ cloud: server['zone'] ? server['zone']['name'] : '',
287
+ plan: server['plan'] ? server['plan']['name'] : '',
288
+ ip: server['externalIp'],
289
+ internal_ip: server['internalIp'],
290
+ nodes: server['containers'] ? server['containers'].size : '',
291
+ # status: format_server_status(server, cyan),
292
+ status: (options[:details]||options[:all_fields]) ? format_server_status(server, cyan) : format_server_status_friendly(server, cyan),
293
+ power: format_server_power_state(server, cyan),
294
+ cpu: cpu_usage_str + cyan,
295
+ memory: memory_usage_str + cyan,
296
+ storage: storage_usage_str + cyan,
297
+ created: format_local_dt(server['dateCreated']),
298
+ updated: format_local_dt(server['lastUpdated']),
299
+ }
300
+ row
301
+ }
302
+ # columns = [:id, :name, :type, :cloud, :ip, :internal_ip, :nodes, :status, :power]
303
+ columns = {
304
+ "ID" => :id,
305
+ "Name" => :name,
306
+ "External Name" => :external_name,
307
+ "Hostname" => :hostname,
308
+ "Type" => :type,
309
+ "Owner" => :owner,
310
+ "Tenant" => :tenant,
311
+ "Cloud" => :cloud,
312
+ "Plan" => :plan,
313
+ "IP" => :ip,
314
+ "Private IP" => :internal_ip,
315
+ "Nodes" => :nodes,
316
+ "Status" => :status,
317
+ "Power" => :power,
318
+ "CPU" => :cpu,
319
+ "Memory" => :memory,
320
+ "Storage" => :storage,
321
+ "Created" => :created,
322
+ "Updated" => :updated,
323
+ }
324
+ if options[:details] != true
325
+ columns.delete("External Name")
326
+ columns.delete("Hostname")
327
+ columns.delete("Plan")
328
+ columns.delete("Private IP")
329
+ columns.delete("Owner")
330
+ columns.delete("Tenant")
331
+ columns.delete("Power")
332
+ columns.delete("Created")
333
+ columns.delete("Updated")
357
334
  end
358
- print reset,"\n"
335
+ # hide External Name if there are none
336
+ if !servers.find {|it| it['externalName'] && it['externalName'] != it['name']}
337
+ columns.delete("External Name")
338
+ end
339
+ if !multi_tenant
340
+ columns.delete("Tenant")
341
+ end
342
+ # columns += [:cpu, :memory, :storage]
343
+ # # custom pretty table columns ...
344
+ # if options[:include_fields]
345
+ # columns = options[:include_fields]
346
+ # end
347
+ print cyan
348
+ print as_pretty_table(rows, columns.upcase_keys!, options)
349
+ print reset
350
+ print_results_pagination(json_response)
359
351
  end
360
- rescue RestClient::Exception => e
361
- print_rest_exception(e, options)
362
- exit 1
352
+ print reset,"\n"
363
353
  end
354
+ return 0, nil
364
355
  end
365
356
 
366
357
  def count(args)
@@ -491,7 +482,7 @@ class Morpheus::Cli::Hosts
491
482
  opts.on('--refresh-until STATUS', String, "Refresh until a specified status is reached.") do |val|
492
483
  options[:refresh_until_status] = val.to_s.downcase
493
484
  end
494
- build_common_options(opts, options, [:json, :csv, :yaml, :fields, :dry_run, :remote])
485
+ build_standard_get_options(opts, options)
495
486
  end
496
487
  optparse.parse!(args)
497
488
  if args.count < 1
@@ -506,37 +497,24 @@ class Morpheus::Cli::Hosts
506
497
  end
507
498
 
508
499
  def _get(arg, options)
509
- begin
510
- @servers_interface.setopts(options)
511
- if options[:dry_run]
512
- if arg.to_s =~ /\A\d{1,}\Z/
513
- print_dry_run @servers_interface.dry.get(arg.to_i)
514
- else
515
- print_dry_run @servers_interface.dry.list({name: arg})
516
- end
517
- return
518
- end
519
- json_response = nil
500
+ @servers_interface.setopts(options)
501
+ if options[:dry_run]
520
502
  if arg.to_s =~ /\A\d{1,}\Z/
521
- json_response = @servers_interface.get(arg.to_i)
503
+ print_dry_run @servers_interface.dry.get(arg.to_i)
522
504
  else
523
- server = find_host_by_name_or_id(arg)
524
- json_response = @servers_interface.get(server['id'])
525
- # json_response = {"server" => server} need stats
526
- end
527
- if options[:json]
528
- json_response.delete('stats') if options[:include_fields]
529
- puts as_json(json_response, options, "server")
530
- return 0
531
- elsif options[:yaml]
532
- json_response.delete('stats') if options[:include_fields]
533
- puts as_yaml(json_response, options, "server")
534
- return 0
535
- end
536
- if options[:csv]
537
- puts records_as_csv([json_response['server']], options)
538
- return 0
505
+ print_dry_run @servers_interface.dry.list({name: arg})
539
506
  end
507
+ return
508
+ end
509
+ json_response = nil
510
+ if arg.to_s =~ /\A\d{1,}\Z/
511
+ json_response = @servers_interface.get(arg.to_i)
512
+ else
513
+ server = find_host_by_name_or_id(arg)
514
+ json_response = @servers_interface.get(server['id'])
515
+ # json_response = {"server" => server} need stats
516
+ end
517
+ render_response(json_response, options, "server") do
540
518
  server = json_response['server'] || json_response['host'] || {}
541
519
  #stats = server['stats'] || json_response['stats'] || {}
542
520
  stats = json_response['stats'] || {}
@@ -622,11 +600,8 @@ class Morpheus::Cli::Hosts
622
600
  _get(arg, options)
623
601
  end
624
602
  end
625
-
626
- rescue RestClient::Exception => e
627
- print_rest_exception(e, options)
628
- exit 1
629
603
  end
604
+ return 0, nil
630
605
  end
631
606
 
632
607
  def stats(args)
@@ -781,7 +756,7 @@ class Morpheus::Cli::Hosts
781
756
  opts.on( '-t', '--type TYPE', "Server Type Code" ) do |val|
782
757
  options[:server_type_code] = val
783
758
  end
784
- opts.on("--security-groups LIST", Integer, "Security Groups, comma sepearated list of security group IDs") do |val|
759
+ opts.on("--security-groups LIST", Integer, "Security Groups, comma separated list of security group IDs") do |val|
785
760
  options[:security_groups] = val.split(",").collect {|s| s.strip }.select {|s| !s.to_s.empty? }
786
761
  end
787
762
  opts.on('--refresh [SECONDS]', String, "Refresh until status is running,failed. Default interval is #{default_refresh_interval} seconds.") do |val|
@@ -842,7 +817,7 @@ class Morpheus::Cli::Hosts
842
817
  cloud_type = cloud_type_for_id(cloud['zoneTypeId'])
843
818
 
844
819
  # Server Type
845
- cloud_server_types = cloud_type['serverTypes'].select{|b| b['creatable'] == true }.sort { |x,y| x['displayOrder'] <=> y['displayOrder'] }
820
+ cloud_server_types = cloud_type['serverTypes'].select{|b| b['creatable'] == true && b['containerHypervisor'] == false }.sort { |x,y| x['displayOrder'] <=> y['displayOrder'] }
846
821
  if options[:server_type_code]
847
822
  server_type_code = options[:server_type_code]
848
823
  else
@@ -1982,6 +1957,10 @@ class Morpheus::Cli::Hosts
1982
1957
  optparse = Morpheus::Cli::OptionParser.new do |opts|
1983
1958
  opts.banner = subcommand_usage("[host]")
1984
1959
  build_standard_list_options(opts, options)
1960
+ opts.footer = <<-EOT
1961
+ List installed software for a host.
1962
+ [host] is required. This is the name or id of a host.
1963
+ EOT
1985
1964
  end
1986
1965
  optparse.parse!(args)
1987
1966
  verify_args!(args:args, optparse:optparse, count:1)
@@ -2026,6 +2005,48 @@ class Morpheus::Cli::Hosts
2026
2005
  end
2027
2006
  end
2028
2007
 
2008
+ def software_sync(args)
2009
+ options = {}
2010
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
2011
+ opts.banner = subcommand_usage("[host]")
2012
+ build_standard_update_options(opts, options)
2013
+ opts.footer = <<-EOT
2014
+ Sync installed software for a host.
2015
+ [host] is required. This is the name or id of a host.
2016
+ EOT
2017
+ end
2018
+ optparse.parse!(args)
2019
+ verify_args!(args:args, optparse:optparse, count:1)
2020
+ connect(options)
2021
+ begin
2022
+ server = find_host_by_name_or_id(args[0])
2023
+ return 1 if server.nil?
2024
+ payload = {}
2025
+ if options[:payload]
2026
+ payload = options[:payload]
2027
+ payload.deep_merge!(parse_passed_options(options))
2028
+ else
2029
+ payload.deep_merge!(parse_passed_options(options))
2030
+ end
2031
+ params = {}
2032
+ params.merge!(parse_query_options(options))
2033
+ @servers_interface.setopts(options)
2034
+ if options[:dry_run]
2035
+ print_dry_run @servers_interface.dry.software_sync(server['id'], payload, params)
2036
+ return
2037
+ end
2038
+ json_response = @servers_interface.software_sync(server['id'], payload, params)
2039
+ render_response(json_response, options) do
2040
+ print_green_success "Syncing software for host #{server['name']}"
2041
+ #get([server['id']])
2042
+ end
2043
+ return 0
2044
+ rescue RestClient::Exception => e
2045
+ print_rest_exception(e, options)
2046
+ exit 1
2047
+ end
2048
+ end
2049
+
2029
2050
  private
2030
2051
 
2031
2052
  def find_host_by_id(id)