morpheus-cli 5.5.1.4 → 5.5.2

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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +25 -0
  4. data/lib/morpheus/api/archive_buckets_interface.rb +1 -1
  5. data/lib/morpheus/api/body_io.rb +22 -0
  6. data/lib/morpheus/api/catalog_item_types_interface.rb +5 -1
  7. data/lib/morpheus/api/clients_interface.rb +41 -0
  8. data/lib/morpheus/api/clouds_interface.rb +21 -0
  9. data/lib/morpheus/api/instances_interface.rb +8 -1
  10. data/lib/morpheus/api/integrations_interface.rb +30 -0
  11. data/lib/morpheus/api/library_instance_types_interface.rb +15 -3
  12. data/lib/morpheus/api/network_pool_server_types_interface.rb +9 -0
  13. data/lib/morpheus/api/plugins_interface.rb +22 -0
  14. data/lib/morpheus/api/roles_interface.rb +20 -1
  15. data/lib/morpheus/api/security_package_types_interface.rb +9 -0
  16. data/lib/morpheus/api/security_packages_interface.rb +9 -0
  17. data/lib/morpheus/api/security_scans_interface.rb +9 -0
  18. data/lib/morpheus/api/servers_interface.rb +17 -17
  19. data/lib/morpheus/api/storage_providers_interface.rb +1 -1
  20. data/lib/morpheus/api/virtual_images_interface.rb +1 -23
  21. data/lib/morpheus/cli/cli_command.rb +81 -7
  22. data/lib/morpheus/cli/commands/apps.rb +28 -2
  23. data/lib/morpheus/cli/commands/archives_command.rb +2 -2
  24. data/lib/morpheus/cli/commands/blueprints_command.rb +16 -0
  25. data/lib/morpheus/cli/commands/catalog_item_types_command.rb +34 -2
  26. data/lib/morpheus/cli/commands/clients_command.rb +338 -0
  27. data/lib/morpheus/cli/commands/clouds.rb +127 -1
  28. data/lib/morpheus/cli/commands/clusters.rb +42 -12
  29. data/lib/morpheus/cli/commands/curl_command.rb +114 -135
  30. data/lib/morpheus/cli/commands/hosts.rb +108 -11
  31. data/lib/morpheus/cli/commands/instances.rb +115 -14
  32. data/lib/morpheus/cli/commands/integrations_command.rb +215 -4
  33. data/lib/morpheus/cli/commands/invoices_command.rb +20 -11
  34. data/lib/morpheus/cli/commands/jobs_command.rb +299 -190
  35. data/lib/morpheus/cli/commands/library_cluster_layouts_command.rb +16 -2
  36. data/lib/morpheus/cli/commands/library_container_scripts_command.rb +14 -0
  37. data/lib/morpheus/cli/commands/library_container_templates_command.rb +131 -48
  38. data/lib/morpheus/cli/commands/library_container_types_command.rb +17 -4
  39. data/lib/morpheus/cli/commands/library_instance_types_command.rb +85 -7
  40. data/lib/morpheus/cli/commands/library_layouts_command.rb +32 -1
  41. data/lib/morpheus/cli/commands/library_option_lists_command.rb +30 -18
  42. data/lib/morpheus/cli/commands/library_option_types_command.rb +31 -14
  43. data/lib/morpheus/cli/commands/library_spec_templates_command.rb +14 -0
  44. data/lib/morpheus/cli/commands/library_upgrades_command.rb +2 -2
  45. data/lib/morpheus/cli/commands/network_pool_server_types.rb +20 -0
  46. data/lib/morpheus/cli/commands/network_pool_servers_command.rb +55 -158
  47. data/lib/morpheus/cli/commands/network_pools_command.rb +49 -23
  48. data/lib/morpheus/cli/commands/networks_command.rb +262 -45
  49. data/lib/morpheus/cli/commands/plugins.rb +213 -0
  50. data/lib/morpheus/cli/commands/price_sets_command.rb +40 -10
  51. data/lib/morpheus/cli/commands/prices_command.rb +17 -5
  52. data/lib/morpheus/cli/commands/processes_command.rb +2 -1
  53. data/lib/morpheus/cli/commands/remote.rb +7 -10
  54. data/lib/morpheus/cli/commands/roles.rb +924 -335
  55. data/lib/morpheus/cli/commands/search_command.rb +2 -0
  56. data/lib/morpheus/cli/commands/security_groups.rb +72 -84
  57. data/lib/morpheus/cli/commands/security_package_types.rb +32 -0
  58. data/lib/morpheus/cli/commands/security_packages.rb +84 -0
  59. data/lib/morpheus/cli/commands/security_scans.rb +107 -0
  60. data/lib/morpheus/cli/commands/service_plans_command.rb +16 -14
  61. data/lib/morpheus/cli/commands/subnets_command.rb +15 -1
  62. data/lib/morpheus/cli/commands/tasks.rb +34 -1
  63. data/lib/morpheus/cli/commands/tenants_command.rb +1 -1
  64. data/lib/morpheus/cli/commands/user_settings_command.rb +11 -2
  65. data/lib/morpheus/cli/commands/users.rb +50 -9
  66. data/lib/morpheus/cli/commands/virtual_images.rb +14 -0
  67. data/lib/morpheus/cli/commands/workflows.rb +14 -0
  68. data/lib/morpheus/cli/mixins/accounts_helper.rb +6 -5
  69. data/lib/morpheus/cli/mixins/infrastructure_helper.rb +79 -0
  70. data/lib/morpheus/cli/mixins/jobs_helper.rb +4 -5
  71. data/lib/morpheus/cli/mixins/library_helper.rb +2 -0
  72. data/lib/morpheus/cli/mixins/logs_helper.rb +3 -0
  73. data/lib/morpheus/cli/mixins/monitoring_helper.rb +1 -1
  74. data/lib/morpheus/cli/mixins/print_helper.rb +29 -4
  75. data/lib/morpheus/cli/mixins/provisioning_helper.rb +38 -9
  76. data/lib/morpheus/cli/mixins/rest_command.rb +106 -8
  77. data/lib/morpheus/cli/mixins/secondary_rest_command.rb +6 -2
  78. data/lib/morpheus/cli/option_types.rb +94 -25
  79. data/lib/morpheus/cli/version.rb +1 -1
  80. data/lib/morpheus/formatters.rb +10 -1
  81. metadata +15 -2
