morpheus-cli 4.1.8 → 4.1.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +24 -0
  4. data/lib/morpheus/api/{old_cypher_interface.rb → budgets_interface.rb} +10 -11
  5. data/lib/morpheus/api/cloud_datastores_interface.rb +7 -0
  6. data/lib/morpheus/api/cloud_resource_pools_interface.rb +2 -2
  7. data/lib/morpheus/api/cypher_interface.rb +18 -12
  8. data/lib/morpheus/api/health_interface.rb +72 -0
  9. data/lib/morpheus/api/instances_interface.rb +1 -1
  10. data/lib/morpheus/api/library_instance_types_interface.rb +7 -0
  11. data/lib/morpheus/api/log_settings_interface.rb +6 -0
  12. data/lib/morpheus/api/network_security_servers_interface.rb +30 -0
  13. data/lib/morpheus/api/price_sets_interface.rb +42 -0
  14. data/lib/morpheus/api/prices_interface.rb +68 -0
  15. data/lib/morpheus/api/provisioning_settings_interface.rb +29 -0
  16. data/lib/morpheus/api/servers_interface.rb +1 -1
  17. data/lib/morpheus/api/service_plans_interface.rb +34 -11
  18. data/lib/morpheus/api/task_sets_interface.rb +8 -0
  19. data/lib/morpheus/api/tasks_interface.rb +8 -0
  20. data/lib/morpheus/cli.rb +6 -3
  21. data/lib/morpheus/cli/appliance_settings_command.rb +13 -5
  22. data/lib/morpheus/cli/approvals_command.rb +1 -1
  23. data/lib/morpheus/cli/apps.rb +88 -28
  24. data/lib/morpheus/cli/backup_settings_command.rb +1 -1
  25. data/lib/morpheus/cli/blueprints_command.rb +2 -0
  26. data/lib/morpheus/cli/budgets_command.rb +672 -0
  27. data/lib/morpheus/cli/cli_command.rb +13 -2
  28. data/lib/morpheus/cli/cli_registry.rb +1 -0
  29. data/lib/morpheus/cli/clusters.rb +40 -274
  30. data/lib/morpheus/cli/commands/standard/benchmark_command.rb +114 -66
  31. data/lib/morpheus/cli/commands/standard/coloring_command.rb +12 -0
  32. data/lib/morpheus/cli/commands/standard/curl_command.rb +31 -6
  33. data/lib/morpheus/cli/commands/standard/echo_command.rb +8 -3
  34. data/lib/morpheus/cli/commands/standard/set_prompt_command.rb +1 -1
  35. data/lib/morpheus/cli/containers_command.rb +37 -24
  36. data/lib/morpheus/cli/cypher_command.rb +191 -150
  37. data/lib/morpheus/cli/health_command.rb +903 -0
  38. data/lib/morpheus/cli/hosts.rb +43 -32
  39. data/lib/morpheus/cli/instances.rb +119 -68
  40. data/lib/morpheus/cli/jobs_command.rb +1 -1
  41. data/lib/morpheus/cli/library_instance_types_command.rb +61 -11
  42. data/lib/morpheus/cli/library_option_types_command.rb +2 -2
  43. data/lib/morpheus/cli/log_settings_command.rb +46 -3
  44. data/lib/morpheus/cli/logs_command.rb +24 -17
  45. data/lib/morpheus/cli/mixins/accounts_helper.rb +2 -0
  46. data/lib/morpheus/cli/mixins/logs_helper.rb +73 -19
  47. data/lib/morpheus/cli/mixins/print_helper.rb +29 -1
  48. data/lib/morpheus/cli/mixins/provisioning_helper.rb +554 -96
  49. data/lib/morpheus/cli/mixins/whoami_helper.rb +13 -1
  50. data/lib/morpheus/cli/networks_command.rb +3 -0
  51. data/lib/morpheus/cli/option_types.rb +83 -53
  52. data/lib/morpheus/cli/price_sets_command.rb +543 -0
  53. data/lib/morpheus/cli/prices_command.rb +669 -0
  54. data/lib/morpheus/cli/processes_command.rb +0 -2
  55. data/lib/morpheus/cli/provisioning_settings_command.rb +237 -0
  56. data/lib/morpheus/cli/remote.rb +9 -4
  57. data/lib/morpheus/cli/reports_command.rb +10 -4
  58. data/lib/morpheus/cli/roles.rb +93 -38
  59. data/lib/morpheus/cli/security_groups.rb +10 -0
  60. data/lib/morpheus/cli/service_plans_command.rb +736 -0
  61. data/lib/morpheus/cli/tasks.rb +220 -8
  62. data/lib/morpheus/cli/tenants_command.rb +3 -16
  63. data/lib/morpheus/cli/users.rb +2 -25
  64. data/lib/morpheus/cli/version.rb +1 -1
  65. data/lib/morpheus/cli/whitelabel_settings_command.rb +18 -18
  66. data/lib/morpheus/cli/whoami.rb +28 -10
  67. data/lib/morpheus/cli/workflows.rb +488 -36
  68. data/lib/morpheus/formatters.rb +22 -0
  69. data/morpheus-cli.gemspec +1 -0
  70. metadata +28 -5
  71. data/lib/morpheus/cli/accounts.rb +0 -335
  72. data/lib/morpheus/cli/old_cypher_command.rb +0 -412
