morpheus-cli 8.1.2 → 9.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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +2 -2
  3. data/lib/morpheus/api/api_client.rb +8 -0
  4. data/lib/morpheus/api/clusters_interface.rb +24 -0
  5. data/lib/morpheus/api/groups_interface.rb +2 -1
  6. data/lib/morpheus/api/instances_interface.rb +1 -1
  7. data/lib/morpheus/api/options_interface.rb +1 -1
  8. data/lib/morpheus/api/servers_interface.rb +0 -1
  9. data/lib/morpheus/api/storage_volumes_interface.rb +8 -0
  10. data/lib/morpheus/api/support_bundles_interface.rb +46 -0
  11. data/lib/morpheus/api/systems_interface.rb +32 -0
  12. data/lib/morpheus/api/tokens_interface.rb +39 -0
  13. data/lib/morpheus/api/virtual_images_interface.rb +10 -2
  14. data/lib/morpheus/cli/cli_command.rb +6 -1
  15. data/lib/morpheus/cli/commands/backups_command.rb +9 -6
  16. data/lib/morpheus/cli/commands/clients_command.rb +60 -74
  17. data/lib/morpheus/cli/commands/clouds.rb +30 -2
  18. data/lib/morpheus/cli/commands/clusters.rb +77 -0
  19. data/lib/morpheus/cli/commands/execution_request_command.rb +6 -2
  20. data/lib/morpheus/cli/commands/groups.rb +46 -23
  21. data/lib/morpheus/cli/commands/hosts.rb +21 -14
  22. data/lib/morpheus/cli/commands/instances.rb +32 -6
  23. data/lib/morpheus/cli/commands/storage_volumes.rb +78 -3
  24. data/lib/morpheus/cli/commands/support_bundles_command.rb +606 -0
  25. data/lib/morpheus/cli/commands/systems.rb +606 -2
  26. data/lib/morpheus/cli/commands/tokens_command.rb +391 -0
  27. data/lib/morpheus/cli/commands/workflows.rb +16 -3
  28. data/lib/morpheus/cli/mixins/infrastructure_helper.rb +30 -14
  29. data/lib/morpheus/cli/mixins/provisioning_helper.rb +21 -7
  30. data/lib/morpheus/cli/version.rb +1 -1
  31. data/test/api/systems_interface_test.rb +26 -0
  32. data/test/cli/storage_volumes_test.rb +79 -0
  33. data/test/cli/systems_test.rb +206 -0
  34. metadata +12 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9b70de51756c24d66d525a95fa915fad33ba69bd53fe631dc160966830b1cd80
4
- data.tar.gz: ed8478518da781a70c3fda18cef5b07061b65c433cb73d16fd714128cb666ec5
3
+ metadata.gz: 1f2fdd08e0bf0dbe8567812778748a10d38dbb6efd64430fc50fdf1134528be3
4
+ data.tar.gz: 2219cdf94a929a790e75d727068567adb10d09a80c30971e96365726812d3334
5
5
  SHA512:
6
- metadata.gz: 1e7bc745768d69b29f5285a7c86d7390e83ced97e1db36cce9d686c771bda49ac832b48f724c6fe80059ed2d8e0bd7a304f4212bf2abf563ed66b1b440126c53
7
- data.tar.gz: fcb53e94eadb615e960ad1c6e6410b13d40ce8f45076a675a8bb3e4e37c3a0be31b2c4797106e3ac98c05f79981bd5d7bb9a6d80a4f3fee5ddf26155f31f21e9
6
+ metadata.gz: 7b276775f4746e397b8d4b05c3c09c04d9b02409c7511e6a40a3ac80a160642d376fb10a2bf78477bfe84f3b3fc9abc1680ca332307efe4c250ec2b478a961f3
7
+ data.tar.gz: 7046dcbfff992519d64e93c1fd1383b0bb1c3166dfad466d56661a2ee65e0b45038a9d94d4c7812ab423c285267347609337e0312fddeeae82fe5f001b67cc6e
data/Dockerfile CHANGED
@@ -1,5 +1,5 @@
1
1
  FROM ruby:2.7.5
2
2
 
3
- RUN gem install morpheus-cli -v 8.1.2
3
+ RUN gem install morpheus-cli -v 9.0.1
4
4
 