@@ -8,7 +8,7 @@ class Morpheus::Cli::Hosts
8
8
  set_command_name :hosts
9
9
  set_command_description "View and manage hosts (servers)."
10
10
  register_subcommands :list, :count, :get, :view, :stats, :add, :update, :remove, :logs, :start, :stop, :resize,
11
- :run_workflow, :make_managed, :upgrade_agent, :snapshots, :software, :software_sync,
11
+ :run_workflow, :make_managed, :upgrade_agent, :snapshots, :software, :software_sync, :update_network_label,
12
12
  {:'types' => :list_types},
13
13
  {:exec => :execution_request},
14
14
  :wiki, :update_wiki
@@ -109,11 +109,11 @@ class Morpheus::Cli::Hosts
109
109
  opts.on( '--tenant TENANT', "Tenant Name or ID" ) do |val|
110
110
  options[:account] = val
111
111
  end
112
- opts.on('--labels label',String, "Filter by labels (keywords).") do |val|
113
- val.split(",").each do |k|
114
- options[:labels] ||= []
115
- options[:labels] << k.strip
116
- end
112
+ opts.on('-l', '--labels LABEL', String, "Filter by labels, can match any of the values") do |val|
113
+ add_query_parameter(params, 'labels', parse_labels(val))
114
+ end
115
+ opts.on('--all-labels LABEL', String, "Filter by labels, must match all of the values") do |val|
116
+ add_query_parameter(params, 'allLabels', parse_labels(val))
117
117
  end
118
118
  opts.on('--tags Name=Value',String, "Filter by tags.") do |val|
119
119
  val.split(",").each do |value_pair|
@@ -519,7 +519,7 @@ class Morpheus::Cli::Hosts
519
519
  "Name" => 'name',
520
520
  "Hostname" => 'hostname',
521
521
  "Description" => 'description',
522
- "Labels" => lambda {|it| it['labels'] ? it['labels'].join(',') : '' },
522
+ "Labels" => lambda {|it| format_list(it['labels']) rescue '' },
523
523
  "Tags" => lambda {|it| tags ? format_metadata(tags) : '' },
524
524
  "Owner" => lambda {|it| it['owner'] ? it['owner']['username'] : '' },
525
525
  "Tenant" => lambda {|it| it['account'] ? it['account']['name'] : '' },
@@ -751,6 +751,16 @@ class Morpheus::Cli::Hosts
751
751
  opts.on("--security-groups LIST", Integer, "Security Groups, comma separated list of security group IDs") do |val|
752
752
  options[:security_groups] = val.split(",").collect {|s| s.strip }.select {|s| !s.to_s.empty? }
753
753
  end
754
+ opts.on('--tags LIST', String, "Metadata tags in the format 'ping=pong,flash=bang'") do |val|
755
+ options[:metadata] = val
756
+ end
757
+ opts.on('--metadata [LIST]', String, "Metadata tags in the format 'ping=pong,flash=bang'") do |val|
758
+ options[:metadata] = val
759
+ end
760
+ opts.add_hidden_option('--metadata')
761
+ opts.on('-l', '--labels [LIST]', String, "Labels") do |val|
762
+ options[:options]['labels'] = parse_labels(val)
763
+ end
754
764
  opts.on('--refresh [SECONDS]', String, "Refresh until status is running,failed. Default interval is #{default_refresh_interval} seconds.") do |val|
