morpheus-cli 5.5.3.2 → 6.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +8 -0
  4. data/lib/morpheus/api/cloud_resource_pools_interface.rb +28 -3
  5. data/lib/morpheus/api/containers_interface.rb +10 -0
  6. data/lib/morpheus/api/doc_interface.rb +1 -10
  7. data/lib/morpheus/api/jobs_interface.rb +2 -2
  8. data/lib/morpheus/api/key_pairs_interface.rb +9 -0
  9. data/lib/morpheus/api/network_floating_ips_interface.rb +37 -0
  10. data/lib/morpheus/api/resource_pool_groups_interface.rb +51 -0
  11. data/lib/morpheus/cli/cli_command.rb +17 -11
  12. data/lib/morpheus/cli/commands/appliance_settings_command.rb +5 -0
  13. data/lib/morpheus/cli/commands/apps.rb +12 -6
  14. data/lib/morpheus/cli/commands/catalog_item_types_command.rb +44 -12
  15. data/lib/morpheus/cli/commands/clusters.rb +23 -2
  16. data/lib/morpheus/cli/commands/containers_command.rb +129 -4
  17. data/lib/morpheus/cli/commands/doc.rb +14 -13
  18. data/lib/morpheus/cli/commands/hosts.rb +2 -0
  19. data/lib/morpheus/cli/commands/instances.rb +9 -3
  20. data/lib/morpheus/cli/commands/jobs_command.rb +50 -3
  21. data/lib/morpheus/cli/commands/key_pairs.rb +94 -33
  22. data/lib/morpheus/cli/commands/network_floating_ips.rb +109 -0
  23. data/lib/morpheus/cli/commands/reports_command.rb +8 -1
  24. data/lib/morpheus/cli/commands/resource_pool_groups_command.rb +586 -0
  25. data/lib/morpheus/cli/commands/roles.rb +10 -10
  26. data/lib/morpheus/cli/commands/service_catalog_command.rb +40 -2
  27. data/lib/morpheus/cli/commands/service_plans_command.rb +51 -22
  28. data/lib/morpheus/cli/commands/shell.rb +1 -1
  29. data/lib/morpheus/cli/commands/tasks.rb +130 -35
  30. data/lib/morpheus/cli/commands/workflows.rb +109 -23
  31. data/lib/morpheus/cli/mixins/infrastructure_helper.rb +148 -0
  32. data/lib/morpheus/cli/mixins/jobs_helper.rb +30 -3
  33. data/lib/morpheus/cli/mixins/processes_helper.rb +2 -26
  34. data/lib/morpheus/cli/mixins/provisioning_helper.rb +2 -1
  35. data/lib/morpheus/cli/option_types.rb +2 -2
  36. data/lib/morpheus/cli/version.rb +1 -1
  37. data/test/cli/doc_test.rb +1 -1
  38. metadata +6 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f25e2109b97f86a74285c68d93483095a810007259c98248f3764f047763826
4
- data.tar.gz: 703c64d291cebb65e2457c38f43c2fb304881db0cf334c6b86cc3317afd2c543
3
+ metadata.gz: 4160be97bfb96acf0fe1091e9e8d6ecde617bd2aba547ef1cfab8a11d6134ecf
4
+ data.tar.gz: 5e0675e4e1061595e560e2395625e7a7546813e68e09a8237ccd33d92aeb948d
5
5
  SHA512:
6
- metadata.gz: 5e1ca2c4b984cf3b4f27cd3f8348b8ce7155fac65be974962a4a3d543bc6ab35efca1e6e938f6c39617e71dab1d510d87a785a4952eb6c113bbc03a9d6d77173
7
- data.tar.gz: 7e00d09edce06f1d099228770835254fde5110f38e4f033529acda0c060f433e55babc72eda319bc79035e5080110ae24b6c4f4eb6600049a99c64aa3699d988
6
+ metadata.gz: a2992a310163d78082f7d748ca4ae50c673c32e7ae782497bb89f721135ed66509549c124290e9b4bd644cad2e3838c878331427ddd1090325f8946c7d30e731
7
+ data.tar.gz: 48ec07412e0cdcad359ea2dc050cc5017a9a39e368fb662e50b880628213fc4681b85da9cd5d8503ea7426be0fbac77b5e61d2dd8bc397ab13f0ce21695d2758
data/Dockerfile CHANGED
@@ -1,5 +1,5 @@
1
1
  FROM ruby:2.7.5
2
2
 
3
- RUN gem install morpheus-cli -v 5.5.3.2
3
+ RUN gem install morpheus-cli -v 6.0.1
4
4
 
5
5
  ENTRYPOINT ["morpheus"]
@@ -400,6 +400,10 @@ class Morpheus::APIClient
400
400
  Morpheus::CloudResourcePoolsInterface.new(common_interface_options).setopts(@options)