5
- ENTRYPOINT ["morpheus"]
5
+ ENTRYPOINT ["morpheus"]
@@ -1034,6 +1034,10 @@ class Morpheus::APIClient
1034
1034
  Morpheus::MigrationsInterface.new(common_interface_options).setopts(@options)
1035
1035
  end
1036
1036
 
1037
+ def tokens
1038
+ Morpheus::TokensInterface.new(common_interface_options).setopts(@options)
1039
+ end
1040
+
1037
1041
  def rest(endpoint)
1038
1042
  Morpheus::RestInterface.new(common_interface_options).setopts(@options.merge({base_path: "#{@base_url}/api/#{endpoint}"}))
1039
1043
  end
@@ -1048,6 +1052,10 @@ class Morpheus::APIClient
1048
1052
  end
1049
1053
  alias :get_interface :interface
1050
1054
 
1055
+ def support_bundles
1056
+ Morpheus::SupportBundlesInterface.new(common_interface_options).setopts(@options)
1057
+ end
1058
+
1051
1059
  # add new interfaces here
1052
1060
 
1053
1061
  protected
@@ -389,5 +389,29 @@ class Morpheus::ClustersInterface < Morpheus::APIClient
389
389
  headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
390
390
  execute(method: :delete, url: url, headers: headers)
391
391
  end
392
+
393
+ def available_updates(id, params={})
394
+ url = "#{base_path}/#{id}/available-updates"
395
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
396
+ execute(method: :get, url: url, headers: headers)
397
+ end
398
+
399
+ def list_updates(id, update_id, params={})
400
+ url = "#{base_path}/#{id}/available-updates"
401
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
402
+ execute(method: :get, url: url, headers: headers)
403
+ end
404
+
405
+ def get_update(id, update_id, params={})
406
+ url = "#{base_path}/#{id}/available-updates/#{update_id}"
407
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
408
+ execute(method: :get, url: url, headers: headers)
409
+ end
410
+
411
+ def execute_update(id, payload)
412
+ url = "#{base_path}/#{id}/execute-update"
413
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
414
+ execute(method: :post, url: url, headers: headers, payload: payload.to_json)
415
+ end
392
416
 
393
417
  end
@@ -9,7 +9,7 @@ class Morpheus::GroupsInterface < Morpheus::APIClient
9
9
  execute(opts)
10
10
  end
11
11
 
12
- def get(options=nil)
12
+ def get(options=nil, params={})
13
13
  url = "#{@base_url}/api/groups"
14
14
  headers = { params: {}, authorization: "Bearer #{@access_token}" }
15
15
 
@@ -17,6 +17,7 @@ class Morpheus::GroupsInterface < Morpheus::APIClient
17
17
  headers[:params].merge!(options)
18
18
  elsif options.is_a?(Numeric)
19
19
  url = "#{@base_url}/api/groups/#{options}"
20
+ headers[:params] = params
20
21
  elsif options.is_a?(String)
21
22
  headers[:params]['name'] = options
22
23
  end
@@ -296,7 +296,7 @@ class Morpheus::InstancesInterface < Morpheus::APIClient
296
296
  def service_plans(params={})
297
297
  url = "#{@base_url}/api/instances/service-plans"
298
298
  headers = { params: params, authorization: "Bearer #{@access_token}" }
299
- opts = {method: :get, url: url, headers: headers}
299
+ opts = {method: :get, url: url, headers: headers, timeout: 180}
300
300
  execute(opts)
301
301
  end
302
302
 
@@ -12,6 +12,6 @@ class Morpheus::OptionsInterface < Morpheus::APIClient
12
12
  url = "#{@base_url}/api/options/#{option_source_type}/#{source}"
13
13
  end
14
14
  headers = { params: params, authorization: "Bearer #{@access_token}" }
15
- execute(method: :get, url: url, headers: headers)
15
+ execute(method: :get, url: url, headers: headers, timeout: 180)
16
16
  end
17
17
  end
@@ -219,5 +219,4 @@ class Morpheus::ServersInterface < Morpheus::APIClient
219
219
  execute(opts)
220
220
  end
221
221
 
222
-
223
222
  end
@@ -13,4 +13,12 @@ class Morpheus::StorageVolumesInterface < Morpheus::RestInterface
13
13
  execute(opts)
14
14
  end
15
15
 