755
765
  options[:refresh_interval] = val.to_s.empty? ? default_refresh_interval : val.to_f
756
766
  end
@@ -933,7 +943,18 @@ class Morpheus::Cli::Hosts
933
943
  end
934
944
  end
935
945
  end
936
-
946
+
947
+ # Metadata Tags
948
+ if metadata_option_type
949
+ if options[:metadata]
950
+ metadata = parse_metadata(options[:metadata])
951
+ payload['tags'] = metadata if !metadata.empty?
952
+ else
953
+ metadata = prompt_metadata(options)
954
+ payload['tags'] = metadata if !metadata.empty?
955
+ end
956
+ end
957
+
937
958
  api_params = {}
938
959
  api_params['zoneId'] = cloud['id']
939
960
  api_params['poolId'] = payload['config']['resourcePool'] if (payload['config'] && payload['config']['resourcePool'])
@@ -988,8 +1009,8 @@ class Morpheus::Cli::Hosts
988
1009
  opts.on('--power-schedule-type ID', String, "Power Schedule Type ID") do |val|
989
1010
  params['powerScheduleType'] = val == "null" ? nil : val
990
1011
  end
991
- opts.on('--labels [LIST]', String, "Labels (keywords) in the format 'foo, bar'") do |val|
992
- params['labels'] = val.to_s.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
1012
+ opts.on('-l', '--labels [LIST]', String, "Labels") do |val|
1013
+ params['labels'] = parse_labels(val)
993
1014
  end
994
1015
  opts.on('--tags LIST', String, "Tags in the format 'name:value, name:value'. This will add and remove tags.") do |val|
995
1016
  options[:tags] = val
@@ -1276,7 +1297,10 @@ class Morpheus::Cli::Hosts
1276
1297
  current_volumes = volumes_response['volumes'].sort {|x,y| x['displayOrder'] <=> y['displayOrder'] }
1277
1298
 
1278
1299
  # prompt for volumes
1279
- volumes = prompt_resize_volumes(current_volumes, service_plan, provision_type, options)
1300
+ vol_options = options
1301
+ vol_options['siteId'] = group_id
1302
+ vol_options['zoneId'] = cloud_id
1303
+ volumes = prompt_resize_volumes(current_volumes, service_plan, provision_type, vol_options)
1280
1304
  if !volumes.empty?
1281
1305
  payload[:volumes] = volumes
1282
1306
  end
@@ -2219,6 +2243,79 @@ EOT
2219
2243
  end
2220
2244
  out
2221
2245
  end
2246
+
2247
+ def update_network_label(args)
2248
+ options = {}
2249
+ params = {}
2250
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
2251
+ opts.banner = subcommand_usage("[server] [options]")
2252
+ opts.on('--network NETWORK', "Network Interface ID" ) do |val|
2253
+ options[:network] = val
2254
+ end
2255
+ opts.on('--label LABEL', "label") do |val|
2256
+ options[:label] = val
2257
+ end
2258
+ opts.footer = "Change the label of a Network Interface.\n" +
2259
+ "Editing an Interface will not apply changes to the physical hardware. The purpose is for a manual override or data correction (mostly for self managed or baremetal servers where cloud sync is not available)\n" +
2260
+ "[name or id] is required. The name or the id of the server.\n" +
2261
+ "[network] ID of the Network Interface. (optional).\n" +
2262
+ "[label] New Label name for the Network Interface (optional)"
2263
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
2264
+ end
2265
+ optparse.parse!(args)
2266
+ if args.count != 1
2267
+ puts_error "#{Morpheus::Terminal.angry_prompt}wrong number of arguments. Expected 1 and received #{args.count} #{args.inspect}\n#{optparse}"
2268
+ return 1
2269
+ end
2270
+ connect(options)
2271
+
2272
+ begin
2273
+ host = find_host_by_name_or_id(args[0])
2274
+ return 1 if host.nil?
2275
+
2276
+ network_id = options[:network]
2277
+ if network_id != nil && network_id.to_i == 0
2278
+ print_red_alert "network must be an ID/integer above 0, not a name/string value."
2279
+ network_id = nil
2280
+ end
2281
+
2282
+
2283
+ if !network_id
2284
+ available_networks = get_available_networks(host)
2285
+ network_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'network', 'fieldLabel' => 'Network', 'type' => 'select', 'selectOptions' => available_networks, 'required' => true, 'defaultValue' => available_networks[0], 'description' => "The networks available for relabeling"}], options[:options])
2286
+ network_id = network_prompt['network']
2287
+ end
2288
+
2289
+ label = options[:label]
2290
+ while label.nil? do
2291
+ label_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'label', 'fieldLabel' => 'Label', 'type' => 'text', 'required' => true}], options[:options])
2292
+ label = label_prompt['label']
2293
+ end
2294
+ payload = { "name" => label }
2295
+ if options[:dry_run]
2296
+ print_dry_run @servers_interface.dry.update_network_label(network_id, host["id"], payload)
2297
+ return
2298
+ end
2299
+ json_response = @servers_interface.update_network_label(network_id, host["id"], payload)
2300
+ if options[:json]
2301
+ puts as_json(json_response, options)
2302
+ else
2303
+ print_green_success "Updated label for host #{host['name']} network #{network_id} to #{label}"
2304
+ end
2305
+ return 0
2306
+ rescue RestClient::Exception => e
2307
+ print_rest_exception(e, options)
2308
+ exit 1
2309
+ end
2310
+ end
2311
+
2312
+ def get_available_networks(host)
2313
+ results = @options_interface.options_for_source('availableNetworksForHost',{serverId: host['id'].to_i})
2314
+ available_networks = results['data'].collect {|it|
2315
+ {"id" => it["value"], "name" => it["name"], "value" => it["value"]}
2316
+ }
2317
+ return available_networks
2318
+ end
2222
2319
 