401
401
  end
402
402
 
403
+ def resource_pool_groups
404
+ Morpheus::ResourcePoolGroupsInterface.new(common_interface_options).setopts(@options)
405
+ end
406
+
403
407
  def cloud_folders
404
408
  Morpheus::CloudFoldersInterface.new(common_interface_options).setopts(@options)
405
409
  end
@@ -950,6 +954,10 @@ class Morpheus::APIClient
950
954
  Morpheus::SecurityScansInterface.new(common_interface_options).setopts(@options)
951
955
  end
952
956
 
957
+ def network_floating_ips
958
+ Morpheus::NetworkFloatingIpsInterface.new(common_interface_options).setopts(@options)
959
+ end
960
+
953
961
  def rest(endpoint)
954
962
  Morpheus::RestInterface.new(common_interface_options).setopts(@options.merge({base_path: "#{@base_url}/api/#{endpoint}"}))
955
963
  end
@@ -4,7 +4,15 @@ class Morpheus::CloudResourcePoolsInterface < Morpheus::APIClient
4
4
 
5
5
  def get(cloud_id, id, params={})
6
6
  raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
7
- url = cloud_id.nil? ? "#{@base_url}/api/zones/resource-pools/#{id}" : "#{@base_url}/api/zones/#{cloud_id}/resource-pools/#{id}"
7
+ url = cloud_id.nil? ? "#{@base_url}/api/zones/resource-pools/#{update_resource_pool_id(id)}" : "#{@base_url}/api/zones/#{cloud_id}/resource-pools/#{update_resource_pool_id(id)}"
8
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
9
+ opts = {method: :get, url: url, headers: headers}
10
+ execute(opts)
11
+ end
12
+
13
+ def get_without_cloud(id, params={})
14
+ raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
15
+ url = "#{@base_url}/api/resource-pools/#{update_resource_pool_id(id)}"
8
16
  headers = { params: params, authorization: "Bearer #{@access_token}" }
9
17
  opts = {method: :get, url: url, headers: headers}
10
18
  execute(opts)
@@ -17,6 +25,13 @@ class Morpheus::CloudResourcePoolsInterface < Morpheus::APIClient
17
25
  execute(opts)
18
26
  end
19
27
 
28
+ def list_without_cloud(params={})
29
+ url = "#{@base_url}/api/resource-pools"
30
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
31
+ opts = {method: :get, url: url, headers: headers}
32
+ execute(opts)
33
+ end
34
+
20
35
  def create(cloud_id, payload)
21
36
  url = "#{@base_url}/api/zones/#{cloud_id}/resource-pools"
22
37
  headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
@@ -25,17 +40,27 @@ class Morpheus::CloudResourcePoolsInterface < Morpheus::APIClient
25
40
  end
26
41
 
27
42
  def update(cloud_id, id, payload)
28
- url = "#{@base_url}/api/zones/#{cloud_id}/resource-pools/#{id}"
43
+ url = "#{@base_url}/api/zones/#{cloud_id}/resource-pools/#{update_resource_pool_id(id)}"
29
44
  headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
30
45
  opts = {method: :put, url: url, headers: headers, payload: payload.to_json}
31
46
  execute(opts)
32
47
  end
33
48
 
34
49
  def destroy(cloud_id, id, params={})
35
- url = "#{@base_url}/api/zones/#{cloud_id}/resource-pools/#{id}"
50
+ url = "#{@base_url}/api/zones/#{cloud_id}/resource-pools/#{update_resource_pool_id(id)}"
36
51
  headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
37
52
  opts = {method: :delete, url: url, headers: headers}
38
53
  execute(opts)
39
54
  end
40
55
 
56
+ private
57
+
58
+ def update_resource_pool_id(id)
59
+ id_string = id.to_s
60
+ if id_string["pool-"]
61
+ return id_string[5..-1]
62
+ end
63
+ return id
64
+ end
65
+
41
66
  end
@@ -132,4 +132,14 @@ class Morpheus::ContainersInterface < Morpheus::APIClient
132
132
  execute(method: :put, url: "#{base_path}/#{container_id}/clone-image", payload: payload, headers: headers)
133
133
  end
134
134
 
135
+ def attach_floating_ip(container_id, payload={}, headers={})
136
+ validate_id!(container_id)
137
+ execute(method: :put, url: "#{base_path}/#{container_id}/attach-floating-ip", payload: payload, headers: headers)
138
+ end
139
+
140
+ def detach_floating_ip(container_id, payload={}, headers={})
141
+ validate_id!(container_id)
142
+ execute(method: :put, url: "#{base_path}/#{container_id}/detach-floating-ip", payload: payload, headers: headers)
143
+ end
144
+
135
145
  end