16
+ # Transfer ownership of a storage volume to another tenant.
17
+ # The server expects { "storageVolume": { "tenant": { "id": <id> } } }
18
+ # and handles all authorization and business-rule validation.
19
+ def update_tenant(id, payload, params={}, headers={})
20
+ validate_id!(id)
21
+ execute(method: :put, url: "#{base_path}/#{CGI::escape(id.to_s)}", params: params, payload: payload, headers: headers)
22
+ end
23
+
16
24
  end
@@ -0,0 +1,46 @@
1
+ require 'morpheus/api/rest_interface'
2
+
3
+ class Morpheus::SupportBundlesInterface < Morpheus::RestInterface
4
+
5
+ def base_path
6
+ "/api/support-bundles"
7
+ end
8
+
9
+ # Download uses chunked streaming to write directly to a file.
10
+ # Follows the same pattern as file_copy_request_interface.rb#download_file_chunked.
11
+ def download(id, outfile, params={})
12
+ raise "#{self.class}.download() passed a blank id!" if id.to_s == ''
13
+ url = "#{base_path}/#{CGI::escape(id.to_s)}/download"
14
+ headers = { params: params }
15
+ opts = {method: :get, url: url, headers: headers, parse_json: false}
16
+ if Dir.exist?(outfile)
17
+ raise "outfile is invalid. It is the name of an existing directory: #{outfile}"
18
+ end
19
+ if @dry_run
20
+ return execute(opts)
21
+ end
22
+ http_response = nil
23
+ begin
24
+ File.open(outfile, 'w') {|f|
25
+ block = proc { |response|
26
+ response.read_body do |chunk|
27
+ f.write chunk
28
+ end
29
+ }
30
+ opts[:block_response] = block
31
+ http_response = execute(opts)
32
+ }
33
+ rescue
34
+ if File.exist?(outfile) && File.file?(outfile)
35
+ File.delete(outfile)
36
+ end
37
+ raise
38
+ end
39
+ return http_response
40
+ end
41
+
42
+ def cancel(id, payload={}, params={}, headers={})
43
+ execute(method: :post, url: "#{base_path}/#{CGI::escape(id.to_s)}/cancel", params: params, payload: payload, headers: headers)
44
+ end
45
+
46
+ end
@@ -20,4 +20,36 @@ class Morpheus::SystemsInterface < Morpheus::RestInterface
20
20
  execute(method: :get, url: "#{base_path}/#{CGI::escape(id.to_s)}/validate", params: params, headers: headers)
21
21
  end
22
22
 
23
+ def list_compute_server_update_definitions(system_id, server_id, params={}, headers={})
24
+ execute(method: :get, url: "#{base_path}/#{CGI::escape(system_id.to_s)}/servers/#{CGI::escape(server_id.to_s)}/update-definitions", params: params, headers: headers)
25
+ end
26
+
27
+ def apply_compute_server_update_definition(system_id, server_id, update_definition_id, payload={}, params={}, headers={})
28
+ execute(method: :post, url: "#{base_path}/#{CGI::escape(system_id.to_s)}/servers/#{CGI::escape(server_id.to_s)}/update-definitions/#{CGI::escape(update_definition_id.to_s)}", params: params, payload: payload, headers: headers)
29
+ end
30
+
31
+ def list_storage_server_update_definitions(system_id, server_id, params={}, headers={})
32
+ execute(method: :get, url: "#{base_path}/#{CGI::escape(system_id.to_s)}/storage-servers/#{CGI::escape(server_id.to_s)}/update-definitions", params: params, headers: headers)
33
+ end
34
+
35
+ def apply_storage_server_update_definition(system_id, server_id, update_definition_id, payload={}, params={}, headers={})
36
+ execute(method: :post, url: "#{base_path}/#{CGI::escape(system_id.to_s)}/storage-servers/#{CGI::escape(server_id.to_s)}/update-definitions/#{CGI::escape(update_definition_id.to_s)}", params: params, payload: payload, headers: headers)
37
+ end
38
+
39
+ def list_network_server_update_definitions(system_id, server_id, params={}, headers={})
40
+ execute(method: :get, url: "#{base_path}/#{CGI::escape(system_id.to_s)}/network-servers/#{CGI::escape(server_id.to_s)}/update-definitions", params: params, headers: headers)
41
+ end
42
+
43
+ def apply_network_server_update_definition(system_id, server_id, update_definition_id, payload={}, params={}, headers={})
44
+ execute(method: :post, url: "#{base_path}/#{CGI::escape(system_id.to_s)}/network-servers/#{CGI::escape(server_id.to_s)}/update-definitions/#{CGI::escape(update_definition_id.to_s)}", params: params, payload: payload, headers: headers)
45
+ end
46
+
47
+ def list_cluster_update_definitions(system_id, cluster_id, params={}, headers={})
48
+ execute(method: :get, url: "#{base_path}/#{CGI::escape(system_id.to_s)}/clusters/#{CGI::escape(cluster_id.to_s)}/update-definitions", params: params, headers: headers)
49
+ end
50
+
51
+ def apply_cluster_update_definition(system_id, cluster_id, update_definition_id, payload={}, params={}, headers={})
52
+ execute(method: :post, url: "#{base_path}/#{CGI::escape(system_id.to_s)}/clusters/#{CGI::escape(cluster_id.to_s)}/update-definitions/#{CGI::escape(update_definition_id.to_s)}", params: params, payload: payload, headers: headers)
53
+ end
54
+
23
55
  end