2223
2320
 
2224
2321
  def make_managed_option_types(connected=true)
@@ -23,6 +23,7 @@ class Morpheus::Cli::Instances
23
23
  :console, :status_check, {:containers => :list_containers},
24
24
  :scaling, {:'scaling-update' => :scaling_update},
25
25
  :wiki, :update_wiki,
26
+ :update_network_label,
26
27
  {:exec => :execution_request},
27
28
  :deploys,
28
29
  :refresh, :prepare_apply, :apply, :state
@@ -121,11 +122,11 @@ class Morpheus::Cli::Instances
121
122
  opts.on( '--plan-code CODE', String, "Filter by Plan code(s)" ) do |val|
122
123
  params['planCode'] = parse_id_list(val)
123
124
  end
124
- opts.on('--labels label',String, "Filter by labels (keywords).") do |val|
125
- val.split(",").each do |k|
126
- options[:labels] ||= []
127
- options[:labels] << k.strip
128
- end
125
+ opts.on('-l', '--labels LABEL', String, "Filter by labels, can match any of the values") do |val|
126
+ add_query_parameter(params, 'labels', parse_labels(val))
127
+ end
128
+ opts.on('--all-labels LABEL', String, "Filter by labels, must match all of the values") do |val|
129
+ add_query_parameter(params, 'allLabels', parse_labels(val))
129
130
  end
130
131
  opts.on('--tags Name=Value',String, "Filter by tags (metadata name value pairs).") do |val|
131
132
  val.split(",").each do |value_pair|
@@ -182,7 +183,6 @@ class Morpheus::Cli::Instances
182
183
 
183
184
  params['showDeleted'] = true if options[:showDeleted]
184
185
  params['deleted'] = true if options[:deleted]
185
- params['labels'] = options[:labels] if options[:labels]
186
186
  if options[:tags]
187
187
  options[:tags].each do |k,v|
188
188
  params['tags.' + k] = v
@@ -257,6 +257,7 @@ class Morpheus::Cli::Instances
257
257
  row = {
258
258
  id: instance['id'],
259
259
  name: instance['name'],
260
+ labels: format_list(instance['labels'], '', 3),
260
261
  connection: format_instance_connection_string(instance),
261
262
  environment: instance['instanceContext'],
262
263
  user: (instance['owner'] ? (instance['owner']['username'] || instance['owner']['id']) : (instance['createdBy'].is_a?(Hash) ? instance['createdBy']['username'] : instance['createdBy'])),
@@ -275,16 +276,16 @@ class Morpheus::Cli::Instances
275
276
  }
276
277
  row
277
278
  }
