morpheus-cli 5.5.3.2 → 6.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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|