morpheus-cli 5.3.0.3 → 5.3.1

Sign up to get free protection for your applications and to get access to all the features.
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)