278
- columns = [:id, {:name => {:max_width => 50}}, :group, :cloud,
279
- :type, :version, :environment,
279
+ columns = [:id, {:name => {:max_width => 50}}, :labels, :group, :cloud,
280
+ :type, :version, :environment, :plan,
280
281
  {:created => {:display_name => "CREATED"}},
281
282
  # {:tenant => {:display_name => "TENANT"}},
282
283
  {:user => {:display_name => "OWNER", :max_width => 20}},
283
- :plan,
284
284
  :nodes, {:connection => {:max_width => 30}}, :status, :cpu, :memory, :storage]
285
285
  # custom pretty table columns ... this is handled in as_pretty_table now(),
286
286
  # todo: remove all these.. and try to always pass rows as the json data itself..
287
287
  if options[:details] != true
288
+ columns.delete(:labels)
288
289
  columns.delete(:plan)
289
290
  end
290
291
  print cyan
@@ -404,7 +405,7 @@ class Morpheus::Cli::Instances
404
405
  end
405
406
  opts.add_hidden_option('--metadata')
406
407
  opts.on('--labels LIST', String, "Labels (keywords) in the format 'foo, bar'") do |val|
407
- options[:labels] = val.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
408
+ options[:labels] = parse_labels(val)
408
409
  end
409
410
  opts.on("--copies NUMBER", Integer, "Number of copies to provision") do |val|
410
411
  options[:copies] = val.to_i
@@ -608,7 +609,7 @@ class Morpheus::Cli::Instances
608
609
  options[:group] = val
609
610
  end
610
611
  opts.on('--labels [LIST]', String, "Labels (keywords) in the format 'foo, bar'") do |val|
611
- params['labels'] = val.to_s.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
612
+ params['labels'] = parse_labels(val)
612
613
  end
613
614
  opts.on('--tags LIST', String, "Tags in the format 'name:value, name:value'. This will add and remove tags.") do |val|
614
615
  options[:tags] = val
@@ -902,6 +903,74 @@ class Morpheus::Cli::Instances
902
903
  end
903
904
  end
904
905
 
906
+ def update_network_label(args)
907
+ options = {}
908
+ params = {}
909
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
910
+ opts.banner = subcommand_usage("[instance] [options]")
911
+ opts.on('--network NETWORK', "Network Interface ID" ) do |val|
912
+ options[:network] = val
913
+ end
914
+ opts.on('--label LABEL', "label") do |val|
915
+ options[:label] = val
916
+ end
917
+ opts.footer = "Change the label of a Network Interface.\n" +
918
+ "Editing an Interface will not apply changes to the physical hardware. The purpose is for a manual override or data correction (mostly for self managed or baremetal servers where cloud sync is not available)\n" +
919
+ "[name or id] is required. The name or the id of the instance.\n" +
920
+ "[network] ID of the Network Interface. (optional).\n" +
921
+ "[label] New Label name for the Network Interface (optional)"
922
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
923
+ end
924
+ optparse.parse!(args)
925
+ if args.count != 1
926
+ puts_error "#{Morpheus::Terminal.angry_prompt}wrong number of arguments. Expected 1 and received #{args.count} #{args.inspect}\n#{optparse}"
927
+ return 1
928
+ end
929
+ connect(options)
930
+
931
+ begin
932
+ instance = find_instance_by_name_or_id(args[0])
933
+ return 1 if instance.nil?
934
+
935
+ network_id = options[:network]
936
+ if network_id != nil && network_id.to_i == 0
937
+ print_red_alert "network must be an ID/integer above 0, not a name/string value."
938
+ network_id = nil
939
+ end
940
+
941
+ if !network_id
942
+ available_networks = get_available_networks(instance)
943
+ network_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'network', 'fieldLabel' => 'Network', 'type' => 'select', 'selectOptions' => available_networks, 'required' => true, 'defaultValue' => available_networks[0], 'description' => "The networks available for relabeling"}], options[:options])
944
+ network_id = network_prompt['network']
945
+ end
946
+
947
+
948
+
949
+ label = options[:label]
950
+ while label.nil? do
951
+ label_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'label', 'fieldLabel' => 'Label', 'type' => 'text', 'required' => true}], options[:options])
952
+ label = label_prompt['label']
953
+ end
954
+ payload = { "name" => label }
955
+ if options[:dry_run]
956
+ print_dry_run @instances_interface.dry.update_network_label(network_id, instance["id"], payload)
957
+ return
958
+ end
959
+ json_response = @instances_interface.update_network_label(network_id, instance["id"], payload)
960
+ if options[:json]
961
+ puts as_json(json_response, options)
962
+ else
963
+ print_green_success "Updated label for instance #{instance['name']} network #{network_id} to #{label}"
964
+ end
965
+ return 0
966
+ rescue RestClient::Exception => e
967
+ print_rest_exception(e, options)
968
+ exit 1
969
+ end
970
+ end
971
+
972
+
973
+
905
974
  def status_check(args)
906
975
  out = ""
907
976
  options = {}
@@ -1725,7 +1794,7 @@ class Morpheus::Cli::Instances
1725
1794
  end
1726
1795
  opts.add_hidden_option('--metadata')
1727
1796
  opts.on('--labels LIST', String, "Labels (keywords) in the format 'foo, bar'") do |val|
1728
- options[:labels] = val.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
1797
+ options[:labels] = parse_labels(val)
1729
1798
  end
1730
1799
  # opts.on("--copies NUMBER", Integer, "Number of copies to provision") do |val|
1731
1800
  # options[:copies] = val.to_i
@@ -2697,13 +2766,19 @@ class Morpheus::Cli::Instances
2697
2766
  cloud_id = instance['cloud']['id']
2698
2767
  layout_id = instance['layout']['id']
2699
2768
  plan_id = instance['plan']['id']
2769
+ resource_pool_id = instance['config']['resourcePoolId'] if instance['config']
2700
2770
  current_plan_name = instance['plan']['name']
2771
+ current_interfaces = get_instance_interfaces(instance)
2772
+ if current_interfaces != false
2773
+ payload['networkInterfaces'] = current_interfaces
2774
+ end
2775
+
2701
2776
 