@@ -0,0 +1,39 @@
1
+ require 'morpheus/api/api_client'
2
+
3
+ class Morpheus::TokensInterface < Morpheus::APIClient
4
+
5
+ def base_path
6
+ "/api/tokens"
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}/#{id}", params: params, headers: headers)
16
+ end
17
+
18
+ def create(payload, params={})
19
+ execute(method: :post, url: "#{base_path}", payload: payload, params: params)
20
+ end
21
+
22
+ # def update(id, payload, params={})
23
+ # execute(method: :put, url: "#{base_path}/#{id}", params: params, headers: headers)
24
+ # end
25
+
26
+ def destroy(id, params={})
27
+ validate_id!(id)
28
+ execute(method: :delete, url: "#{base_path}/#{id}", params: params)
29
+ end
30
+
31
+ def destroy_all(params={})
32
+ execute(method: :delete, url: "#{base_path}", params: params)
33
+ end
34
+
35
+ def available_clients(params={})
36
+ execute(method: :get, url: "#{base_path}/api-clients", params: params)
37
+ end
38
+
39
+ end
@@ -117,7 +117,11 @@ class Morpheus::VirtualImagesInterface < Morpheus::APIClient
117
117
  gz.close
118
118
  }
119
119
  http_opts[:body] = Morpheus::BodyIO.new(rd)
120
- response = http.post(url, http_opts)
120
+ if Gem::Version.new(HTTP::VERSION) >= Gem::Version.new("6.0.0")
121
+ response = http.post(url, **http_opts)
122
+ else
123
+ response = http.post(url, http_opts)
124
+ end
121
125
  else
122
126
  if @dry_run
123
127
  return {method: :post, url: url, headers: headers, params: query_params, payload: payload}
@@ -126,7 +130,11 @@ class Morpheus::VirtualImagesInterface < Morpheus::APIClient
126
130
  http = HTTP.headers(headers)
127
131
  http_opts[:params] = query_params
128
132
  http_opts[:body] = payload
129
- response = http.post(url, http_opts)
133
+ if Gem::Version.new(HTTP::VERSION) >= Gem::Version.new("6.0.0")
134
+ response = http.post(url, **http_opts)
135
+ else
136
+ response = http.post(url, http_opts)
137
+ end
130
138
  end
131
139
  # puts "Took #{Time.now.to_i - start_time.to_i}"
132
140
  # return response
@@ -1485,7 +1485,12 @@ module Morpheus
1485
1485
  # returns Array of subtitles as strings in the format ["Phrase: blah", "Max: 100"]
1486
1486
  def parse_list_subtitles(options={})
1487
1487
  subtitles = []
1488
- list_params = {}
1488
+ if options[:tenant]
1489
+ subtitles << "Tenant: #{options[:tenant]}".strip
1490
+ end
1491
+ if options[:include_tenants]
1492
+ subtitles << "Include Tenants: true".strip
1493
+ end
1489
1494
  [:phrase, :offset, :max, :sort, :direction, :lastUpdated].each do |k|
1490
1495
  if options.key?(k)
1491
1496
  subtitles << "#{k.to_s}: #{options[k]}"
@@ -96,6 +96,8 @@ EOT
96
96
  print_h1 "Backup Details", [], options