@@ -1,11 +1,6 @@
1
1
  require 'morpheus/api/api_client'
2
2
 
3
3
  class Morpheus::DocInterface < Morpheus::APIClient
4
-
5
- # no Authorization header is required
6
- def authorization_required?
7
- false
8
- end
9
4
 
10
5
  def list(params={})
11
6
  url = "/api/doc"
@@ -24,8 +19,6 @@ class Morpheus::DocInterface < Morpheus::APIClient
24
19
  execute(method: :get, url: url, headers: headers, timeout: 172800, parse_json: !is_yaml)
25
20
  end
26
21
 
27
- alias :swagger :openapi
28
-
29
22
  def download_openapi(outfile, params={})
30
23
  # note that RestClient.execute still requires the full path with base_url
31
24
  url = "#{@base_url}/api/doc/openapi"
@@ -33,7 +26,7 @@ class Morpheus::DocInterface < Morpheus::APIClient
33
26
  if fmt
34
27
  url = url + "." + fmt
35
28
  end
36
- headers = {params: params}
29
+ headers = {params: params, authorization: "Bearer #{@access_token}"}
37
30
  opts = {method: :get, url: url, headers: headers, timeout: 172800, parse_json: false}
38
31
 
39
32
  if @dry_run
@@ -54,6 +47,4 @@ class Morpheus::DocInterface < Morpheus::APIClient
54
47
  http_response
55
48
  end
56
49
 
57
- alias :swagger :download_openapi
58
-
59
50
  end
@@ -43,10 +43,10 @@ class Morpheus::JobsInterface < Morpheus::APIClient
43
43
  execute(method: :delete, url: url, headers: headers)
44
44
  end
45
45
 
46
- def execute_job(id, params={})
46
+ def execute_job(id, payload={}, params={})
47
47
  url = "#{base_path}/#{id}/execute"