2702
2777
  # need to GET provision type for some settings...
2703
2778
  provision_type = @provision_types_interface.get(instance['layout']['provisionTypeId'])['provisionType']
2704
2779
 
2705
2780
  # prompt for service plan
2706
- service_plans_json = @instances_interface.service_plans({zoneId: cloud_id, siteId: group_id, layoutId: layout_id})
2781
+ service_plans_json = @instances_interface.service_plans({zoneId: cloud_id, siteId: group_id, layoutId: layout_id, resourcePoolId: resource_pool_id})
2707
2782
  service_plans = service_plans_json["plans"]
2708
2783
  service_plans_dropdown = service_plans.collect {|sp| {'name' => sp["name"], 'value' => sp["id"]} } # already sorted
2709
2784
  service_plans_dropdown.each do |plan|
@@ -2723,7 +2798,10 @@ class Morpheus::Cli::Instances
2723
2798
  current_volumes = volumes_response['volumes'].sort {|x,y| x['displayOrder'] <=> y['displayOrder'] }
2724
2799
 
2725
2800
  # prompt for volumes
2726
- volumes = prompt_resize_volumes(current_volumes, service_plan, provision_type, options)
2801
+ vol_options = options
2802
+ vol_options['siteId'] = group_id
2803
+ vol_options['zoneId'] = cloud_id
2804
+ volumes = prompt_resize_volumes(current_volumes, service_plan, provision_type, vol_options)
2727
2805
  if !volumes.empty?
2728
2806
  payload["volumes"] = volumes
2729
2807
  end
@@ -5184,4 +5262,27 @@ private
5184
5262
  }
5185
5263
  end
5186
5264
 
5265
+ def get_available_networks(instance)
5266
+ results = @options_interface.options_for_source('availableNetworksForInstance',{instanceId: instance['id'].to_i})
5267
+ available_networks = results['data'].collect {|it|
5268
+ {"id" => it["value"], "name" => it["name"], "value" => it["value"]}
5269
+ }
5270
+ return available_networks
5271
+ end
5272
+
5273
+ def get_instance_interfaces(instance)
5274
+ begin
5275
+ servers = instance['servers']
5276
+ interfaces = []
5277
+ servers.each do |server|
5278
+ details = @servers_interface.get(server.to_i)['server']
5279
+ details['interfaces'].each do |inter|
5280
+ interfaces.push(inter)
5281
+ end
5282
+ end
5283
+ return interfaces
5284
+ rescue
5285
+ return false
5286
+ end
5287
+ end
5187
5288
  end
@@ -8,6 +8,7 @@ class Morpheus::Cli::IntegrationsCommand
8
8
 
9
9
  register_subcommands :list, :get, :add, :update, :remove, :refresh
10
10
  register_subcommands :list_objects, :get_object, :add_object, :remove_object
11
+ register_subcommands :list_inventory, :get_inventory, :update_inventory
11
12
  register_subcommands :list_types, :get_type
12
13
 
13
14
  def connect(opts)
@@ -936,6 +937,170 @@ EOT
936
937
  return 0, nil
937
938
  end
938
939
 
