morpheus-cli 3.3.1.4 → 3.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/lib/morpheus/api/api_client.rb +28 -0
  3. data/lib/morpheus/api/instance_types_interface.rb +12 -10
  4. data/lib/morpheus/api/instances_interface.rb +4 -0
  5. data/lib/morpheus/api/library_container_scripts_interface.rb +49 -0
  6. data/lib/morpheus/api/library_container_templates_interface.rb +49 -0
  7. data/lib/morpheus/api/library_container_types_interface.rb +65 -0
  8. data/lib/morpheus/api/library_container_upgrades_interface.rb +66 -0
  9. data/lib/morpheus/api/library_instance_types_interface.rb +59 -0
  10. data/lib/morpheus/api/library_layouts_interface.rb +65 -0
  11. data/lib/morpheus/api/servers_interface.rb +4 -0
  12. data/lib/morpheus/api/user_sources_interface.rb +120 -0
  13. data/lib/morpheus/api/virtual_images_interface.rb +7 -0
  14. data/lib/morpheus/cli.rb +12 -1
  15. data/lib/morpheus/cli/accounts.rb +35 -9
  16. data/lib/morpheus/cli/cli_command.rb +82 -2
  17. data/lib/morpheus/cli/curl_command.rb +1 -1
  18. data/lib/morpheus/cli/echo_command.rb +1 -1
  19. data/lib/morpheus/cli/hosts.rb +40 -14
  20. data/lib/morpheus/cli/instance_types.rb +106 -64
  21. data/lib/morpheus/cli/instances.rb +39 -15
  22. data/lib/morpheus/cli/library.rb +1 -1184
  23. data/lib/morpheus/cli/library_container_scripts_command.rb +437 -0
  24. data/lib/morpheus/cli/library_container_templates_command.rb +397 -0
  25. data/lib/morpheus/cli/library_container_types_command.rb +653 -0
  26. data/lib/morpheus/cli/library_instance_types_command.rb +491 -0
  27. data/lib/morpheus/cli/library_layouts_command.rb +650 -0
  28. data/lib/morpheus/cli/library_option_lists_command.rb +476 -0
  29. data/lib/morpheus/cli/library_option_types_command.rb +549 -0
  30. data/lib/morpheus/cli/library_upgrades_command.rb +604 -0
  31. data/lib/morpheus/cli/mixins/library_helper.rb +123 -0
  32. data/lib/morpheus/cli/mixins/print_helper.rb +21 -22
  33. data/lib/morpheus/cli/mixins/provisioning_helper.rb +56 -11
  34. data/lib/morpheus/cli/network_services_command.rb +1 -1
  35. data/lib/morpheus/cli/option_types.rb +12 -2
  36. data/lib/morpheus/cli/power_scheduling_command.rb +1 -1
  37. data/lib/morpheus/cli/shell.rb +120 -22
  38. data/lib/morpheus/cli/sleep_command.rb +45 -0
  39. data/lib/morpheus/cli/user_sources_command.rb +963 -0
  40. data/lib/morpheus/cli/users.rb +33 -2
  41. data/lib/morpheus/cli/version.rb +1 -1
  42. data/lib/morpheus/cli/version_command.rb +1 -1
  43. data/lib/morpheus/cli/virtual_images.rb +93 -39
  44. data/lib/morpheus/formatters.rb +37 -27
  45. data/lib/morpheus/terminal.rb +1 -1
  46. metadata +20 -2