48
48
  headers = { params: params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
49
- execute(method: :put, url: url, headers: headers)
49
+ execute(method: :put, url: url, headers: headers, payload: payload.to_json)
50
50
  end
51
51
 
52
52
  =begin
@@ -45,4 +45,13 @@ class Morpheus::KeyPairsInterface < Morpheus::APIClient
45
45
  opts = {method: :delete, url: url, headers: headers}
46
46
  execute(opts)
47
47
  end
48
+
49
+ def generate(account_id, options)
50
+ url = "#{@base_url}/api/key-pairs/generate"
51
+ headers = { :params => {}, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
52
+ headers[:params]['accountId'] = account_id if account_id
53
+ payload = options
54
+ opts = {method: :post, url: url, headers: headers, payload: payload.to_json}
55
+ execute(opts)
56
+ end
48
57
  end
@@ -0,0 +1,37 @@
1
+ require 'morpheus/api/api_client'
2
+
3
+ class Morpheus::NetworkFloatingIpsInterface < Morpheus::APIClient
4
+
5
+ def base_path
6
+ "/api/networks/floating-ips"
7
+ end
8
+
9
+ def list(params={}, headers={})
10
+ execute(method: :get, url: "#{base_path}", params: params, headers: headers)
11
+ end
12
+
13
+ def get(id, params={}, headers={})
14
+ validate_id!(id)
15
+ execute(method: :get, url: "#{base_path}/#{CGI::escape(id.to_s)}", params: params, headers: headers)
16
+ end
17
+
18
+ # def create(payload, params={}, headers={})
19
+ # execute(method: :post, url: "#{base_path}", params: params, payload: payload, headers: headers)
20
+ # end
21
+
22
+ # def update(id, payload, params={}, headers={})
23
+ # validate_id!(id)
24
+ # execute(method: :put, url: "#{base_path}/#{CGI::escape(id.to_s)}", params: params, payload: payload, headers: headers)
25
+ # end
26
+
27
+ def destroy(id, params = {}, headers={})
28
+ validate_id!(id)
29
+ execute(method: :delete, url: "#{base_path}/#{CGI::escape(id.to_s)}", params: params, headers: headers)
30
+ end
31
+
32
+ def release(id, payload={}, params={}, headers={})
33
+ validate_id!(id)
34
+ execute(method: :put, url: "#{base_path}/#{CGI::escape(id.to_s)}/release", params: params, payload: payload, headers: headers)
35
+ end
36
+
37
+ end
@@ -0,0 +1,51 @@
1
+ require 'morpheus/api/api_client'
2
+
3
+ class Morpheus::ResourcePoolGroupsInterface < Morpheus::APIClient
4
+
5
+ def get(id, params={})
6
+ raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
7
+ url = "#{@base_url}/api/resource-pools/groups/#{update_resource_pool_group_id(id)}"
8
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
9
+ opts = {method: :get, url: url, headers: headers}
10
+ execute(opts)
11
+ end
12
+
13
+ def list(params={})
14
+ url = "#{@base_url}/api/resource-pools/groups"
15
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
16
+ opts = {method: :get, url: url, headers: headers}
17
+ execute(opts)
18
+ end
19
+
20
+ def create(payload)
21
+ url = "#{@base_url}/api/resource-pools/groups"
22
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
23
+ opts = {method: :post, url: url, headers: headers, payload: payload.to_json}
24
+ execute(opts)
25
+ end
26
+
27
+ def update(id, payload)
28
+ url = "#{@base_url}/api/resource-pools/groups/#{update_resource_pool_group_id(id)}"
29
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
30
+ opts = {method: :put, url: url, headers: headers, payload: payload.to_json}
31
+ execute(opts)
32
+ end
33
+
34
+ def destroy(id, params={})
35
+ url ="#{@base_url}/api/resource-pools/groups/#{update_resource_pool_group_id(id)}"
36
+ headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
37
+ opts = {method: :delete, url: url, headers: headers}
38
+ execute(opts)
39
+ end
40
+
41
+ private
42
+
43
+ def update_resource_pool_group_id(id)
44
+ id_string = id.to_s
45
+ if id_string["poolGroup-"]
46
+ return id_string[10..-1]
47
+ end
48
+ return id
49
+ end
50
+
51
+ end
@@ -149,6 +149,7 @@ module Morpheus
149
149
  #opts.separator ""
150
150
  #opts.separator "Options:"
151
151
  options[:options] ||= {} # this is where these go..for now
152
+ options[:option_types] = (options[:option_types] || []) + option_types
152
153
  custom_options = options[:options]
153
154
 
154
155
  # add each one to the OptionParser
@@ -345,6 +346,7 @@ module Morpheus
345
346
  custom_option_args = option.sub(/\s?\=\s?/, '__OPTION_DELIM__').split('__OPTION_DELIM__')
346
347
  custom_options = options[:options]
347
348
  option_name_args = custom_option_args[0].split('.')
349
+ option_type = (options[:option_types] || []).find {|it| it['fieldName'] == custom_option_args[0]} || {}
348
350
  if option_name_args.count > 1
349
351
  nested_options = custom_options
350
352
  option_name_args.each_with_index do |name_element,index|
@@ -353,11 +355,13 @@ module Morpheus
353
355
  nested_options = nested_options[name_element]
354
356
  else
355
357
  val = custom_option_args[1]
356
- if (val.to_s[0] == '{' && val.to_s[-1] == '}') || (val.to_s[0] == '[' && val.to_s[-1] == ']')
357
- begin
358
- val = JSON.parse(val)
359
- rescue
360
- Morpheus::Logging::DarkPrinter.puts "Failed to parse option value '#{val}' as JSON" if Morpheus::Logging.debug?
358
+ unless option_type['noParse']
359
+ if (val.to_s[0] == '{' && val.to_s[-1] == '}') || (val.to_s[0] == '[' && val.to_s[-1] == ']')
360
+ begin
361
+ val = JSON.parse(val)
362
+ rescue
363
+ Morpheus::Logging::DarkPrinter.puts "Failed to parse option value '#{val}' as JSON" if Morpheus::Logging.debug?
364
+ end
361
365
  end
362
366
  end
363
367
  nested_options[name_element] = val
@@ -365,11 +369,13 @@ module Morpheus
365
369
  end
366
370
  else
367
371
  val = custom_option_args[1]
368
- if (val.to_s[0] == '{' && val.to_s[-1] == '}') || (val.to_s[0] == '[' && val.to_s[-1] == ']')
369
- begin
370
- val = JSON.parse(val)
371
- rescue
372
- Morpheus::Logging::DarkPrinter.puts "Failed to parse option value '#{val}' as JSON" if Morpheus::Logging.debug?
372
+ unless option_type['noParse']
373
+ if (val.to_s[0] == '{' && val.to_s[-1] == '}') || (val.to_s[0] == '[' && val.to_s[-1] == ']')
374
+ begin
375
+ val = JSON.parse(val)
376
+ rescue
377
+ Morpheus::Logging::DarkPrinter.puts "Failed to parse option value '#{val}' as JSON" if Morpheus::Logging.debug?
378
+ end
373
379
  end
374
380
  end
375
381
  custom_options[custom_option_args[0]] = val
@@ -1347,7 +1353,7 @@ module Morpheus
1347
1353
  if record.nil?
1348
1354
  # avoid double error render by exiting here, ew
1349
1355
  exit 1
1350
- raise_command_error "Storage Server not found for '#{val}'"
1356
+ raise_command_error "#{type.titleize} not found for '#{val}'"
1351
1357
  end
1352
1358
  params[param_name] = record['id']
1353
1359
  else
@@ -89,6 +89,8 @@ class Morpheus::Cli::ApplianceSettingsCommand
89
89
  # Currency Settings
90
90
  "Currency Provider" => lambda {|it| it['currencyProvider'] },
91
91
  "Currency Provider API Key" => lambda {|it| it['currencyKey'] },
92
+ # Retention Settings
93
+ "Stats Retainment Period" => lambda {|it| it['statsRetainmentPeriod'] ? it['statsRetainmentPeriod'].to_s + ' days' : '' },
92
94
  }
93
95
  print_description_list(description_cols, appliance_settings)
94
96
 
@@ -215,6 +217,9 @@ class Morpheus::Cli::ApplianceSettingsCommand
215
217
  opts.on("--disable-all-clouds", "Set all cloud types enabled status off, can be used in conjunction with --enable-clouds options") do
216
218
  params['disableAllZoneTypes'] = true
217
219
  end
220
+ opts.on("--stats-retainment-period DAYS", Integer, "Stats retainment period. The number of days stats should be available. Can be 30, 60, or 90.") do |val|
221
+ params['statsRetainmentPeriod'] = val.to_i
222
+ end
218
223
  build_common_options(opts, options, [:json, :payload, :dry_run, :quiet, :remote])
219
224
  end
220
225
 
@@ -781,11 +781,11 @@ class Morpheus::Cli::Apps
781
781
  print_h2 "#{app_tier['tier']['name']}", options
782
782
  print cyan
783
783
  tier_instances = (app_tier['appInstances'] || []).collect {|it| it['instance']}
784
- instances = tier_instances.collect { |tier_instance| instances.find { |i| i['id'] == tier_instance['id'] } }
785
- if instances.empty?
784
+ instance_list = tier_instances.collect { |tier_instance| instances.find { |i| i['id'] == tier_instance['id'] } }
785
+ if instance_list.empty?
786
786
  puts yellow, "This tier is empty", reset
787
787
  else
788
- instances_rows = instances.collect do |instance|
788
+ instances_rows = instance_list.collect do |instance|
789
789
  connection_string = ''
790
790
  if !instance['connectionInfo'].nil? && instance['connectionInfo'].empty? == false
791
791
  connection_string = "#{instance['connectionInfo'][0]['ip']}:#{instance['connectionInfo'][0]['port']}"
@@ -1020,7 +1020,7 @@ EOT
1020
1020
  end
1021
1021
 
1022
1022
  def apply(args)
1023
- default_refresh_interval = 10
1023
+ default_refresh_interval = 5
1024
1024
  params, payload, options = {}, {}, {}
1025
1025
  optparse = Morpheus::Cli::OptionParser.new do |opts|
1026
1026
  opts.banner = subcommand_usage("[app] [options]")
@@ -1381,9 +1381,15 @@ EOT
1381
1381
  opts.on( '--keep-backups', '--keep-backups', "Preserve copy of backups" ) do
1382
1382
  query_params[:keepBackups] = 'on'
1383
1383
  end
1384
- opts.on('--releaseEIPs [on|off]', ['on','off'], "Release EIPs. Default is on. Applies to Amazon only.") do |val|
1385
- query_params[:releaseEIPs] = val.nil? ? 'on' : val
1384
+ opts.on('--release-ips [on|off]', ['on','off'], "Release Floating IPs. Default is on. Applies to certain types only. Only applies when used with --remove-instances") do |val|
1385
+ query_params[:releaseFloatingIps] = val.nil? ? 'on' : val
1386
+ query_params[:releaseEIPs] = query_params[:releaseFloatingIps] # old parameter before 6.0
1386
1387
  end
1388
+ opts.on('--releaseEIPs [on|off]', ['on','off'], "Alias for Release Floating IPs") do |val|
1389
+ query_params[:releaseFloatingIps] = val.nil? ? 'on' : val
1390
+ query_params[:releaseEIPs] = query_params[:releaseFloatingIps] # old parameter before 6.0
1391
+ end
1392
+ opts.add_hidden_option('--releaseEIPs')
1387
1393
  opts.on( '-f', '--force', "Force Delete" ) do
1388
1394
  query_params[:force] = 'on'
1389
1395
  end
@@ -43,6 +43,12 @@ class Morpheus::Cli::CatalogItemTypesCommand
43
43
  opts.on('--all-labels LABEL', String, "Filter by labels, must match all of the values") do |val|
44
44
  add_query_parameter(params, 'allLabels', parse_labels(val))
45
45
  end
46
+ opts.on('--code CODE', String, "Filter by code" ) do |val|
47
+ params[:code] = val
48
+ end
49
+ opts.on('-c', '--category CATEGORY', String, "Filter by category") do |val|
50
+ add_query_parameter(params, 'category', val)
51
+ end
46
52
  build_standard_list_options(opts, options)
47
53
  opts.footer = "List catalog item types."
48
54
  end
@@ -212,6 +218,23 @@ EOT
212
218
  print reset,"(blank)","\n",reset
213
219
  end
214
220
  elsif item_type_code == 'workflow' || item_type_code == 'operationalworkflow' || item_type_code == 'taskset'
221
+ print_h2 "Workflow Config"
222
+ if catalog_item_type['workflowConfig']
223
+ #print reset,(JSON.pretty_generate(config) rescue config),"\n",reset
224
+ #print reset,(as_yaml(config, options) rescue config),"\n",reset
225
+ config_string = catalog_item_type['workflowConfig'] || ""
226
+ config_lines = config_string.split("\n")
227
+ config_line_count = config_lines.size
228
+ max_lines = 10
229
+ if config_lines.size > max_lines
230
+ config_string = config_lines.first(max_lines).join("\n")
231
+ config_string << "\n\n"
232
+ config_string << "#{dark}(#{(config_line_count - max_lines)} more lines were not shown, use -c to show the config)#{reset}"
233
+ end
234
+ print reset,config_string.chomp("\n"),"\n",reset
235
+ else
236
+ print reset,"(blank)","\n",reset
237
+ end
215
238
  end
216
239
  end
217
240
 
@@ -227,7 +250,7 @@ EOT
227
250
  end
228
251
 
229
252
  def add(args)
230
- options = {}
253
+ options = {:option_types => add_catalog_item_type_option_types}
231
254
  params = {}
232
255
  logo_file = nil
233
256
  dark_logo_file = nil
@@ -657,6 +680,8 @@ EOT
657
680
  {
658
681
  "ID" => 'id',
659
682
  "Name" => 'name',
683
+ "Code" => 'code',
684
+ "Category" => 'category',
660
685
  "Labels" => lambda {|it| format_list(it['labels'], '', 3) },
661
686
  "Description" => 'description',
662
687
  "Type" => lambda {|it| format_catalog_type(it) },
@@ -680,6 +705,8 @@ EOT
680
705
  "Name" => 'name',
681
706
  "Labels" => lambda {|it| format_list(it['labels']) },
682
707
  "Description" => 'description',
708
+ "Code" => 'code',
709
+ "Category" => 'category',
683
710
  "Type" => lambda {|it| format_catalog_type(it) },
684
711
  "Visibility" => 'visibility',
685
712
  "Layout Code" => 'layoutCode',
@@ -689,6 +716,7 @@ EOT
689
716
  # "Content" => lambda {|it| it['content'] },
690
717
  "Enabled" => lambda {|it| format_boolean(it['enabled']) },
691
718
  "Featured" => lambda {|it| format_boolean(it['featured']) },
719
+ "Allow Quantity" => lambda {|it| format_boolean(it['allowQuantity']) },
692
720
  #"Config" => lambda {|it| it['config'] },
693
721
  "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
694
722
  "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
@@ -725,21 +753,25 @@ EOT
725
753
  [
726
754
  {'code' => 'catalogItemType.type', 'shorthand' => '-t', 'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => [{'name' => 'Instance', 'value' => 'instance'}, {'name' => 'Blueprint', 'value' => 'blueprint'}, {'name' => 'Workflow', 'value' => 'workflow'}], 'defaultValue' => 'instance', 'required' => true, 'displayOrder' => 1},
727
755
  {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 2},
728
- {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'displayOrder' => 3},
729
- {'fieldName' => 'enabled', 'fieldLabel' => 'Enabled', 'type' => 'checkbox', 'defaultValue' => true, 'displayOrder' => 4},
730
- {'fieldName' => 'featured', 'fieldLabel' => 'Featured', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 5},
731
- {'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'type' => 'select', 'selectOptions' => [{'name' => 'Private', 'value' => 'private'}, {'name' => 'Public', 'value' => 'public'}], 'defaultValue' => 'private', 'required' => true, 'displayOrder' => 6},
732
- {'fieldName' => 'layoutCode', 'fieldLabel' => 'Layout Code', 'type' => 'text', 'required' => false, 'displayOrder' => 7},
733
- {'fieldName' => 'iconPath', 'fieldLabel' => 'Logo', 'type' => 'select', 'optionSource' => 'iconList', 'displayOrder' => 8},
756
+ {'fieldName' => 'code', 'fieldLabel' => 'Code', 'type' => 'text', 'displayOrder' => 3},
757
+ {'fieldName' => 'category', 'fieldLabel' => 'Category', 'type' => 'text', 'displayOrder' => 3.5},
758
+ {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'displayOrder' => 4},
759
+ {'fieldName' => 'enabled', 'fieldLabel' => 'Enabled', 'type' => 'checkbox', 'defaultValue' => true, 'displayOrder' => 5},
760
+ {'fieldName' => 'featured', 'fieldLabel' => 'Featured', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 6},
761
+ {'fieldName' => 'allowQuantity', 'fieldLabel' => 'Allow Quantity', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 7},
762
+ {'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'type' => 'select', 'selectOptions' => [{'name' => 'Private', 'value' => 'private'}, {'name' => 'Public', 'value' => 'public'}], 'defaultValue' => 'private', 'required' => true, 'displayOrder' => 8},
763
+ {'fieldName' => 'layoutCode', 'fieldLabel' => 'Layout Code', 'type' => 'text', 'required' => false, 'displayOrder' => 9},
764
+ {'fieldName' => 'iconPath', 'fieldLabel' => 'Logo', 'type' => 'select', 'optionSource' => 'iconList', 'displayOrder' => 10},
734
765
  #{'fieldName' => 'optionTypes', 'fieldLabel' => 'Option Types', 'type' => 'text', 'description' => 'Option Types to include, comma separated list of names or IDs.', 'displayOrder' => 8},
735
- {'dependsOnCode' => 'catalogItemType.type:instance', 'fieldName' => 'config', 'fieldLabel' => 'Config', 'type' => 'code-editor', 'description' => 'JSON or YAML', 'required' => true, 'displayOrder' => 9},
736
- {'dependsOnCode' => 'catalogItemType.type:blueprint', 'fieldName' => 'blueprint', 'fieldLabel' => 'Blueprint', 'type' => 'select', 'optionSource' => 'blueprints', 'description' => 'Choose a blueprint to apply to the catalog item.', 'required' => true, 'noParams' => true, 'displayOrder' => 10},
737
- {'dependsOnCode' => 'catalogItemType.type:blueprint', 'fieldName' => 'appSpec', 'fieldLabel' => 'App Spec', 'type' => 'code-editor', 'description' => 'Enter a spec in the for the App, the Scribe YAML format', 'required' => true, 'displayOrder' => 11},
738
- {'dependsOnCode' => 'catalogItemType.type:workflow', 'fieldName' => 'workflow', 'fieldLabel' => 'Workflow', 'type' => 'select', 'optionSource' => 'operationWorkflows', 'description' => 'Enter a spec in the for the App, the Scribe YAML format', 'noParams' => true, 'displayOrder' => 12},
766
+ {'dependsOnCode' => 'catalogItemType.type:instance', 'fieldName' => 'config', 'fieldLabel' => 'Config', 'type' => 'code-editor', 'description' => 'JSON or YAML', 'required' => true, 'displayOrder' => 11},
767
+ {'dependsOnCode' => 'catalogItemType.type:workflow', 'fieldName' => 'workflowConfig', 'fieldLabel' => 'Config', 'type' => 'textarea', 'description' => 'Enter configuration for the Workflow', 'required' => false, 'noParse' => true, 'displayOrder' => 11},
768
+ {'dependsOnCode' => 'catalogItemType.type:blueprint', 'fieldName' => 'blueprint', 'fieldLabel' => 'Blueprint', 'type' => 'select', 'optionSource' => 'blueprints', 'description' => 'Choose a blueprint to apply to the catalog item.', 'required' => true, 'noParams' => true, 'displayOrder' => 12},
769
+ {'dependsOnCode' => 'catalogItemType.type:blueprint', 'fieldName' => 'appSpec', 'fieldLabel' => 'App Spec', 'type' => 'code-editor', 'description' => 'Enter a spec in the for the App, the Scribe YAML format', 'required' => true, 'displayOrder' => 13},
770
+ {'dependsOnCode' => 'catalogItemType.type:workflow', 'fieldName' => 'workflow', 'fieldLabel' => 'Workflow', 'type' => 'select', 'optionSource' => 'operationWorkflows', 'description' => 'Enter a spec in the for the App, the Scribe YAML format', 'required' => true, 'noParams' => true, 'displayOrder' => 14},
739
771
  {'dependsOnCode' => 'catalogItemType.type:workflow', 'fieldName' => 'context', 'fieldLabel' => 'Context Type', 'type' => 'select', 'optionSource' => lambda { |api_client, api_params|
740
772
  [{'name' => "Select", 'value' => ""}, {'name' => "None", 'value' => "appliance"}, {'name' => "Instance", 'value' => "instance"}, {'name' => "Server", 'value' => "server"}]
741
773
  }, 'description' => 'Context for operational workflow, determines target type', 'defaultValue' => 'Select', 'required' => false},
742
- {'fieldName' => 'content', 'fieldLabel' => 'Content', 'type' => 'code-editor', 'description' => 'Wiki Page Content describing the catalog item', 'displayOrder' => 13}
774
+ {'fieldName' => 'content', 'fieldLabel' => 'Content', 'type' => 'code-editor', 'description' => 'Wiki Page Content describing the catalog item', 'displayOrder' => 15}
743
775
  ]
744
776
  end
745
777
 
@@ -33,6 +33,7 @@ class Morpheus::Cli::Clusters
33
33
  @security_groups_interface = @api_client.security_groups
34
34
  #@security_group_rules_interface = @api_client.security_group_rules
35
35
  @cloud_resource_pools_interface = @api_client.cloud_resource_pools
36
+ @resource_pool_groups_interface = @api_client.resource_pool_groups
36
37
  @clouds_interface = @api_client.clouds
37
38
  @servers_interface = @api_client.servers
38
39
  @server_types_interface = @api_client.server_types
@@ -644,6 +645,7 @@ class Morpheus::Cli::Clusters
644
645
  !type['enabled'] || !type['creatable'] || type['fieldComponent']
645
646
  } rescue []))
646
647
 
648
+
647
649
  # remove metadata option_type , prompt manually for that field 'tags' instead of 'metadata'
648
650
  metadata_option_type = option_type_list.find {|type| type['fieldName'] == 'metadata' }
649
651
  option_type_list = option_type_list.reject {|type| type['fieldName'] == 'metadata' }
@@ -665,6 +667,14 @@ class Morpheus::Cli::Clusters
665
667
  # Security Groups
666
668
  server_payload['securityGroups'] = prompt_security_groups_by_cloud(cloud, provision_type, resource_pool, options)
667
669
 
670
+ # KLUDGE part 2: need to ask for hauwei floating ip option
671
+ if option_type = option_type_list.find {|type| type['code'] == 'computeServerType.openstackLinux.selectFloatingIp'}
672
+ floating_ip = load_floating_options(cluster_payload, options)
673
+ if floating_ip != nil
674
+ server_payload['config']['osExternalNetworkId'] = floating_ip
675
+ end
676
+ end
677
+
668
678
  # Visibility
669
679
  server_payload['visibility'] = options[:visibility] || (Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'type' => 'select', 'defaultValue' => 'private', 'required' => true, 'selectOptions' => [{'name' => 'Private', 'value' => 'private'},{'name' => 'Public', 'value' => 'public'}]}], options[:options], @api_client, {})['visibility'])