940
+ def list_inventory(args)
941
+ options = {}
942
+ params = {}
943
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
944
+ opts.banner = subcommand_usage("[integration] [search]")
945
+ build_standard_list_options(opts, options)
946
+ opts.footer = <<-EOT
947
+ List integration inventory.
948
+ [integration] is required. This is the name or id of an integration.
949
+ Only certain types of integrations support this operation, such as Ansible Tower.
950
+ EOT
951
+ end
952
+ optparse.parse!(args)
953
+ verify_args!(args:args, optparse:optparse, min:1)
954
+ connect(options)
955
+
956
+ integration = find_integration_by_name_or_id(args[0])
957
+ return 1, "integration not found for #{args[0]}" if integration.nil?
958
+
959
+ if args.count > 1
960
+ options[:phrase] = args[1..-1].join(" ")
961
+ end
962
+ params.merge!(parse_list_options(options))
963
+ @integrations_interface.setopts(options)
964
+ if options[:dry_run]
965
+ print_dry_run @integrations_interface.dry.list_inventory(integration['id'], params)
966
+ return 0, nil
967
+ end
968
+ json_response = @integrations_interface.list_inventory(integration['id'], params)
969
+ render_response(json_response, options, integration_inventory_list_key) do
970
+ integration_inventory = json_response[integration_inventory_list_key]
971
+ print_h1 "Integration Inventory [#{integration['name']}]", parse_list_subtitles(options), options
972
+ if integration_inventory.empty?
973
+ print cyan,"No inventory found.",reset,"\n"
974
+ else
975
+ list_columns = {
976
+ "ID" => 'id',
977
+ "Name" => 'name',
978
+ "Description" => 'description',
979
+ "Tenant Default" => lambda {|it| (format_list(it['tenants'].collect {|t| t['name'] }) rescue "") },
980
+ }
981
+ print as_pretty_table(integration_inventory, list_columns.upcase_keys!, options)
982
+ print_results_pagination(json_response)
983
+ end
984
+ print reset,"\n"
985
+ end
986
+ return 0, nil
987
+ end
988
+
989
+ def get_inventory(args)
990
+ params = {}
991
+ options = {}
992
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
993
+ opts.banner = subcommand_usage("[integration] [inventory]")
994
+ build_standard_get_options(opts, options)
995
+ opts.footer = <<-EOT
996
+ Get details about a specific integration inventory item.
997
+ [integration] is required. This is the name or id of an integration.
998
+ [inventory] is required. This is the name or id of an integration inventory item.
999
+ Only certain types of integrations support this operation, such as Ansible Tower.
1000
+ EOT
1001
+ end
1002
+ optparse.parse!(args)
1003
+ verify_args!(args:args, optparse:optparse, min:2)
1004
+ connect(options)
1005
+ integration = find_integration_by_name_or_id(args[0])
1006
+ return 1, "integration not found for #{args[0]}" if integration.nil?
1007
+ params.merge!(parse_query_options(options))
1008
+ id_list = parse_id_list(args[1..-1])
1009
+ return run_command_for_each_arg(id_list) do |arg|
1010
+ _get_inventory(integration, arg, params, options)
1011
+ end
1012
+ end
1013
+
1014
+ def _get_inventory(integration, id, params, options)
1015
+ integration_object = nil
1016
+ if id.to_s !~ /\A\d{1,}\Z/
1017
+ integration_inventory = find_integration_inventory_by_name_or_id(integration['id'], id)
1018
+ return 1, "integration inventory not found for #{id}" if integration_inventory.nil?
1019
+ id = integration_inventory['id']
1020
+ end
1021
+ @integrations_interface.setopts(options)
1022
+ if options[:dry_run]
1023
+ print_dry_run @integrations_interface.dry.get_inventory(integration['id'], id, params)
1024
+ return
1025
+ end
1026
+ json_response = @integrations_interface.get_inventory(integration['id'], id, params)
1027
+ integration_inventory = json_response[integration_inventory_object_key]
1028
+ render_response(json_response, options, integration_inventory_object_key) do
1029
+ print_h1 "Integration Inventory Details", [], options
1030
+ print cyan
1031
+ show_columns = {
1032
+ "ID" => 'id',
1033
+ "Name" => 'name',
1034
+ "Description" => 'description',
1035
+ "Tenant Default" => lambda {|it| (format_list(it['tenants'].collect {|t| t['name'] }) rescue "") },
1036
+ }
1037
+ print_description_list(show_columns, integration_inventory, options)
1038
+ print reset,"\n"
1039
+ end
1040
+ return 0, nil
1041
+ end
1042
+
1043
+ def update_inventory(args)
1044
+ options = {}
1045
+ params = {}
1046
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
1047
+ opts.banner = subcommand_usage("[integration] [inventory] [options]")
1048
+ opts.on('--tenants [LIST]', String, "Tenant Default, comma separated list of account IDs") do |val|
1049
+ options[:tenants] = parse_array(val)
1050
+ end
1051
+ build_standard_update_options(opts, options)
1052
+ opts.footer = <<-EOT
1053
+ Update an integration inventory item.
1054
+ [integration] is required. This is the name or id of an integration.
1055
+ [inventory] is required. This is the name or id of an integration inventory item.
1056
+ Only certain types of integrations support this operation, such as Ansible Tower.
1057
+ EOT
1058
+ end
1059
+ optparse.parse!(args)
1060
+ verify_args!(args:args, optparse:optparse, count:2)
1061
+ connect(options)
1062
+ integration = find_integration_by_name_or_id(args[0])
1063
+ return 1, "integration not found for #{args[0]}" if integration.nil?
1064
+ integration_inventory = find_integration_inventory_by_name_or_id(integration['id'], args[1])
1065
+ return 1, "integration inventory not found for #{args[1]}" if integration_inventory.nil?
1066
+ # construct payload
1067
+ object_key = integration_inventory_object_key
1068
+ payload = build_payload(options, object_key)
1069
+ if options[:tenants]
1070
+ #params['tenants'] = options[:tenants]
1071
+ params['tenants'] = options[:tenants].collect do |val|
1072
+ if val.to_s =~ /\A\d{1,}\Z/
1073
+ val.to_i
1074
+ else
1075
+ # todo: use /api/options/allTenants to avoid permission errors here..
1076
+ record = find_by_name_or_id(:account, val)
1077
+ if record.nil?
1078
+ exit 1 #return 1, "Tenant not found by '#{val}'"
1079
+ else
1080
+ record['id']
1081
+ end
1082
+ end
1083
+ end
1084
+ end
1085
+ payload.deep_merge!({object_key => params})
1086
+ if payload.empty? || payload[object_key].empty?
1087
+ raise_command_error "Specify at least one option to update.\n#{optparse}"
1088
+ end
1089
+ # make request
1090
+ @integrations_interface.setopts(options)
1091
+ if options[:dry_run]
1092
+ print_dry_run @integrations_interface.dry.update_inventory(integration['id'], integration_inventory['id'], payload)
1093
+ return
1094
+ end
1095
+ json_response = @integrations_interface.update_inventory(integration['id'], integration_inventory['id'], payload)
1096
+ integration_inventory = json_response[object_key]
1097
+ render_response(json_response, options, object_key) do
1098
+ print_green_success "Updated integration inventory #{integration_inventory['name']}"
1099
+ # return _get_inventory(integration, integration_inventory["id"], {}, options)
1100
+ end
1101
+ return 0, nil
1102
+ end
1103
+
939
1104
  private