@@ -69,7 +69,6 @@ class Morpheus::Cli::Processes
69
69
  connect(options)
70
70
  begin
71
71
  params.merge!(parse_list_options(options))
72
- # params[:query] = params.delete(:phrase) unless params[:phrase].nil?
73
72
  @processes_interface.setopts(options)
74
73
  if options[:dry_run]
75
74
  print_dry_run @processes_interface.dry.list(params)
@@ -201,7 +200,6 @@ class Morpheus::Cli::Processes
201
200
  begin
202
201
  process_id = args[0]
203
202
  params.merge!(parse_list_options(options))
204
- params[:query] = params.delete(:phrase) unless params[:phrase].nil?
205
203
  @processes_interface.setopts(options)
206
204
  if options[:dry_run]
207
205
  print_dry_run @processes_interface.dry.get(process_id, params)
@@ -0,0 +1,237 @@
1
+ require 'morpheus/cli/cli_command'
2
+
3
+ class Morpheus::Cli::ProvisioningSettingsCommand
4
+ include Morpheus::Cli::CliCommand
5
+ include Morpheus::Cli::AccountsHelper
6
+ include Morpheus::Cli::WhoamiHelper
7
+
8
+ set_command_name :'provisioning-settings'
9
+
10
+ register_subcommands :get, :update
11
+
12
+ set_default_subcommand :get
13
+
14
+ def connect(opts)
15
+ @api_client = establish_remote_appliance_connection(opts)
16
+ @provisioning_settings_interface = @api_client.provisioning_settings
17
+ @storage_providers_interface = @api_client.storage_providers
18
+ @key_pairs_interface = @api_client.key_pairs
19
+ @blueprints_interface = @api_client.blueprints
20
+ end
21
+
22
+ def handle(args)
23
+ handle_subcommand(args)
24
+ end
25
+
26
+ def get(args)
27
+ options = {}
28
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
29
+ opts.banner = subcommand_usage()
30
+ build_common_options(opts, options, [:query, :json, :yaml, :csv, :fields, :dry_run, :remote])
31
+ opts.footer = "Get provisioning settings."
32
+ end
33
+ optparse.parse!(args)
34
+ connect(options)
35
+ if args.count != 0
36
+ raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args}\n#{optparse}"
37
+ return 1
38
+ end
39
+
40
+ begin
41
+ @provisioning_settings_interface.setopts(options)
42
+
43
+ if options[:dry_run]
44
+ print_dry_run @provisioning_settings_interface.dry.get()
45
+ return
46
+ end
47
+ json_response = @provisioning_settings_interface.get()
48
+ if options[:json]
49
+ puts as_json(json_response, options, "provisioningSettings")
50
+ return 0
51
+ elsif options[:yaml]
52
+ puts as_yaml(json_response, options, "provisioningSettings")
53
+ return 0
54
+ elsif options[:csv]
55
+ puts records_as_csv([json_response['provisioningSettings']], options)
56
+ return 0
57
+ end
58
+
59
+ settings = json_response['provisioningSettings']
60
+
61
+ print_h1 "Provisioning Settings"
62
+ print cyan
63
+
64
+ description_cols = {
65
+ "Allow Cloud Selection" => lambda {|it| format_boolean(it['allowZoneSelection'])},
66
+ "Allow Host Selection" => lambda {|it| format_boolean(it['allowServerSelection'])},
67
+ "Require Environment Selection" => lambda {|it| format_boolean(it['requireEnvironments'])},
68
+ "Show Pricing" => lambda {|it| format_boolean(it['showPricing'])},
69
+ "Hide Datastore Stats On Selection" => lambda {|it| format_boolean(it['hideDatastoreStats'])},
70
+ "Cross-Tenant Naming Policies" => lambda {|it| format_boolean(it['crossTenantNamingPolicies'])},
71
+ "Reuse Naming Sequence Numbers" => lambda {|it| format_boolean(it['reuseSequence'])},
72
+ "Deployment Archive Store" => lambda {|it| it['deployStorageProvider'] ? it['deployStorageProvider']['name'] : nil},
73
+ # Cloud-Init Settings
74
+ "Cloud-Init Username" => lambda {|it| it['cloudInitUsername']},
75
+ "Cloud-Init Password" => lambda {|it| it['cloudInitPassword']},
76
+ "Cloud-Init Key Pair" => lambda {|it| it['cloudInitKeyPair'] ? it['cloudInitKeyPair']['name'] : nil},
77
+ # Windows Settings
78
+ "Windows Adminstrator Password" => lambda {|it| it['windowsPassword']},
79
+ # PXE Boot Settings
80
+ "Default Root Password" => lambda {|it| it['pxeRootPassword']},
81
+ # App Blueprint Settings
82
+ "Default Blueprint Type" => lambda {|it| it['defaultTemplateType'] ? it['defaultTemplateType']['name'].capitalize : 'Morpheus'}
83
+ }
84
+ print_description_list(description_cols, settings)
85
+ print reset "\n"
86
+ return 0
87
+ rescue RestClient::Exception => e
88
+ print_rest_exception(e, options)
89
+ return 1
90
+ end
91
+ end
92
+
93
+ def update(args)
94
+ options = {}
95
+ params = {}
96
+
97
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
98
+ opts.banner = opts.banner = subcommand_usage()
99
+ opts.on("--allow-cloud [on|off]", ['on','off'], "Allow cloud selection. Default is on") do |val|
100
+ params['allowZoneSelection'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
101
+ end
102
+ opts.on("--allow-host [on|off]", ['on','off'], "Allow host selection. Default is on") do |val|
103
+ params['allowServerSelection'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
104
+ end
105
+ opts.on("--require-env [on|off]", ['on','off'], "Require environment selection. Default is on") do |val|
106
+ params['requireEnvironments'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
107
+ end
108
+ opts.on("--show-pricing [on|off]", ['on','off'], "Show pricing. Default is on") do |val|
109
+ params['showPricing'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
110
+ end
111
+ opts.on("--ds-hide-stats [on|off]", ['on','off'], "Hide datastore stats on selection. Default is on") do |val|
112
+ params['hideDatastoreStats'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
113
+ end
114
+ opts.on("--x-tenant-naming [on|off]", ['on','off'], "Cross-tenant naming policies. Default is on") do |val|
115
+ params['crossTenantNamingPolicies'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
116
+ end
117
+ opts.on("--reuse-name-seq [on|off]", ['on','off'], "Reuse naming sequence numbers. Default is on") do |val|
118
+ params['reuseSequence'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
119
+ end
120
+ opts.on("--deploy-bucket BUCKET", String, "Deployment archive storage provider ID or name") do |val|
121
+ if val == 'null'
122
+ params['deployStorageProvider'] = nil
123
+ else
124
+ options[:deployBucket] = val
125
+ end
126
+ end
127
+ opts.on("--cloud-username STRING", String, "Cloud-init username") do |val|
128
+ params['cloudInitUsername'] = val == 'null' ? nil : val
129
+ end
130
+ opts.on("--cloud-pwd STRING", String, "Cloud-init password") do |val|
131
+ params['cloudInitPassword'] = val == 'null' ? nil : val
132
+ end
133
+ opts.on("--cloud-keypair KEYPAIR", String, "Cloud-init key pair ID or name") do |val|
134
+ if val == 'null'
135
+ params['cloudInitKeyPair'] = nil
136
+ else
137
+ options[:cloudKeyPair] = val
138
+ end
139
+ end
140
+ opts.on("--windows-pwd STRING", String, "Windows administrator password") do |val|
141
+ params['windowsPassword'] = val == 'null' ? nil : val
142
+ end
143
+ opts.on("--pxe-pwd STRING", String, "PXE Boot default root password") do |val|
144
+ params['pxeRootPassword'] = val == 'null' ? nil : val
145
+ end
146
+ opts.on("--blueprint-type TYPE", String, "Default blueprint type ID, name or code") do |val|
147
+ if val == 'null'
148
+ params['defaultTemplateType'] = nil
149
+ else
150
+ options[:blueprintType] = val
151
+ end
152
+ end
153
+ build_common_options(opts, options, [:json, :payload, :dry_run, :quiet, :remote])
154
+ end
155
+
156
+ optparse.parse!(args)
157
+ connect(options)
158
+ if args.count != 0
159
+ raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args}\n#{optparse}"
160
+ return 1
161
+ end
162
+
163
+ begin
164
+ payload = parse_payload(options)
165
+
166
+ if !payload
167
+ if options[:deployBucket]
168
+ bucket = find_storage_provider(options[:deployBucket])
169
+
170
+ if !bucket
171
+ print_red_alert "Storage provider #{options[:deployBucket]} not found"
172
+ exit 1
173
+ end
174
+ params['deployStorageProvider'] = {'id' => bucket['id']}
175
+ end
176
+
177
+ if options[:cloudKeyPair]
178
+ key_pair = find_key_pair(options[:cloudKeyPair])
179
+
180
+ if !key_pair
181
+ print_red_alert "Key pair #{options[:cloudKeyPair]} not found"
182
+ exit 1
183
+ end
184
+ params['cloudInitKeyPair'] = {'id' => key_pair['id']}
185
+ end
186
+
187
+ if options[:blueprintType]
188
+ template_type = find_template_type(options[:blueprintType])
189
+
190
+ if !template_type
191
+ print_red_alert "Blueprint type #{options[:blueprintType]} not found"
192
+ end
193
+ params['defaultTemplateType'] = {'id' => template_type['id']}
194
+ end
195
+ payload = {'provisioningSettings' => params}
196
+ end
197
+
198
+ @provisioning_settings_interface.setopts(options)
199
+ if options[:dry_run]
200
+ print_dry_run @provisioning_settings_interface.dry.update(payload)
201
+ return
202
+ end
203
+ json_response = @provisioning_settings_interface.update(payload)
204
+
205
+ if options[:json]
206
+ puts as_json(json_response, options)
207
+ elsif !options[:quiet]
208
+ if json_response['success']
209
+ print_green_success "Updated provisioning settings"
210
+ get([] + (options[:remote] ? ["-r",options[:remote]] : []))
211
+ else
212
+ print_red_alert "Error updating provisioning settings: #{json_response['msg'] || json_response['errors']}"
213
+ end
214
+ end
215
+ return 0
216
+
217
+ rescue RestClient::Exception => e
218
+ print_rest_exception(e, options)
219
+ exit 1
220
+ end
221
+ end
222
+
223
+ private
224
+
225
+ def find_storage_provider(val)
226
+ (val.to_s =~ /\A\d{1,}\Z/) ? @storage_providers_interface.get(val.to_i)['storageBucket'] : @storage_providers_interface.list({'name' => val})["storageBuckets"].first
227
+ end
228
+
229
+ def find_key_pair(val)
230
+ (val.to_s =~ /\A\d{1,}\Z/) ? @key_pairs_interface.get(current_account['id'], val.to_i)['keyPair'] : @key_pairs_interface.list(current_account['id'], {'name' => val})["keyPairs"].first
231
+ end
232
+
233
+ def find_template_type(val)
234
+ template_types = @provisioning_settings_interface.template_types['templateTypes']
235
+ (val.to_s =~ /\A\d{1,}\Z/) ? template_types.find {|it| it['id'] == val.to_i} : template_types.find {|it| it['name'].casecmp(val) == 0 || it['code'].casecmp(val) == 0}
236
+ end
237
+ end
@@ -133,7 +133,7 @@ EOT
133
133
  opts.on(nil, "--insecure", "Allow insecure HTTPS communication. i.e. Ignore SSL errors.") do
134
134
  secure = false
135
135
  end
136
- build_common_options(opts, options, [:quiet])
136
+ build_common_options(opts, options, [:options, :quiet])
137
137
  opts.footer = <<-EOT
138
138
  This will add a new remote appliance to your morpheus client configuration.
139
139
  If this is your first remote, --use is automatically applied so
@@ -233,7 +233,7 @@ EOT
233
233
  # hit check api and store version and other info
234
234
  if !options[:quiet]
235
235
  print cyan
236
- puts "Inspecting remote appliance url: #{appliance[:host]} ..."
236
+ puts "Inspecting remote appliance #{appliance[:host]} ..."
237
237
  end
238
238
  appliance, check_json_response = ::Morpheus::Cli::Remote.refresh_remote(new_appliance_name.to_sym)
239
239
  if !options[:quiet]
@@ -260,6 +260,11 @@ EOT
260
260
  return exit_code, err
261
261
  end
262
262
 
263
+ # just skip setup/login stuff is no prompt -N is used.
264
+ if options[:no_prompt]
265
+ return exit_code, err
266
+ end
267
+
263
268
  # check_cmd_result = check_appliance([new_appliance_name, "--quiet"])
264
269
  # check_cmd_result = check_appliance([new_appliance_name])
265
270
 
@@ -1299,9 +1304,9 @@ EOT
1299
1304
  elsif status_str == "ready"
1300
1305
  out << "#{green}#{status_str.upcase}#{return_color}"
1301
1306
  elsif status_str == "http-error"
1302
- out << "#{red}#{status_str.upcase}#{return_color}"
1307
+ out << "#{red}HTTP ERROR#{return_color}"
1303
1308
  elsif ['error', 'net-error', 'ssl-error', 'http-timeout', 'unreachable', 'unrecognized'].include?(status_str)
1304
- out << "#{red}#{status_str.upcase}#{return_color}"
1309
+ out << "#{red}#{status_str.gsub('-', ' ').upcase}#{return_color}"
1305
1310
  else
1306
1311
  # dunno
1307
1312
  out << "#{yellow}#{status_str.upcase}#{return_color}"
@@ -17,6 +17,10 @@ class Morpheus::Cli::ReportsCommand
17
17
 
18
18
  register_subcommands :list, :get, :run, :view, :export, :remove, :types
19
19
 
20
+ def default_refresh_interval
21
+ 5
22
+ end
23
+
20
24
  def handle(args)
21
25
  handle_subcommand(args)
22
26
  end
@@ -29,14 +33,13 @@ class Morpheus::Cli::ReportsCommand
29
33
  opts.on( '--type CODE', String, "Report Type code(s)" ) do |val|
30
34
  params['reportType'] = val.to_s.split(",").compact.collect {|it| it.strip }
31
35
  end
32
- build_common_options(opts, options, [:list, :json, :dry_run, :remote])
36
+ build_common_options(opts, options, [:list, :query, :json, :dry_run, :remote])
33
37
  opts.footer = "List report history."
34
38
  end
35
39
  optparse.parse!(args)
36
40
  connect(options)
37
41
  begin
38
42
  params.merge!(parse_list_options(options))
39
-
40
43
  @reports_interface.setopts(options)
41
44
  if options[:dry_run]
42
45
  print_dry_run @reports_interface.dry.list(params)
@@ -268,6 +271,10 @@ class Morpheus::Cli::ReportsCommand
268
271
 
269
272
  # Report Types tell us what the available filters are...
270
273
  report_option_types = report_type['optionTypes'] || []
274
+ report_option_types = report_option_types.collect {|it|
275
+ it['fieldContext'] = nil
276
+ it
277
+ }
271
278
  # pluck out optionTypes like the UI does..
272
279
  metadata_option_type = nil
273
280
  if report_option_types.find {|it| it['fieldName'] == 'metadata' }
@@ -275,6 +282,7 @@ class Morpheus::Cli::ReportsCommand
275
282
  end
276
283
 
277
284
  v_prompt = Morpheus::Cli::OptionTypes.prompt(report_option_types, options[:options], @api_client)
285
+ payload.deep_merge!({'report' => v_prompt}) unless v_prompt.empty?
278
286
 
279
287
  if metadata_option_type
280
288
  if !options[:options]['metadata']
@@ -285,8 +293,6 @@ class Morpheus::Cli::ReportsCommand
285
293
  end
286
294
  end
287
295
 
288
- # payload.deep_merge!({'report' => v_prompt}) unless v_prompt.empty?
289
- payload.deep_merge!(v_prompt) unless v_prompt.empty?
290
296
  end
291
297
 
292
298
  @reports_interface.setopts(options)
@@ -27,7 +27,7 @@ class Morpheus::Cli::Roles
27
27
  @instances_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instances
28
28
  @instance_types_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instance_types
29
29
  @blueprints_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).blueprints
30
- @active_group_id = Morpheus::Cli::Groups.active_group
30
+ @active_group_id = Morpheus::Cli::Groups.active_groups[@appliance_name]
31
31
  end
32
32
 
33
33
  def handle(args)
@@ -88,6 +88,7 @@ class Morpheus::Cli::Roles
88
88
 
89
89
  def get(args)
90
90
  options = {}
91
+ params = {}
91
92
  optparse = Morpheus::Cli::OptionParser.new do |opts|
92
93
  opts.banner = subcommand_usage("[name]")
93
94
  opts.on('-p','--permissions', "Display Permissions") do |val|
@@ -116,7 +117,7 @@ class Morpheus::Cli::Roles
116
117
  options[:include_instance_type_access] = true
117
118
  options[:include_blueprint_access] = true
118
119
  end
119
- build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
120
+ build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
120
121
  opts.footer = "Get details about a role.\n" +
121
122
  "[name] is required. This is the name or id of a role."
122
123
  end
@@ -131,6 +132,9 @@ class Morpheus::Cli::Roles
131
132
  begin
132
133
  account = find_account_from_options(options)
133
134
  account_id = account ? account['id'] : nil
135
+
136
+ params.merge!(parse_query_options(options))
137
+
134
138
  @roles_interface.setopts(options)
135
139
  if options[:dry_run]
136
140
  if args[0].to_s =~ /\A\d{1,}\Z/
@@ -159,16 +163,8 @@ class Morpheus::Cli::Roles
159
163
  role = json_response['role']
160
164
  end
161
165
 
162
- if options[:json]
163
- puts as_json(json_response, options, "role")
164
- return 0
165
- elsif options[:yaml]
166
- puts as_yaml(json_response, options, "role")
167
- return 0
168
- elsif options[:csv]
169
- puts records_as_csv([json_response['role']], options)
170
- return 0
171
- end
166
+ render_result = render_with_format(json_response, options, 'role')
167
+ return 0 if render_result
172
168
 
173
169
  print cyan
174
170
  print_h1 "Role Details", options
@@ -179,7 +175,9 @@ class Morpheus::Cli::Roles
179
175
  "Description" => 'description',
180
176
  "Scope" => lambda {|it| it['scope'] },
181
177
  "Type" => lambda {|it| format_role_type(it) },
182
- "Multitenant" => lambda {|it| format_boolean(it['multitenant']) },
178
+ "Multitenant" => lambda {|it|
179
+ format_boolean(it['multitenant']).to_s + (it['multitenantLocked'] ? " (LOCKED)" : "")
180
+ },
183
181
  "Owner" => lambda {|it| role['owner'] ? role['owner']['name'] : '' },
184
182
  #"Account" => lambda {|it| it['account'] ? it['account']['name'] : '' },
185
183
  "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
@@ -205,15 +203,40 @@ class Morpheus::Cli::Roles
205
203
  access: get_access_string(it['access']),
206
204
  }
207
205
  end
206
+ if options[:sort]
207
+ rows.sort! {|a,b| a[options[:sort]] <=> b[options[:sort]] }
208
+ end
209
+ if options[:direction] == 'desc'
210
+ rows.reverse!
211
+ end
212
+ if options[:phrase]
213
+ phrase_regexp = /#{Regexp.escape(options[:phrase])}/i
214
+ rows = rows.select {|row| row[:code].to_s =~ phrase_regexp || row[:name].to_s =~ phrase_regexp }
215
+ end
208
216
  print as_pretty_table(rows, [:code, :name, :access], options)
209
217
  else
210
- puts "Use --permissions to list permissions"
211
- end
212
-
213
- print_h2 "Group Access", options
218
+ print cyan,"Use --permissions to list permissions","\n"
219
+ end
220
+
221
+ print_h2 "Global Access", options
222
+ # role_access_rows = [
223
+ # {name: "Groups", access: get_access_string(json_response['globalSiteAccess']) },
224
+ # {name: "Clouds", access: get_access_string(json_response['globalZoneAccess']) },
225
+ # {name: "Instance Types", access: get_access_string(json_response['globalInstanceTypeAccess']) },
226
+ # {name: "Blueprints", access: get_access_string(json_response['globalAppTemplateAccess'] || json_response['globalBlueprintAccess']) }
227
+ # ]
228
+ # puts as_pretty_table(role_access_rows, [:name, :access], options)
229
+ puts as_pretty_table([json_response], [
230
+ {"Groups" => lambda {|it| get_access_string(it['globalSiteAccess']) } },
231
+ {"Clouds" => lambda {|it| get_access_string(it['globalZoneAccess']) } },
232
+ {"Instance Types" => lambda {|it| get_access_string(it['globalInstanceTypeAccess']) } },
233
+ {"Blueprints" => lambda {|it| get_access_string(it['globalAppTemplateAccess'] || it['globalBlueprintAccess']) } },
234
+ ], options)
235
+
236
+ #print_h2 "Group Access: #{get_access_string(json_response['globalSiteAccess'])}", options
214
237
  print cyan
215
- puts "Global Group Access: #{get_access_string(json_response['globalSiteAccess'])}\n\n"
216
238
  if json_response['globalSiteAccess'] == 'custom'
239
+ print_h2 "Group Access", options
217
240
  if options[:include_group_access]
218
241
  rows = json_response['sites'].collect do |it|
219
242
  {
@@ -223,14 +246,18 @@ class Morpheus::Cli::Roles
223
246
  end
224
247
  print as_pretty_table(rows, [:name, :access], options)
225
248
  else
226
- puts "Use --group-access to list custom access"
249
+ print cyan,"Use -g, --group-access to list custom access","\n"
227
250
  end
251
+ else
252
+ # print "\n"
253
+ # print cyan,bold,"Group Access: #{get_access_string(json_response['globalSiteAccess'])}",reset,"\n"
228
254
  end
229
-
230
- print_h2 "Cloud Access", options
255
+
231
256
  print cyan
232
- puts "Global Cloud Access: #{get_access_string(json_response['globalZoneAccess'])}\n\n"
257
+ #puts "Cloud Access: #{get_access_string(json_response['globalZoneAccess'])}"
258
+ #print "\n"
233
259
  if json_response['globalZoneAccess'] == 'custom'
260
+ print_h2 "Cloud Access", options
234
261
  if options[:include_cloud_access]
235
262
  rows = json_response['zones'].collect do |it|
236
263
  {
@@ -240,14 +267,18 @@ class Morpheus::Cli::Roles
240
267
  end
241
268
  print as_pretty_table(rows, [:name, :access], options)
242
269
  else
243
- puts "Use --cloud-access to list custom access"
270
+ print cyan,"Use -c, --cloud-access to list custom access","\n"
244
271
  end
272
+ else
273
+ # print "\n"
274
+ # print cyan,bold,"Cloud Access: #{get_access_string(json_response['globalZoneAccess'])}",reset,"\n"
245
275
  end
246
276
 
247
- print_h2 "Instance Type Access", options
248
277
  print cyan
249
- puts "Global Instance Type Access: #{get_access_string(json_response['globalInstanceTypeAccess'])}\n\n"
278
+ # puts "Instance Type Access: #{get_access_string(json_response['globalInstanceTypeAccess'])}"
279
+ # print "\n"
250
280
  if json_response['globalInstanceTypeAccess'] == 'custom'
281
+ print_h2 "Instance Type Access", options
251
282
  if options[:include_instance_type_access]
252
283
  rows = json_response['instanceTypePermissions'].collect do |it|
253
284
  {
@@ -257,16 +288,20 @@ class Morpheus::Cli::Roles
257
288
  end
258
289
  print as_pretty_table(rows, [:name, :access], options)
259
290
  else
260
- puts "Use --instance-type-access to list custom access"
291
+ print cyan,"Use -i, --instance-type-access to list custom access","\n"
261
292
  end
293
+ else
294
+ # print "\n"
295
+ # print cyan,bold,"Instance Type Access: #{get_access_string(json_response['globalInstanceTypeAccess'])}",reset,"\n"
262
296
  end
263
297
 
264
298
  blueprint_global_access = json_response['globalAppTemplateAccess'] || json_response['globalBlueprintAccess']
265
299
  blueprint_permissions = json_response['appTemplatePermissions'] || json_response['blueprintPermissions'] || []
266
- print_h2 "Blueprint Access", options
267
300
  print cyan
268
- puts "Global Blueprint Access: #{get_access_string(json_response['globalAppTemplateAccess'])}\n\n"
301
+ # print_h2 "Blueprint Access: #{get_access_string(json_response['globalAppTemplateAccess'])}", options
302
+ # print "\n"
269
303
  if blueprint_global_access == 'custom'
304
+ print_h2 "Blueprint Access", options
270
305
  if options[:include_blueprint_access]
271
306
  rows = blueprint_permissions.collect do |it|
272
307
  {
@@ -276,8 +311,11 @@ class Morpheus::Cli::Roles
276
311
  end
277
312
  print as_pretty_table(rows, [:name, :access], options)
278
313
  else
279
- puts "Use --blueprint-access to list custom access"
314
+ print cyan,"Use -b, --blueprint-access to list custom access","\n"
280
315
  end
316
+ else
317
+ # print "\n"
318
+ # print cyan,bold,"Blueprint Access: #{get_access_string(json_response['globalAppTemplateAccess'])}",reset,"\n"
281
319
  end
282
320
 
283
321
  print reset,"\n"
@@ -292,7 +330,7 @@ class Morpheus::Cli::Roles
292
330
  options = {}
293
331
  optparse = Morpheus::Cli::OptionParser.new do |opts|
294
332
  opts.banner = subcommand_usage("[role]")
295
- build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
333
+ build_common_options(opts, options, [:list, :json, :yaml, :csv, :fields, :dry_run, :remote])
296
334
  opts.footer = "List the permissions for a role.\n" +
297
335
  "[role] is required. This is the name or id of a role."
298
336
  end
@@ -358,9 +396,19 @@ class Morpheus::Cli::Roles
358
396
  access: get_access_string(it['access']),
359
397
  }
360
398
  end
399
+ if options[:sort]
400
+ rows.sort! {|a,b| a[options[:sort]] <=> b[options[:sort]] }
401
+ end
402
+ if options[:direction] == 'desc'
403
+ rows.reverse!
404
+ end
405
+ if options[:phrase]
406
+ phrase_regexp = /#{Regexp.escape(options[:phrase])}/i
407
+ rows = rows.select {|row| row[:code].to_s =~ phrase_regexp || row[:name].to_s =~ phrase_regexp }
408
+ end
361
409
  print as_pretty_table(rows, [:code, :name, :access], options)
362
410
  else
363
- puts "No permissions found?"
411
+ puts "No permissions found"
364
412
  end
365
413
 
366
414
  print reset,"\n"
@@ -376,12 +424,17 @@ class Morpheus::Cli::Roles
376
424
  options = {}
377
425
  params = {}
378
426
  optparse = Morpheus::Cli::OptionParser.new do |opts|
379
- opts.banner = subcommand_usage("[options]")
427
+ opts.banner = subcommand_usage("[name] [options]")
380
428
  build_option_type_options(opts, options, add_role_option_types)
381
429
  build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
382
430
  end
383
431
  optparse.parse!(args)
384
-
432
+ if args.count > 1
433
+ raise_command_error "wrong number of arguments, expected 0-1 and got (#{args.count}) #{args}\n#{optparse}"
434
+ end
435
+ if args[0]
436
+ options[:options]['authority'] = args[0]
437
+ end
385
438
  connect(options)
386
439
  begin
387
440
 
@@ -425,6 +478,10 @@ class Morpheus::Cli::Roles
425
478
  if role_payload['roleType'] == 'user'
426
479
  v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'multitenant', 'fieldLabel' => 'Multitenant', 'type' => 'checkbox', 'defaultValue' => 'off', 'description' => 'A Multitenant role is automatically copied into all existing subaccounts as well as placed into a subaccount when created. Useful for providing a set of predefined roles a Customer can use', 'displayOrder' => 5}], options[:options])
427
480
  role_payload['multitenant'] = ['on','true'].include?(v_prompt['multitenant'].to_s)
481
+ if role_payload['multitenant']
482
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'multitenantLocked', 'fieldLabel' => 'Multitenant Locked', 'type' => 'checkbox', 'defaultValue' => 'off', 'description' => 'Prevents subtenants from branching off this role/modifying it. '}], options[:options])
483
+ role_payload['multitenantLocked'] = ['on','true'].include?(v_prompt['multitenantLocked'].to_s)
484
+ end
428
485
  end
429
486
  end
430
487
 
@@ -505,10 +562,10 @@ class Morpheus::Cli::Roles
505
562
  params.deep_merge!(passed_options)
506
563
  prompt_option_types = update_role_option_types()
507
564
  if !@is_master_account
508
- prompt_option_types = prompt_option_types.reject {|it| ['roleType', 'multitenant'].include?(it['fieldName']) }
565
+ prompt_option_types = prompt_option_types.reject {|it| ['roleType', 'multitenant','multitenantLocked'].include?(it['fieldName']) }
509
566
  end
510
567
  if role['roleType'] != 'user'
511
- prompt_option_types = prompt_option_types.reject {|it| ['multitenant'].include?(it['fieldName']) }
568
+ prompt_option_types = prompt_option_types.reject {|it| ['multitenant','multitenantLocked'].include?(it['fieldName']) }
512
569
  end
513
570
  #params = Morpheus::Cli::OptionTypes.prompt(prompt_option_types, options[:options], @api_client, options[:params])
514
571
 
@@ -1265,9 +1322,7 @@ class Morpheus::Cli::Roles
1265
1322
  {'fieldName' => 'roleType', 'fieldLabel' => 'Role Type', 'type' => 'select', 'selectOptions' => [{'name' => 'User Role', 'value' => 'user'}, {'name' => 'Account Role', 'value' => 'account'}], 'defaultValue' => 'user', 'displayOrder' => 3},
1266
1323
  {'fieldName' => 'baseRole', 'fieldLabel' => 'Copy From Role', 'type' => 'text', 'displayOrder' => 4},
1267
1324
  {'fieldName' => 'multitenant', 'fieldLabel' => 'Multitenant', 'type' => 'checkbox', 'defaultValue' => 'off', 'description' => 'A Multitenant role is automatically copied into all existing subaccounts as well as placed into a subaccount when created. Useful for providing a set of predefined roles a Customer can use', 'displayOrder' => 5},
1268
- # {'fieldName' => 'instanceLimits.maxStorage', 'fieldLabel' => 'Max Storage (bytes)', 'type' => 'text', 'displayOrder' => 8},
1269
- # {'fieldName' => 'instanceLimits.maxMemory', 'fieldLabel' => 'Max Memory (bytes)', 'type' => 'text', 'displayOrder' => 9},
1270
- # {'fieldName' => 'instanceLimits.maxCpu', 'fieldLabel' => 'CPU Count', 'type' => 'text', 'displayOrder' => 10},
1325
+ {'fieldName' => 'multitenantLocked', 'fieldLabel' => 'Multitenant Locked', 'type' => 'checkbox', 'defaultValue' => 'off', 'description' => 'Prevents subtenants from branching off this role/modifying it. ', 'displayOrder' => 6}
1271
1326
  ]
1272
1327
  end
1273
1328