670
680
 
@@ -3785,7 +3795,7 @@ class Morpheus::Cli::Clusters
3785
3795
  name: cluster['name'],
3786
3796
  type: (cluster['type']['name'] rescue ''),
3787
3797
  layout: (cluster['layout']['name'] rescue ''),
3788
- workers: cluster['workerCount'],
3798
+ workers: cluster['servers'].size,
3789
3799
  cloud: (cluster['zone']['name'] rescue ''),
3790
3800
  status: format_cluster_status(cluster)
3791
3801
  }
@@ -4273,7 +4283,11 @@ class Morpheus::Cli::Clusters
4273
4283
  else
4274
4284
  resource_pool_id = resource_pool_options.first['id']
4275
4285
  end
4276
- resource_pool = @cloud_resource_pools_interface.get(cloud['id'], resource_pool_id)['resourcePool']
4286
+ if resource_pool_id.to_s["poolGroup-"]
4287
+ resource_pool = @resource_pool_groups_interface.get(resource_pool_id)['resourcePoolGroup']
4288
+ else
4289
+ resource_pool = @cloud_resource_pools_interface.get(cloud['id'], resource_pool_id)['resourcePool']
4290
+ end
4277
4291
  end
4278
4292
  end
4279
4293
  end
@@ -4357,6 +4371,13 @@ class Morpheus::Cli::Clusters
4357
4371
  end
4358
4372
  end
4359
4373
 
4374
+ def load_floating_options(cluster, options)
4375
+ floating_ip_opts = @api_client.options.options_for_source('openstackFloatingIpOptions', {optionsSourceType: 'openStack', zoneId: cluster['cloud']['id']})['data']
4376
+ floating_ip_opts = floating_ip_opts.reject {|it| it['value'] == '' }
4377
+ floating_ip = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'osExternalNetworkId', 'fieldLabel' => 'Floating IP', 'type' => 'select', 'selectOptions' => floating_ip_opts, 'required' => false, 'description' => "Select Floating IP"}], options[:options])['osExternalNetworkId']
4378
+ return floating_ip
4379
+ end
4380
+
4360
4381
  def available_kube_templates
4361
4382
  option_results = options_interface.options_for_source('availableKubeTemplates')
4362
4383
  available_templates = option_results['data'].collect {|it|