940
1105
 
941
1106
  def format_integration_type(integration)
@@ -999,7 +1164,7 @@ EOT
999
1164
  return json_response[integration_object_key]
1000
1165
  rescue RestClient::Exception => e
1001
1166
  if e.response && e.response.code == 404
1002
- print_red_alert "integration not found by id '#{id}'"
1167
+ print_red_alert "Integration not found by id '#{id}'"
1003
1168
  else
1004
1169
  raise e
1005
1170
  end
@@ -1010,7 +1175,7 @@ EOT
1010
1175
  json_response = @integrations_interface.list({name: name.to_s})
1011
1176
  integrations = json_response[integration_list_key]
1012
1177
  if integrations.empty?
1013
- print_red_alert "integration not found by name '#{name}'"
1178
+ print_red_alert "Integration not found by name '#{name}'"
1014
1179
  return nil
1015
1180
  elsif integrations.size > 1
1016
1181
  print_red_alert "#{integrations.size} integrations found by name '#{name}'"
@@ -1075,7 +1240,7 @@ EOT
1075
1240
  return json_response[integration_object_key]
1076
1241
  rescue RestClient::Exception => e
1077
1242
  if e.response && e.response.code == 404
1078
- print_red_alert "integration not found by id '#{id}'"
1243
+ print_red_alert "Integration not found by id '#{id}'"
1079
1244
  else
1080
1245
  raise e
1081
1246
  end
@@ -1086,7 +1251,7 @@ EOT
1086
1251
  json_response = @integration_types_interface.list(params.merge({name: name.to_s}))
1087
1252
  integration_types = json_response[integration_type_list_key]
1088
1253
  if integration_types.empty?
1089
- print_red_alert "integration type not found by name '#{name}'"
1254
+ print_red_alert "Integration type not found by name '#{name}'"
1090
1255
  return nil
1091
1256
  elsif integration_types.size > 1
1092
1257
  print_red_alert "#{integration_types.size} integration types found by name '#{name}'"
@@ -1196,4 +1361,50 @@ EOT
1196
1361
  end
1197
1362
  end
1198
1363
 
1364
+ def integration_inventory_object_key
1365
+ 'inventory'
1366
+ end
1367
+
1368
+ def integration_inventory_list_key
1369
+ 'inventory'
1370
+ end
1371
+
1372
+ def find_integration_inventory_by_name_or_id(integration_id, val)
1373
+ if val.to_s =~ /\A\d{1,}\Z/
1374
+ return find_integration_inventory_by_id(integration_id, val)
1375
+ else
1376
+ return find_integration_inventory_by_name(integration_id, val)
1377
+ end
1378
+ end
1379
+
1380
+ def find_integration_inventory_by_id(integration_id, id)
1381
+ begin
1382
+ json_response = @integrations_interface.get_inventory(integration_id, id.to_i)
1383
+ return json_response[integration_inventory_object_key]
1384
+ rescue RestClient::Exception => e
1385
+ if e.response && e.response.code == 404
1386
+ print_red_alert "Inventory not found by id '#{id}'"
1387
+ else
1388
+ raise e
1389
+ end
1390
+ end
1391
+ end
1392
+
1393
+ def find_integration_inventory_by_name(integration_id, name)
1394
+ json_response = @integrations_interface.list_inventory(integration_id, {name: name.to_s})
1395
+ integration_inventory = json_response[integration_inventory_list_key]
1396
+ if integration_inventory.empty?
1397
+ print_red_alert "Inventory not found by name '#{name}'"
1398
+ return nil
1399
+ elsif integration_inventory.size > 1
1400
+ print_red_alert "#{integration_inventory.size} inventory found by name '#{name}'"
1401
+ puts_error as_pretty_table(integration_inventory, [:id, :name], {color:red})
1402
+ print_red_alert "Try using ID instead"
1403
+ print reset,"\n"
1404
+ return nil
1405
+ else
1406
+ return integration_inventory[0]
1407
+ end
1408
+ end
1409
+
1199
1410
  end