97
97
  print cyan
98
98
  columns = backup_column_definitions
99
+ columns.delete("Backup Provider") if backup['backupProvider'].nil?
100
+ columns.delete("Storage Provider") if backup['storageProvider'].nil?
99
101
  columns.delete("Instance") if backup['instance'].nil?
100
102
  columns.delete("Container ID") if backup['containerId'].nil?
101
103
  columns.delete("Host") if backup['server'].nil?
@@ -541,8 +543,9 @@ EOT
541
543
  {
542
544
  "ID" => 'id',
543
545
  "Name" => 'name',
544
- "Backup Type" => lambda {|it| format_backup_type_tag(it) },
545
- "Backup Location" => lambda {|it| format_backup_location_tag(it) },
546
+ "Backup Type" => lambda {|it| (it['backupType'] && it['backupType']['name']) ? it['backupType']['name'] : format_backup_type_tag(it) },
547
+ "Backup Provider" => lambda {|it| it['backupProvider']['name'] rescue '' },
548
+ "Storage Provider" => lambda {|it| it['storageProvider']['name'] rescue '' },
546
549
  "Location Type" => lambda {|it|
547
550
  if it['locationType'] == "instance"
548
551
  "Instance"
@@ -610,11 +613,11 @@ EOT
610
613
  def format_backup_location_tag(backup)
611
614
  # check storage provider or backup provider indicating remote storage
612
615
  if backup['storageProvider'] && backup['storageProvider']['id']
613
- provider_type = backup['storageProvider']['type'] || backup['storageProvider']['providerType'] || ''
614
- "#{green}REMOTE#{reset} (#{provider_type})"
616
+ provider_label = backup['storageProvider']['type'] || backup['storageProvider']['providerType'] || backup['storageProvider']['name'] || ''
617
+ "#{green}REMOTE#{reset} (#{provider_label})"
615
618
  elsif backup['backupProvider'] && backup['backupProvider']['id']
616
- provider_type = backup['backupProvider']['type'] || backup['backupProvider']['providerType'] || ''
617
- "#{green}REMOTE#{reset} (#{provider_type})"
619
+ provider_label = backup['backupProvider']['type'] || backup['backupProvider']['providerType'] || backup['backupProvider']['name'] || ''
620
+ "#{green}REMOTE#{reset} (#{provider_label})"
618
621
  else
619
622
  "#{blue}LOCAL#{reset}"
620
623
  end
@@ -30,41 +30,29 @@ class Morpheus::Cli::ClientsCommand
30
30
  end
31
31
  optparse.parse!(args)
32
32
  connect(options)
33
-
33
+ # verify_args!(args:args, optparse:optparse, count:0)
34
+ if args.count > 0
35
+ options[:phrase] = args.join(" ")
36
+ end
34
37
  params.merge!(parse_list_options(options))
35
38
  @clients_interface.setopts(options)
36
39
  if options[:dry_run]
37
40
  print_dry_run @clients_interface.dry.list(params)
38
41
  return 0
39
42
  end
40
-
41
43
  json_response = @clients_interface.list(params)
42
44
  render_response(json_response, options, "clients") do
43
- clients = json_response["clients"]
44
- if clients.empty?
45
- print cyan,"No clients found",reset,"\n"
46
- else
47
- rows = clients.collect {|client|
48
- row = {
49
- id: client['id'],
50
- client_id: client['clientId'],
51
- access_token_seconds: client['accessTokenValiditySeconds'],
52
- refresh_token_seconds: client['refreshTokenValiditySeconds']
53
- }
54
- row
55
- }
56
- columns = [:id, {:client_id => {:max_width => 50}}, :access_token_seconds, :refresh_token_seconds]
57
- print_h1 "Morpheus Clients", [], options
58
- print as_pretty_table(rows, columns, options)
59
- print reset
60
- print_results_pagination(json_response)
61
- end
45
+ clients = json_response['clients']
46
+ print_h1 "Morpheus Clients", [], options
47
+ print as_pretty_table(clients, client_columns.upcase_keys!, options)
48
+ print reset
49
+ print_results_pagination(json_response)
62
50
  print reset,"\n"
63
51
  end
64
- return 0, nil
65
52
  end
66
53
 
67
54
  def get(args)
55
+ params = {}
68
56
  options = {}
69
57
  optparse = Morpheus::Cli::OptionParser.new do |opts|
70
58
  opts.banner = subcommand_usage("[client]")
@@ -74,47 +62,28 @@ class Morpheus::Cli::ClientsCommand
74
62
 
75
63
  end
76
64
  optparse.parse!(args)
77
- if args.count < 1
78
- puts optparse
79
- exit 1
80
- end
65
+ verify_args!(args:args, optparse:optparse, count:1)
81
66
  connect(options)
82
- begin
83
- @clients_interface.setopts(options)
84
- if options[:dry_run]
85
- if args[0].to_s =~ /\A\d{1,}\Z/
86
- print_dry_run @clients_interface.dry.get(args[0])
87
- else
88
- print_dry_run @clients_interface.dry.list({name: args[0].to_s})
89
- end
90
- return 0
91
- end
92
- client = find_client_by_name_or_id(args[0])
67
+ id = args[0]
68
+ if id.to_s !~ /\A\d{1,}\Z/
69
+ client = find_client_by_client_id(id)
93
70
  return 1 if client.nil?
94
- json_response = {'client' => client}
95
- render_result = render_with_format(json_response, options, 'client')
96
- return 0 if render_result
97
-
98
- unless options[:quiet]
99
- print_h1 "Client Details"
100
- print cyan
101
- client_columns = {
102
- "ID" => 'id',
103
- "Client ID" => 'clientId',
104
- "Access Token Validity Seconds" => 'accessTokenValiditySeconds',
105
- "Refresh Token Validity Seconds" => 'refreshTokenValiditySeconds',
106
- # "Scopes" => lambda {|client| client['scopes'].join(", ")},
107
- "Redirect URI" => lambda {|client| client['redirectUris'].join(", ")}
108
- }
109
- print_description_list(client_columns, client)
110
- print reset,"\n"
111
-
112
- end
71
+ id = client['id']
72
+ else
73
+ id = id.to_i
74
+ end
75
+ @clients_interface.setopts(options)
76
+ if options[:dry_run]
77
+ print_dry_run @clients_interface.dry.get(id, params)
78
+ return
79
+ end
80
+ json_response = @clients_interface.get(id, params)
81
+ render_response(json_response, options, 'client') do
82
+ client = json_response['client']
83
+ print_h1 "Client Details", [], options
84
+ print cyan
85
+ print_description_list(client_columns, client)
113
86
  print reset,"\n"
114
- return 0
115
- rescue RestClient::Exception => e
116
- print_rest_exception(e, options)
117
- exit 1
118
87
  end
119
88
  end
120
89
 
@@ -151,6 +120,7 @@ class Morpheus::Cli::ClientsCommand
151
120
  payload.deep_merge!({'client' => passed_options}) unless passed_options.empty?
152
121
  # prompt for options
153
122
  params = Morpheus::Cli::OptionTypes.prompt(add_client_option_types, options[:options], @api_client, options[:params])
123
+ params.booleanize!
154
124
  if params['redirectUris'] && params['redirectUris'].is_a?(String)
155
125
  params['redirectUris'] = params['redirectUris'].split(',').collect {|it| it.strip}.reject {|it| it.empty?}
156
126
  end
@@ -182,7 +152,7 @@ class Morpheus::Cli::ClientsCommand
182
152
  options = {}
183
153
  optparse = Morpheus::Cli::OptionParser.new do |opts|
184
154
  opts.banner = subcommand_usage("[clientId] [options]")
185
- build_option_type_options(opts, options, client_option_types)
155
+ build_option_type_options(opts, options, update_client_option_types)
186
156
  build_standard_update_options(opts, options)
187
157
  opts.footer = "Update Oauth Client Record."
188
158
  end
@@ -212,8 +182,9 @@ class Morpheus::Cli::ClientsCommand
212
182
  # allow arbitrary -O options
213
183
  payload.deep_merge!({'client' => passed_options}) unless passed_options.empty?
214
184
  # prompt for options
215
- #params = Morpheus::Cli::OptionTypes.prompt(update_wiki_page_option_types, options[:options], @api_client, options[:params])
185
+ #params = Morpheus::Cli::OptionTypes.no_prompt(update_client_option_types, options[:options], @api_client, options[:params])
216
186
  params = passed_options
187
+ params.booleanize!
217
188
  if params['redirectUris'] && params['redirectUris'].is_a?(String)
218
189
  params['redirectUris'] = params['redirectUris'].split(',').collect {|it| it.strip}.reject {|it| it.empty?}
219
190
  end
@@ -325,21 +296,36 @@ class Morpheus::Cli::ClientsCommand
325
296
  end
326
297
  end
327
298
 
328
- def client_option_types
329
- [
330
- {'fieldName' => 'clientId', 'fieldLabel' => 'Client Id', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
331
- {'fieldName' => 'accessTokenValiditySeconds', 'fieldLabel' => 'Access Token Validity Length (Seconds)', 'type' => 'number', 'required' => true, 'defaultValue' => 43200,'displayOrder' => 2},
332
- {'fieldName' => 'refreshTokenValiditySeconds', 'fieldLabel' => 'Refresh Token Validity Length (Seconds)', 'type' => 'number', 'required' => true, 'defaultValue' => 43200,'displayOrder' => 3}
333
- ]
299
+ def client_columns
300
+ {
301
+ "ID" => lambda {|it| it['id'] },
302
+ "Client ID" => lambda {|it| it['clientId'] },
303
+ "TTL" => lambda {|it| it['accessTokenValiditySeconds'] },
304
+ "Refresh TTL" => lambda {|it| it['refreshTokenValiditySeconds'] },
305
+ "Redirect URI" => lambda {|client| client['redirectUris'].join(", ")},
306
+ "Requires Consent" => lambda {|it| format_boolean it['requireConsent']}
307
+ }
334
308
  end
335
309
 
336
- def add_client_option_types
310
+
311
+ def add_client_option_types
337
312
  [
338
- {'fieldName' => 'clientId', 'fieldLabel' => 'Client Id', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
339
- {'fieldName' => 'clientSecret', 'fieldLabel' => 'Client Secret', 'type' => 'text', 'displayOrder' => 2},
340
- {'fieldName' => 'accessTokenValiditySeconds', 'fieldLabel' => 'Access Token Validity Length (Seconds)', 'type' => 'number', 'required' => true,'defaultValue' => 43200, 'displayOrder' => 3},
341
- {'fieldName' => 'refreshTokenValiditySeconds', 'fieldLabel' => 'Refresh Token Validity Length (Seconds)', 'type' => 'number', 'required' => true,'defaultValue' => 43200, 'displayOrder' => 4},
342
- {'fieldName' => 'redirectUris', 'fieldLabel' => 'Redirect URI', 'type' => 'text', 'displayOrder' => 5}
313
+ {'fieldName' => 'clientId', 'fieldLabel' => 'Client Id', 'type' => 'text', 'required' => true},
314
+ {'fieldName' => 'clientSecret', 'fieldLabel' => 'Client Secret', 'type' => 'text'},
315
+ {'switch' => 'ttl', 'fieldName' => 'accessTokenValiditySeconds', 'fieldLabel' => 'TTL (Seconds)', 'type' => 'number', 'required' => true,'defaultValue' => 43200, 'description' => 'Access Token Validity Seconds'},
316
+ {'switch' => 'refresh-ttl', 'fieldName' => 'refreshTokenValiditySeconds', 'fieldLabel' => 'Refresh TTL (Seconds)', 'type' => 'number', 'required' => true,'defaultValue' => 43200, 'description' => 'Refresh Token Validity Seconds'},
317
+ {'fieldName' => 'redirectUris', 'fieldLabel' => 'Redirect URI', 'type' => 'text', 'description' => 'Redirect URI(s), use commas to delimit values.'},
318
+ {'fieldName' => 'requireConsent', 'fieldLabel' => 'Requires Consent', 'type' => 'checkbox', 'defaultValue' => false}
343
319
  ]
344
320
  end
321
+
322
+ def update_client_option_types
323
+ option_types = add_client_option_types #.select {|it| ['clientId', 'clientSecret', 'accessTokenValiditySeconds'].include?(it['fieldName']) }
324
+ option_types.each {|it|
325
+ it.delete('required')
326
+ it.delete('defaultValue')
327
+ it.delete('skipSingleOption')
328
+ }
329
+ option_types
330
+ end
345
331
  end
@@ -2,6 +2,7 @@ require 'morpheus/cli/cli_command'
2
2
 
3
3
  class Morpheus::Cli::Clouds
4
4
  include Morpheus::Cli::CliCommand
5
+ include Morpheus::Cli::AccountsHelper
5
6
  include Morpheus::Cli::InfrastructureHelper
6
7
  include Morpheus::Cli::ProvisioningHelper
7
8
  include Morpheus::Cli::WhoamiHelper
@@ -25,6 +26,8 @@ class Morpheus::Cli::Clouds
25
26
  @api_client = establish_remote_appliance_connection(opts)
26
27
  @clouds_interface = @api_client.clouds
27
28
  @groups_interface = @api_client.groups
29
+ @accounts_interface = @api_client.accounts
30
+ @account_users_interface = @api_client.account_users
28
31
  @active_group_id = Morpheus::Cli::Groups.active_groups[@appliance_name]
29
32
  end
30
33
 
@@ -49,6 +52,13 @@ class Morpheus::Cli::Clouds
49
52
  opts.on('--all-labels LABEL', String, "Filter by labels, must match all of the values") do |val|
50
53
  add_query_parameter(params, 'allLabels', parse_labels(val))
51
54
  end
55
+ opts.on('--include-tenants','--include-tenants', "Include sub tenant clouds") do
56
+ options[:include_tenants] = true
57
+ params['includeTenants'] = true
58
+ end
59
+ opts.on('--tenant TENANT', String, "Tenant Name or ID" ) do |val|
60
+ options[:tenant] = val
61
+ end
52
62
  build_standard_list_options(opts, options)
53
63
  opts.footer = "List clouds."
54
64
  end
@@ -69,7 +79,18 @@ class Morpheus::Cli::Clouds
69
79
  params['groupId'] = group['id']
70
80
  end
71
81
  end
72
-
82
+ if options[:tenant]
83
+ if options[:tenant].to_s !~ /\A\d{1,}\Z/
84
+ account = find_account_by_name_or_id(options[:tenant])
85
+ if account.nil?
86
+ return 1, "Tenant not found by name: #{options[:tenant]}"
87
+ else
88
+ params['tenantId'] = account['id']
89
+ end
90
+ else
91
+ params['tenantId'] = options[:tenant]
92
+ end
93
+ end
73
94
  params.merge!(parse_list_options(options))
74
95
  @clouds_interface.setopts(options)
75
96
  if options[:dry_run]
@@ -94,6 +115,9 @@ class Morpheus::Cli::Clouds
94
115
  print cyan,"No clouds found.",reset,"\n"
95
116
  else
96
117
  columns = cloud_list_column_definitions(options).upcase_keys!
118
+ if !options[:include_tenants] && !options[:tenant]
119
+ columns.delete("Tenant")
120
+ end
97
121
  print as_pretty_table(clouds, columns, options)
98
122
  print_results_pagination(json_response)
99
123
  end
@@ -141,6 +165,9 @@ class Morpheus::Cli::Clouds
141
165
  params = {}
142
166
  optparse = Morpheus::Cli::OptionParser.new do |opts|
143
167
  opts.banner = subcommand_usage("[name]")
168
+ opts.on('--include-tenants','--include-tenants', "Include sub tenant clouds when finding cloud by name") do
169
+ options[:include_tenants] = true
170
+ end
144
171
  build_standard_list_options(opts, options)
145
172
  opts.footer = "Get details about a cloud.\n" +
146
173
  "[name] is required. This is the name or id of a cloud."
@@ -158,7 +185,7 @@ class Morpheus::Cli::Clouds
158
185
  def _get(id, params, options={})
159
186
  cloud = nil
160
187
  if id.to_s !~ /\A\d{1,}\Z/
161
- cloud = find_cloud_by_name_or_id(id)
188
+ cloud = find_cloud_by_name_or_id(id, options[:include_tenants])
162
189
  id = cloud['id']
163
190
  end
164
191
  @clouds_interface.setopts(options)
@@ -1481,6 +1508,7 @@ EOT
1481
1508
  {
1482
1509
  "ID" => 'id',
1483
1510
  "Name" => 'name',
1511
+ "Tenant" => lambda {|it| it['owner'] ? it['owner']['name'] : '' },
1484
1512
  "Type" => lambda {|it| it['zoneType'] ? it['zoneType']['name'] : '' },
1485
1513
  "Labels" => lambda {|it| format_list(it['labels'], '', 3) rescue '' },
1486
1514
  "Location" => 'location',