@@ -0,0 +1,45 @@
1
+ require 'morpheus/cli/cli_command'
2
+ require 'term/ansicolor'
3
+ require 'json'
4
+
5
+ # This is for use in dotfile scripts
6
+ class Morpheus::Cli::Sleep
7
+ include Morpheus::Cli::CliCommand
8
+ set_command_name :sleep
9
+ set_command_hidden
10
+
11
+ def handle(args)
12
+ append_newline = true
13
+ options = {}
14
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
15
+ opts.banner = "Usage: morpheus sleep [seconds]"
16
+ build_common_options(opts, options, [:quiet])
17
+ end
18
+ optparse.parse!(args)
19
+
20
+ sleep_seconds = args[0]
21
+
22
+ # if !sleep_seconds
23
+ # print_error Morpheus::Terminal.angry_prompt
24
+ # puts_error "#{command_name} missing argument: [seconds]\n#{optparse}"
25
+ # return 1
26
+ # end
27
+
28
+ if !sleep_seconds
29
+ sleep_seconds = 1.0
30
+ end
31
+
32
+ # do it
33
+ if !options[:quiet]
34
+ if options[:debug]
35
+ # puts "Sleep for #{sleep_seconds.to_f} seconds..."
36
+ Morpheus::Logging::DarkPrinter.puts "sleep #{sleep_seconds.to_f} seconds..." if Morpheus::Logging.debug?
37
+ end
38
+ end
39
+
40
+ sleep(sleep_seconds.to_f)
41
+
42
+ return 0
43
+ end
44
+
45
+ end
@@ -0,0 +1,963 @@
1
+ require 'morpheus/cli/cli_command'
2
+ require 'morpheus/cli/mixins/accounts_helper'
3
+
4
+ class Morpheus::Cli::UserSourcesCommand
5
+ include Morpheus::Cli::CliCommand
6
+ include Morpheus::Cli::AccountsHelper
7
+
8
+ set_command_name :'user-sources'
9
+
10
+ register_subcommands :list, :get, :add, :update, :remove
11
+ register_subcommands :activate, :deactivate
12
+ register_subcommands({:'update-subdomain' => :update_subdomain})
13
+ register_subcommands({:'list-types' => :list_types})
14
+ register_subcommands({:'get-type' => :get_type})
15
+
16
+ def initialize()
17
+ # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
18
+ end
19
+
20
+ def connect(opts)
21
+ @api_client = establish_remote_appliance_connection(opts)
22
+ @user_sources_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).user_sources
23
+ @accounts_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).accounts
24
+ @users_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).users
25
+ end
26
+
27
+ def handle(args)
28
+ handle_subcommand(args)
29
+ end
30
+
31
+
32
+ def list(args)
33
+ options = {}
34
+ params = {}
35
+ account = nil
36
+ account_id = nil
37
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
38
+ opts.banner = subcommand_usage()
39
+ opts.on('--account ID', String, "Filter by Tenant") do |val|
40
+ account_id = val
41
+ end
42
+ # opts.on('--technology VALUE', String, "Filter by technology") do |val|
43
+ # params['provisionType'] = val
44
+ # end
45
+ build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
46
+ opts.footer = "List user sources."
47
+ end
48
+ optparse.parse!(args)
49
+ connect(options)
50
+ # instance is required right now.
51
+ # account_id = args[0] if !account_id
52
+ if args.count > 0
53
+ print_error Morpheus::Terminal.angry_prompt
54
+ puts_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
55
+ return 1
56
+ end
57
+ begin
58
+ # construct payload
59
+ if account_id
60
+ account = find_account_by_name_or_id(account_id)
61
+ return 1 if account.nil?
62
+ account_id = account['id']
63
+ end
64
+
65
+ params.merge!(parse_list_options(options))
66
+
67
+ if options[:dry_run]
68
+ print_dry_run @user_sources_interface.dry.list(account_id, params)
69
+ return
70
+ end
71
+
72
+ json_response = @user_sources_interface.list(account_id, params)
73
+ if options[:include_fields]
74
+ json_response = {"userSources" => filter_data(json_response["userSources"], options[:include_fields]) }
75
+ end
76
+ if options[:json]
77
+ puts as_json(json_response, options)
78
+ return 0
79
+ elsif options[:csv]
80
+ puts records_as_csv(json_response['userSources'], options)
81
+ return 0
82
+ elsif options[:yaml]
83
+ puts as_yaml(json_response, options)
84
+ return 0
85
+ end
86
+ user_sources = json_response['userSources']
87
+ title = "Morpheus User Sources"
88
+ subtitles = []
89
+ if account
90
+ subtitles << "Account: #{account['name']}".strip
91
+ end
92
+ subtitles += parse_list_subtitles(options)
93
+ print_h1 title, subtitles
94
+ if user_sources.empty?
95
+ if account
96
+ print cyan,"No user sources found for account #{account['name']}.",reset,"\n"
97
+ else
98
+ print cyan,"No user sources found.",reset,"\n"
99
+ end
100
+ else
101
+ print_user_sources_table(user_sources, options)
102
+ print_results_pagination(json_response, {:label => "user source", :n_label => "user sources"})
103
+ end
104
+ print reset,"\n"
105
+ rescue RestClient::Exception => e
106
+ print_rest_exception(e, options)
107
+ return 1
108
+ end
109
+ end
110
+
111
+ def get(args)
112
+ options = {}
113
+ params = {}
114
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
115
+ opts.banner = subcommand_usage("[name]")
116
+ build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
117
+ opts.footer = "Get details about an user source." + "\n" +
118
+ "[name] is required. This is the name or id of an user source."
119
+ end
120
+ optparse.parse!(args)
121
+ connect(options)
122
+ if args.count != 1
123
+ print_error Morpheus::Terminal.angry_prompt
124
+ puts_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
125
+ return 1
126
+ end
127
+ account_id = nil
128
+ account = nil
129
+ user_source_id = args[0]
130
+ # account_id = args[0]
131
+ # account = find_account_by_name_or_id(account_id)
132
+ # exit 1 if account.nil?
133
+ # account_id = account['id']
134
+ # user_source_id = args[1]
135
+ begin
136
+ if options[:dry_run]
137
+ if user_source_id.to_s =~ /\A\d{1,}\Z/
138
+ print_dry_run @user_sources_interface.dry.get(account_id, user_source_id.to_i)
139
+ else
140
+ print_dry_run @user_sources_interface.dry.list(account_id, {name:user_source_id})
141
+ end
142
+ return
143
+ end
144
+ user_source = find_user_source_by_name_or_id(account_id, user_source_id)
145
+ if user_source.nil?
146
+ return 1
147
+ end
148
+ # fetch by id to get config too
149
+ json_response = nil
150
+ if user_source_id.to_s =~ /\A\d{1,}\Z/
151
+ json_response = {'userSource' => user_source}
152
+ else
153
+ json_response = @user_sources_interface.get(account_id, user_source['id'])
154
+ user_source = json_response['userSource']
155
+ end
156
+
157
+ #user_source = json_response['userSource']
158
+ if options[:include_fields]
159
+ json_response = {"userSource" => filter_data(json_response["userSource"], options[:include_fields]) }
160
+ end
161
+ if options[:json]
162
+ puts as_json(json_response, options)
163
+ return 0
164
+ elsif options[:yaml]
165
+ puts as_yaml(json_response, options)
166
+ return 0
167
+ elsif options[:csv]
168
+ puts records_as_csv([json_response['userSource']], options)
169
+ return 0
170
+ end
171
+
172
+ print_h1 "User Source Details"
173
+ print cyan
174
+ description_cols = {
175
+ "ID" => lambda {|it| it['id'] },
176
+ "Name" => lambda {|it| it['name'] },
177
+ "Description" => lambda {|it| it['description'] },
178
+ "Type" => lambda {|it| it['type'] },
179
+ "Account" => lambda {|it| it['account'] ? it['account']['name'] : '' },
180
+ #"Subdomain" => lambda {|it| it['subdomain'] },
181
+ "Login URL" => lambda {|it| it['loginURL'] },
182
+ "Default Role" => lambda {|it| it['defaultAccountRole'] ? it['defaultAccountRole']['authority'] : '' },
183
+ "Active" => lambda {|it| format_boolean it['active'] },
184
+ }
185
+ print_description_list(description_cols, user_source)
186
+
187
+ # show config settings...
188
+ user_source_config = user_source['config']
189
+ print_h2 "#{user_source['type']} Configuration"
190
+ if user_source_config
191
+ columns = user_source_config.keys #.sort
192
+ print_description_list(columns, user_source_config)
193
+ # print reset,"\n"
194
+ else
195
+ print yellow,"No config found.","\n",reset
196
+ end
197
+
198
+ role_mappings = user_source['roleMappings']
199
+ print_h2 "Role Mappings"
200
+ if role_mappings && role_mappings.size > 0
201
+ # print_h2 "Role Mappings"
202
+ role_mapping_columns = [
203
+ {"MORPHEUS ROLE" => lambda {|it|
204
+ it['mappedRole'] ? it['mappedRole']['authority'] : ''
205
+ } },
206
+ {"SOURCE ROLE NAME" => lambda {|it| it['sourceRoleName'] } },
207
+ {"SOURCE ROLE FQN" => lambda {|it| it['sourceRoleFqn'] } },
208
+ ]
209
+ print as_pretty_table(role_mappings, role_mapping_columns)
210
+ print "\n",reset
211
+ else
212
+ print yellow,"No role mappings found for this user source.","\n",reset
213
+ end
214
+ return 0
215
+ rescue RestClient::Exception => e
216
+ print_rest_exception(e, options)
217
+ return 1
218
+ end
219
+ end
220
+
221
+ def add(args)
222
+ options = {}
223
+ params = {}
224
+ account_id = nil
225
+ type_code = nil
226
+ role_mappings = nil
227
+ role_mapping_names = nil
228
+ default_role_id = nil
229
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
230
+ opts.banner = subcommand_usage("[account] [name]")
231
+ opts.on('--account ID', String, "Account this user source belongs to") do |val|
232
+ account_id = val
233
+ end
234
+ opts.on('--type CODE', String, "User Source Type") do |val|
235
+ type_code = val
236
+ end
237
+ opts.on('--name VALUE', String, "Name for this user source") do |val|
238
+ params['name'] = val
239
+ end
240
+ opts.on('--description VALUE', String, "Description") do |val|
241
+ params['description'] = val
242
+ end
243
+ opts.on('--role-mappings MAPPINGS', String, "Role Mappings FQN in the format id1:FQN1,id2:FQN2") do |val|
244
+ role_mappings = {}
245
+ val.split(',').collect {|it| it.strip.split(':') }.each do |pair|
246
+ k, v = pair[0], pair[1]
247
+ if !k.to_s.empty?
248
+ role_mappings[k.to_s] = v
249
+ end
250
+ end
251
+ end
252
+ opts.on('--role-mapping-names MAPPINGS', String, "Role Mapping Names in the format id1:Name1,id2:Name2") do |val|
253
+ role_mapping_names = {}
254
+ val.split(',').collect {|it| it.strip.split(':') }.each do |pair|
255
+ k, v = pair[0], pair[1]
256
+ if !k.to_s.empty?
257
+ role_mapping_names[k.to_s] = v
258
+ end
259
+ end
260
+ end
261
+
262
+ opts.on('--default-role ID', String, "Default Role ID") do |val|
263
+ default_role_id = val
264
+ end
265
+ #build_option_type_options(opts, options, add_user_source_option_types())
266
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
267
+ opts.footer = "Create a new user source." + "\n" +
268
+ "[account] is required. This is the name or id of an account."
269
+ end
270
+ optparse.parse!(args)
271
+ connect(options)
272
+ if args.count > 2
273
+ print_error Morpheus::Terminal.angry_prompt
274
+ puts_error "wrong number of arguments, expected 0-2 and got (#{args.count}) #{args.inspect}\n#{optparse}"
275
+ return 1
276
+ end
277
+ if args[0]
278
+ account_id = args[0]
279
+ end
280
+ if args[1]
281
+ params['name'] = args[1]
282
+ end
283
+ begin
284
+ # find the account first, or just prompt for that too please.
285
+ if !account_id
286
+ print_error Morpheus::Terminal.angry_prompt
287
+ puts_error "missing required argument [account]\n#{optparse}"
288
+ return 1
289
+ end
290
+ account = find_account_by_name_or_id(account_id)
291
+ return 1 if account.nil?
292
+ account_id = account['id']
293
+
294
+ # construct payload
295
+ payload = nil
296
+ if options[:payload]
297
+ payload = options[:payload]
298
+ else
299
+ payload = {'userSource' => {}}
300
+
301
+ # User Source Type
302
+ user_source_types = @user_sources_interface.list_types({userSelectable: true})['userSourceTypes']
303
+ if user_source_types.empty?
304
+ print_red_alert "No available User Source Types found"
305
+ return 1
306
+ end
307
+ user_source_type = nil
308
+ if !type_code
309
+ user_source_type_dropdown = user_source_types.collect {|it| { 'name' => it['type'], 'value' => it['type']} }
310
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'type' => 'select', 'selectOptions' => user_source_type_dropdown, 'fieldLabel' => 'Type', 'required' => true}], options[:options])
311
+ type_code = v_prompt['type'] if v_prompt['type']
312
+ end
313
+ user_source_type = user_source_types.find { |it| it['type'] == type_code }
314
+
315
+ if user_source_type.nil?
316
+ print_red_alert "User Source Type not found for '#{type_code}'"
317
+ return 1
318
+ end
319
+
320
+ payload['userSource']['type'] = type_code
321
+
322
+ # Name
323
+ if params['name']
324
+ payload['userSource']['name'] = params['name']
325
+ else
326
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => 'Name', 'required' => true}], options[:options])
327
+ payload['userSource']['name'] = v_prompt['name'] if v_prompt['name']
328
+ end
329
+
330
+ # custom options by type
331
+ my_option_types = load_user_source_type_option_types(user_source_type['type'])
332
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(my_option_types, options[:options])
333
+ payload['userSource'].deep_merge!(v_prompt)
334
+
335
+ # Default Account Role
336
+ # todo: a proper select
337
+ if !default_role_id
338
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'defaultAccountRole', 'fieldName' => 'id', 'type' => 'text', 'fieldLabel' => 'Default Account Role ID', 'required' => true}], options[:options])
339
+ if v_prompt['defaultAccountRole'] && v_prompt['defaultAccountRole']['id']
340
+ default_role_id = v_prompt['defaultAccountRole']['id']
341
+ end
342
+ end
343
+ if default_role_id
344
+ payload['userSource']['defaultAccountRole'] = {'id' => default_role_id }
345
+ end
346
+
347
+
348
+ if role_mappings
349
+ payload['roleMappings'] = role_mappings
350
+ end
351
+
352
+ if role_mapping_names
353
+ payload['roleMappingNames'] = role_mapping_names
354
+ end
355
+
356
+ # support old -O options
357
+ payload['userSource'].deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
358
+
359
+
360
+ end
361
+ # dry run?
362
+ if options[:dry_run]
363
+ print_dry_run @user_sources_interface.dry.create(account_id, payload)
364
+ return
365
+ end
366
+ # do it
367
+ json_response = @user_sources_interface.create(account_id, payload)
368
+ # print and return result
369
+ if options[:json]
370
+ puts as_json(json_response, options)
371
+ return 0
372
+ end
373
+ user_source = json_response['userSource']
374
+ print_green_success "Added User Source #{user_source['name']}"
375
+ get([user_source['id']])
376
+ return 0
377
+ rescue RestClient::Exception => e
378
+ print_rest_exception(e, options)
379
+ exit 1
380
+ end
381
+ end
382
+
383
+ def update(args)
384
+ options = {}
385
+ params = {}
386
+ account_id = nil
387
+ role_mappings = nil
388
+ role_mapping_names = nil
389
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
390
+ opts.banner = subcommand_usage("[name] [options]")
391
+ opts.on('--name VALUE', String, "Name for this user source") do |val|
392
+ params['name'] = val
393
+ end
394
+ opts.on('--description VALUE', String, "Description") do |val|
395
+ params['description'] = val
396
+ end
397
+ opts.on('--role-mappings MAPPINGS', String, "Role Mappings in the format id1:FQN,id2:FQN2") do |val|
398
+ role_mappings = {}
399
+ val.split(',').collect {|it| it.strip.split(':') }.each do |pair|
400
+ k, v = pair[0], pair[1]
401
+ if !k.to_s.empty?
402
+ role_mappings[k.to_s] = v
403
+ end
404
+ end
405
+ end
406
+ opts.on('--role-mapping-names MAPPINGS', String, "Role Mapping Names in the format id1:Name1,id2:Name2") do |val|
407
+ role_mapping_names = {}
408
+ val.split(',').collect {|it| it.strip.split(':') }.each do |pair|
409
+ k, v = pair[0], pair[1]
410
+ if !k.to_s.empty?
411
+ role_mapping_names[k.to_s] = v
412
+ end
413
+ end
414
+ end
415
+ build_common_options(opts, options, [:options, :json, :dry_run, :remote])
416
+ opts.footer = "Update a user source." + "\n" +
417
+ "[name] is required. This is the name or id of a user source."
418
+ end
419
+ optparse.parse!(args)
420
+ if args.count < 1
421
+ puts optparse
422
+ exit 1
423
+ end
424
+ connect(options)
425
+ begin
426
+ user_source = find_user_source_by_name_or_id(nil, args[0])
427
+ exit 1 if user_source.nil?
428
+ payload = nil
429
+ if options[:payload]
430
+ payload = options[:payload]
431
+ else
432
+ payload = {'userSource' => {}}
433
+
434
+ # Name
435
+ if params['name']
436
+ payload['userSource']['name'] = params['name']
437
+ end
438
+
439
+ # Description
440
+ if params['description']
441
+ payload['userSource']['description'] = params['description']
442
+ end
443
+
444
+ if role_mappings
445
+ payload['roleMappings'] = role_mappings
446
+ end
447
+
448
+ if role_mapping_names
449
+ payload['roleMappingNames'] = role_mapping_names
450
+ end
451
+
452
+ # support old -O options
453
+ payload['userSource'].deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
454
+
455
+ end
456
+
457
+ if options[:dry_run]
458
+ print_dry_run @user_sources_interface.dry.update(nil, user_source['id'], payload)
459
+ return
460
+ end
461
+
462
+ json_response = @user_sources_interface.update(nil, user_source['id'], payload)
463
+
464
+ if options[:json]
465
+ puts JSON.pretty_generate(json_response)
466
+ return
467
+ end
468
+
469
+ print_green_success "Updated User Source #{params['name'] || user_source['name']}"
470
+ get([user_source['id']])
471
+ rescue RestClient::Exception => e
472
+ print_rest_exception(e, options)
473
+ exit 1
474
+ end
475
+ end
476
+
477
+ def activate(args)
478
+ options = {}
479
+ params = {}
480
+ account_id = nil
481
+ role_mappings = nil
482
+ role_mapping_names = nil
483
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
484
+ opts.banner = subcommand_usage("[name]")
485
+ build_common_options(opts, options, [:options, :json, :dry_run, :remote])
486
+ opts.footer = "Activate a user source." + "\n" +
487
+ "[name] is required. This is the name or id of a user source."
488
+ end
489
+ optparse.parse!(args)
490
+ if args.count < 1
491
+ puts optparse
492
+ exit 1
493
+ end
494
+ connect(options)
495
+ begin
496
+ user_source = find_user_source_by_name_or_id(nil, args[0])
497
+ exit 1 if user_source.nil?
498
+ payload = nil
499
+ if options[:payload]
500
+ payload = options[:payload]
501
+ else
502
+ payload = {}
503
+ # support old -O options
504
+ payload.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
505
+ end
506
+
507
+ if options[:dry_run]
508
+ print_dry_run @user_sources_interface.dry.activate(nil, user_source['id'], payload)
509
+ return
510
+ end
511
+
512
+ json_response = @user_sources_interface.activate(nil, user_source['id'], payload)
513
+
514
+ if options[:json]
515
+ puts JSON.pretty_generate(json_response)
516
+ return
517
+ end
518
+
519
+ print_green_success "Activated User Source #{user_source['name']}"
520
+ get([user_source['id']])
521
+ rescue RestClient::Exception => e
522
+ print_rest_exception(e, options)
523
+ exit 1
524
+ end
525
+ end
526
+
527
+ def deactivate(args)
528
+ options = {}
529
+ params = {}
530
+ account_id = nil
531
+ role_mappings = nil
532
+ role_mapping_names = nil
533
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
534
+ opts.banner = subcommand_usage("[name]")
535
+ build_common_options(opts, options, [:options, :json, :dry_run, :remote])
536
+ opts.footer = "Deactivate a user source." + "\n" +
537
+ "[name] is required. This is the name or id of a user source."
538
+ end
539
+ optparse.parse!(args)
540
+ if args.count < 1
541
+ puts optparse
542
+ exit 1
543
+ end
544
+ connect(options)
545
+ begin
546
+ user_source = find_user_source_by_name_or_id(nil, args[0])
547
+ exit 1 if user_source.nil?
548
+ payload = nil
549
+ if options[:payload]
550
+ payload = options[:payload]
551
+ else
552
+ payload = {}
553
+ # support old -O options
554
+ payload.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
555
+ end
556
+
557
+ if options[:dry_run]
558
+ print_dry_run @user_sources_interface.dry.deactivate(nil, user_source['id'], payload)
559
+ return
560
+ end
561
+
562
+ json_response = @user_sources_interface.deactivate(nil, user_source['id'], payload)
563
+
564
+ if options[:json]
565
+ puts JSON.pretty_generate(json_response)
566
+ return
567
+ end
568
+
569
+ print_green_success "Activated User Source #{user_source['name']}"
570
+ get([user_source['id']])
571
+ rescue RestClient::Exception => e
572
+ print_rest_exception(e, options)
573
+ exit 1
574
+ end
575
+ end
576
+
577
+ def update_subdomain(args)
578
+ options = {}
579
+ params = {}
580
+ account_id = nil
581
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
582
+ opts.banner = subcommand_usage("[name]")
583
+ opts.on('--subdomain VALUE', String, "New subdomain for this user source") do |val|
584
+ params['subdomain'] = (val == 'null') ? nil : val
585
+ end
586
+ build_common_options(opts, options, [:options, :json, :dry_run, :remote])
587
+ opts.footer = "Update subdomain for a user source." + "\n" +
588
+ "[name] is required. This is the name or id of a user source."
589
+ end
590
+ optparse.parse!(args)
591
+ if args.count < 1
592
+ puts optparse
593
+ exit 1
594
+ end
595
+ connect(options)
596
+ begin
597
+ user_source = find_user_source_by_name_or_id(nil, args[0])
598
+ exit 1 if user_source.nil?
599
+ payload = nil
600
+ if options[:payload]
601
+ payload = options[:payload]
602
+ else
603
+ payload = {}
604
+ payload['subdomain'] = params['subdomain'] if params.key?('subdomain')
605
+ # support old -O options
606
+ payload.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
607
+ end
608
+
609
+ if options[:dry_run]
610
+ print_dry_run @user_sources_interface.dry.update_subdomain(nil, user_source['id'], payload)
611
+ return
612
+ end
613
+
614
+ json_response = @user_sources_interface.update_subdomain(nil, user_source['id'], payload)
615
+
616
+ if options[:json]
617
+ puts JSON.pretty_generate(json_response)
618
+ return
619
+ end
620
+
621
+ print_green_success "Activated User Source #{user_source['name']}"
622
+ get([user_source['id']])
623
+ rescue RestClient::Exception => e
624
+ print_rest_exception(e, options)
625
+ exit 1
626
+ end
627
+ end
628
+
629
+ def remove(args)
630
+ options = {}
631
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
632
+ opts.banner = subcommand_usage("[name]")
633
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
634
+ opts.footer = "Delete a user_source."
635
+ end
636
+ optparse.parse!(args)
637
+ if args.count < 1
638
+ puts optparse
639
+ exit 1
640
+ end
641
+ connect(options)
642
+
643
+ begin
644
+ user_source = find_user_source_by_name_or_id(nil, args[0])
645
+ exit 1 if user_source.nil?
646
+
647
+ unless Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the user source #{user_source['name']}?", options)
648
+ exit
649
+ end
650
+ if options[:dry_run]
651
+ print_dry_run @user_sources_interface.dry.destroy(nil, user_source['id'])
652
+ return
653
+ end
654
+ json_response = @user_sources_interface.destroy(nil, user_source['id'])
655
+
656
+ if options[:json]
657
+ print JSON.pretty_generate(json_response), "\n"
658
+ return
659
+ end
660
+
661
+ print_green_success "Removed User Source #{user_source['name']}"
662
+ #list([])
663
+ rescue RestClient::Exception => e
664
+ print_rest_exception(e, options)
665
+ exit 1
666
+ end
667
+ end
668
+
669
+ def list_types(args)
670
+ options = {}
671
+ params = {}
672
+ account = nil
673
+ account_id = nil
674
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
675
+ opts.banner = subcommand_usage()
676
+ build_common_options(opts, options, [:list, :json, :yaml, :csv, :fields, :dry_run, :remote])
677
+ opts.footer = "List user source types."
678
+ end
679
+ optparse.parse!(args)
680
+ connect(options)
681
+ # instance is required right now.
682
+ # account_id = args[0] if !account_id
683
+ if args.count != 0
684
+ print_error Morpheus::Terminal.angry_prompt
685
+ puts_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args.inspect}\n#{optparse}"
686
+ return 1
687
+ end
688
+ begin
689
+ # construct payload
690
+ params.merge!(parse_list_options(options))
691
+
692
+ if options[:dry_run]
693
+ print_dry_run @user_sources_interface.dry.list_types(params)
694
+ return
695
+ end
696
+
697
+ json_response = @user_sources_interface.list_types(params)
698
+ if options[:include_fields]
699
+ json_response = {"userSourceTypes" => filter_data(json_response["userSourceTypes"], options[:include_fields]) }
700
+ end
701
+ if options[:json]
702
+ puts as_json(json_response, options)
703
+ return 0
704
+ elsif options[:csv]
705
+ puts records_as_csv(json_response['userSourceTypes'], options)
706
+ return 0
707
+ elsif options[:yaml]
708
+ puts as_yaml(json_response, options)
709
+ return 0
710
+ end
711
+ user_source_types = json_response['userSourceTypes']
712
+ title = "Morpheus User Source Types"
713
+ subtitles = []
714
+ subtitles += parse_list_subtitles(options)
715
+ print_h1 title, subtitles
716
+ if user_source_types.empty?
717
+ print cyan,"No types found.",reset,"\n"
718
+ else
719
+ print_user_source_types_table(user_source_types, options)
720
+ print_results_pagination(json_response, {:label => "type", :n_label => "types"})
721
+ end
722
+ print reset,"\n"
723
+ rescue RestClient::Exception => e
724
+ print_rest_exception(e, options)
725
+ return 1
726
+ end
727
+ end
728
+
729
+ def get_type(args)
730
+ options = {}
731
+ params = {}
732
+ account = nil
733
+ account_id = nil
734
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
735
+ opts.banner = subcommand_usage("[type]")
736
+ build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
737
+ opts.footer = "Get details about a user source type." + "\n" +
738
+ "[type] is required. This is the type identifier."
739
+ end
740
+ optparse.parse!(args)
741
+ connect(options)
742
+ # instance is required right now.
743
+ # account_id = args[0] if !account_id
744
+ expected_arg_count = 1
745
+ if args.count != expected_arg_count
746
+ print_error Morpheus::Terminal.angry_prompt
747
+ puts_error "wrong number of arguments, expected #{expected_arg_count} and got (#{args.count}) #{args.inspect}\n#{optparse}"
748
+ return 1
749
+ end
750
+ begin
751
+ user_source_type_id = args[0]
752
+
753
+ # all_user_source_types = @user_sources_interface.dry.list_types({})['userSourceTypes']
754
+ # user_source_type = all_user_source_types.find {|it| it['type'] == user_source_type_id }
755
+ # if !user_source_type
756
+ # print_red_alert "User Source Type not found by id '#{user_source_type_id}'"
757
+ # return 1
758
+ # end
759
+
760
+ # construct payload
761
+
762
+ if options[:dry_run]
763
+ print_dry_run @user_sources_interface.dry.list_types(user_source_type_id, params)
764
+ return
765
+ end
766
+ json_response = @user_sources_interface.get_type(user_source_type_id, params)
767
+ user_source_type = json_response['userSourceType']
768
+ if options[:include_fields]
769
+ json_response = {"userSource" => filter_data(user_source_type, options[:include_fields]) }
770
+ end
771
+ if options[:json]
772
+ puts as_json(json_response, options)
773
+ return 0
774
+ elsif options[:yaml]
775
+ puts as_yaml(json_response, options)
776
+ return 0
777
+ elsif options[:csv]
778
+ puts records_as_csv([user_source_type], options)
779
+ return 0
780
+ end
781
+ title = "User Source Type"
782
+ subtitles = []
783
+ print_h1 title, subtitles
784
+ print cyan
785
+ description_cols = {
786
+ #"ID" => lambda {|it| it['id'] },
787
+ # "Name" => lambda {|it| it['name'] },
788
+ # "Code" => lambda {|it| it['code'] },
789
+ "Type" => lambda {|it| it['type'] },
790
+ "External Login" => lambda {|it| format_boolean it['externalLogin'] },
791
+ "Selectable" => lambda {|it| format_boolean it['userSelectable'] },
792
+ }
793
+ print_description_list(description_cols, user_source_type)
794
+
795
+ # show config settings...
796
+ my_option_types = user_source_type['optionTypes']
797
+
798
+
799
+ if !my_option_types
800
+ my_option_types = load_user_source_type_option_types(user_source_type['type'])
801
+ end
802
+
803
+ print_h2 "Configuration Option Types"
804
+ if my_option_types && my_option_types.size > 0
805
+ columns = [
806
+ {"FIELD LABEL" => lambda {|it| it['fieldLabel'] } },
807
+ {"FIELD NAME" => lambda {|it| [it['fieldContext'], it['fieldName']].select {|it| !it.to_s.empty? }.join('.') } },
808
+ {"TYPE" => lambda {|it| it['type'] } },
809
+ {"DEFAULT" => lambda {|it| it['defaultValue'] } },
810
+ {"REQUIRED" => lambda {|it| format_boolean it['required'] } },
811
+ ]
812
+ print as_pretty_table(my_option_types, columns)
813
+ else
814
+ print yellow,"No option types found.","\n",reset
815
+ end
816
+
817
+ print reset,"\n"
818
+ rescue RestClient::Exception => e
819
+ print_rest_exception(e, options)
820
+ return 1
821
+ end
822
+ end
823
+
824
+ private
825
+
826
+ def find_user_source_by_name_or_id(account_id, val)
827
+ if val.to_s =~ /\A\d{1,}\Z/
828
+ return find_user_source_by_id(account_id, val)
829
+ else
830
+ return find_user_source_by_name(account_id, val)
831
+ end
832
+ end
833
+
834
+ def find_user_source_by_id(account_id, id)
835
+ begin
836
+ json_response = @user_sources_interface.get(account_id, id.to_i)
837
+ return json_response['userSource']
838
+ rescue RestClient::Exception => e
839
+ if e.response && e.response.code == 404
840
+ print_red_alert "User Source not found by id #{id}"
841
+ else
842
+ raise e
843
+ end
844
+ end
845
+ end
846
+
847
+ def find_user_source_by_name(account_id, name)
848
+ user_sources = @user_sources_interface.list(account_id, {name: name.to_s})['userSources']
849
+ if user_sources.empty?
850
+ print_red_alert "User Source not found by name #{name}"
851
+ return nil
852
+ elsif user_sources.size > 1
853
+ print_red_alert "#{user_sources.size} user sources found by name #{name}"
854
+ print_user_sources_table(user_sources, {color: red})
855
+ print_red_alert "Try using ID instead"
856
+ print reset,"\n"
857
+ return nil
858
+ else
859
+ return user_sources[0]
860
+ end
861
+ end
862
+
863
+ def print_user_sources_table(user_sources, opts={})
864
+ columns = [
865
+ {"ID" => lambda {|user_source| user_source['id'] } },
866
+ {"NAME" => lambda {|user_source| user_source['name'] } },
867
+ {"TYPE" => lambda {|user_source| user_source['type'] } },
868
+ {"ACCOUNT" => lambda {|user_source| user_source['account'] ? user_source['account']['name'] : '' } },
869
+ {"ACTIVE" => lambda {|user_source| format_boolean user_source['active'] } },
870
+ ]
871
+ if opts[:include_fields]
872
+ columns = opts[:include_fields]
873
+ end
874
+ print as_pretty_table(user_sources, columns, opts)
875
+ end
876
+
877
+ def print_user_source_types_table(user_sources, opts={})
878
+ columns = [
879
+ {"TYPE" => lambda {|user_source| user_source['type'] } },
880
+ # {"NAME" => lambda {|user_source| user_source['name'] } },
881
+ ]
882
+ if opts[:include_fields]
883
+ columns = opts[:include_fields]
884
+ end
885
+ print as_pretty_table(user_sources, columns, opts)
886
+ end
887
+
888
+ # manual options until this is data driven
889
+ # * ldap [ldap]
890
+ # * jumpCloud [jumpCloud]
891
+ # * activeDirectory [activeDirectory]
892
+ # * okta [okta]
893
+ # * oneLogin [oneLogin]
894
+ # * saml [saml]
895
+ # * customExternal [customExternal]
896
+ # * customApi [customApi]
897
+ def load_user_source_type_option_types(type_code)
898
+ if type_code == 'ldap'
899
+ [
900
+ {'fieldContext' => 'config', 'fieldName' => 'url', 'type' => 'text', 'fieldLabel' => 'URL', 'required' => true, 'description' => ''},
901
+ {'fieldContext' => 'config', 'fieldName' => 'bindingUsername', 'type' => 'text', 'fieldLabel' => 'Binding Username', 'required' => true, 'description' => ''},
902
+ {'fieldContext' => 'config', 'fieldName' => 'bindingPassword', 'type' => 'password', 'fieldLabel' => 'Binding Password', 'required' => true, 'description' => ''},
903
+ {'fieldContext' => 'config', 'fieldName' => 'requiredGroup', 'type' => 'text', 'fieldLabel' => 'Required group name (a.k.a. tag)', 'required' => true, 'description' => ''},
904
+ ]
905
+ elsif type_code == 'jumpCloud'
906
+ [
907
+ {'fieldContext' => 'config', 'fieldName' => 'organizationId', 'type' => 'text', 'fieldLabel' => 'Organization ID', 'required' => true, 'description' => ''},
908
+ {'fieldContext' => 'config', 'fieldName' => 'bindingUsername', 'type' => 'text', 'fieldLabel' => 'Binding Username', 'required' => true, 'description' => ''},
909
+ {'fieldContext' => 'config', 'fieldName' => 'bindingPassword', 'type' => 'password', 'fieldLabel' => 'Binding Password', 'required' => true, 'description' => ''},
910
+ {'fieldContext' => 'config', 'fieldName' => 'requiredRole', 'type' => 'text', 'fieldLabel' => 'Required group name (a.k.a. tag)', 'required' => true, 'description' => ''},
911
+ ]
912
+ elsif type_code == 'activeDirectory'
913
+ [
914
+ {'fieldContext' => 'config', 'fieldName' => 'url', 'type' => 'text', 'fieldLabel' => 'AD Server', 'required' => true, 'description' => ''},
915
+ {'fieldContext' => 'config', 'fieldName' => 'domain', 'type' => 'text', 'fieldLabel' => 'Domain', 'required' => true, 'description' => ''},
916
+ {'fieldContext' => 'config', 'fieldName' => 'useSSL', 'type' => 'checkbox', 'fieldLabel' => 'Use SSL', 'required' => true, 'description' => '', 'defaultValue' => false},
917
+ {'fieldContext' => 'config', 'fieldName' => 'bindingUsername', 'type' => 'text', 'fieldLabel' => 'Binding Username', 'required' => true, 'description' => ''},
918
+ {'fieldContext' => 'config', 'fieldName' => 'bindingPassword', 'type' => 'password', 'fieldLabel' => 'Binding Password', 'required' => true, 'description' => ''},
919
+ {'fieldContext' => 'config', 'fieldName' => 'requiredGroup', 'type' => 'text', 'fieldLabel' => 'Required Group', 'required' => true, 'description' => ''},
920
+ {'fieldContext' => 'config', 'fieldName' => 'searchMemberGroups', 'type' => 'checkbox', 'fieldLabel' => 'Include Member Groups', 'required' => true, 'description' => '', 'defaultValue' => false},
921
+ ]
922
+ elsif type_code == 'okta'
923
+ [
924
+ {'fieldContext' => 'config', 'fieldName' => 'url', 'type' => 'text', 'fieldLabel' => 'OKTA URL', 'required' => true, 'description' => ''},
925
+ {'fieldContext' => 'config', 'fieldName' => 'administratorAPIToken', 'type' => 'password', 'fieldLabel' => 'Adminstrator API Token', 'required' => true, 'description' => ''},
926
+ {'fieldContext' => 'config', 'fieldName' => 'requiredGroup', 'type' => 'text', 'fieldLabel' => 'Required Group', 'required' => true, 'description' => ''}
927
+ ]
928
+ elsif type_code == 'oneLogin'
929
+ [
930
+ {'fieldContext' => 'config', 'fieldName' => 'subdomain', 'type' => 'text', 'fieldLabel' => 'OneLogin Subdomain', 'required' => true, 'description' => ''},
931
+ {'fieldContext' => 'config', 'fieldName' => 'region', 'type' => 'text', 'fieldLabel' => 'OneLogin Region', 'required' => true, 'description' => ''},
932
+ {'fieldContext' => 'config', 'fieldName' => 'clientSecret', 'type' => 'password', 'fieldLabel' => 'API Client Secret', 'required' => true, 'description' => ''},
933
+ {'fieldContext' => 'config', 'fieldName' => 'clientId', 'type' => 'text', 'fieldLabel' => 'API Client ID', 'required' => true, 'description' => ''},
934
+ {'fieldContext' => 'config', 'fieldName' => 'requiredRole', 'type' => 'text', 'fieldLabel' => 'Required Role', 'required' => true, 'description' => ''},
935
+ ]
936
+ elsif type_code == 'saml'
937
+ [
938
+ {'fieldContext' => 'config', 'fieldName' => 'url', 'type' => 'text', 'fieldLabel' => 'Login Redirect URL', 'required' => true, 'description' => ''},
939
+ {'fieldContext' => 'config', 'fieldName' => 'doNotIncludeSAMLRequest', 'type' => 'checkbox', 'fieldLabel' => 'Exclude SAMLRequest Parameter', 'required' => true, 'description' => 'Do not include SAMLRequest parameter', 'defaultValue' => false},
940
+ {'fieldContext' => 'config', 'fieldName' => 'logoutUrl', 'type' => 'text', 'fieldLabel' => 'Logout Post URL', 'required' => true, 'description' => ''},
941
+ {'fieldContext' => 'config', 'fieldName' => 'publicKey', 'type' => 'textarea', 'fieldLabel' => 'Signing Public Key', 'required' => true, 'description' => ''},
942
+ ]
943
+ elsif type_code == 'customExternal'
944
+ [
945
+ {'fieldContext' => 'config', 'fieldName' => 'loginUrl', 'type' => 'text', 'fieldLabel' => 'External Login URL', 'required' => true, 'description' => ''},
946
+ {'fieldContext' => 'config', 'fieldName' => 'doNotIncludeSAMLRequest', 'type' => 'checkbox', 'fieldLabel' => 'Exclude SAMLRequest Parameter', 'required' => true, 'description' => 'Do not include SAMLRequest parameter', 'defaultValue' => false},
947
+ {'fieldContext' => 'config', 'fieldName' => 'logout', 'type' => 'text', 'fieldLabel' => 'External Logout URL', 'required' => true, 'description' => ''},
948
+ {'fieldContext' => 'config', 'fieldName' => 'encryptionAlgo', 'type' => 'select', 'selectOptions' => ['NONE','AES','DES','DESede','HmacSHA1', 'HmacSHA256'].collect {|it| { 'name' => it, 'value' => it} }, 'fieldLabel' => 'Encryption Algorithm', 'required' => true, 'description' => ''},
949
+ {'fieldContext' => 'config', 'fieldName' => 'encryptionKey', 'type' => 'text', 'fieldLabel' => 'Encryption Key', 'required' => true, 'description' => ''},
950
+ ]
951
+ elsif type_code == 'customApi'
952
+ [
953
+ {'fieldContext' => 'config', 'fieldName' => 'endpoint', 'type' => 'text', 'fieldLabel' => 'API Endpoint', 'required' => true, 'description' => ''},
954
+ {'fieldContext' => 'config', 'fieldName' => 'apiStyle', 'type' => 'select', 'selectOptions' => ['Form URL Encoded [GET]','Form URL Encoded [POST]','JSON [POST]','XML [POST]','HTTP Basic [GET]'].collect {|it| { 'name' => it, 'value' => it} }, 'fieldLabel' => 'API Style', 'required' => true, 'description' => ''},
955
+ {'fieldContext' => 'config', 'fieldName' => 'encryptionAlgo', 'type' => 'select', 'selectOptions' => ['NONE','AES','DES','DESede','HmacSHA1', 'HmacSHA256'].collect {|it| { 'name' => it, 'value' => it} }, 'fieldLabel' => 'Encryption Algorithm', 'required' => true, 'description' => ''},
956
+ {'fieldContext' => 'config', 'fieldName' => 'encryptionKey', 'type' => 'text', 'fieldLabel' => 'Encryption Key', 'required' => true, 'description' => ''},
957
+ ]
958
+ else
959
+ print "unknown user source type: #{type_code}"
960
+ []
961
+ end
962
+ end
963
+ end