morpheus-cli 3.1.2.1 → 3.2.0

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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/lib/morpheus/api/api_client.rb +6 -2
  3. data/lib/morpheus/api/license_interface.rb +7 -0
  4. data/lib/morpheus/api/monitoring_apps_interface.rb +15 -2
  5. data/lib/morpheus/api/{checks_interface.rb → monitoring_checks_interface.rb} +8 -21
  6. data/lib/morpheus/api/monitoring_groups_interface.rb +23 -2
  7. data/lib/morpheus/api/{incidents_interface.rb → monitoring_incidents_interface.rb} +5 -5
  8. data/lib/morpheus/api/monitoring_interface.rb +4 -4
  9. data/lib/morpheus/api/user_groups_interface.rb +65 -0
  10. data/lib/morpheus/cli.rb +1 -0
  11. data/lib/morpheus/cli/curl_command.rb +9 -7
  12. data/lib/morpheus/cli/dot_file.rb +11 -5
  13. data/lib/morpheus/cli/echo_command.rb +27 -3
  14. data/lib/morpheus/cli/license.rb +109 -20
  15. data/lib/morpheus/cli/login.rb +2 -0
  16. data/lib/morpheus/cli/logout.rb +2 -0
  17. data/lib/morpheus/cli/mixins/monitoring_helper.rb +97 -37
  18. data/lib/morpheus/cli/mixins/print_helper.rb +5 -2
  19. data/lib/morpheus/cli/monitoring_apps_command.rb +564 -9
  20. data/lib/morpheus/cli/monitoring_checks_command.rb +326 -93
  21. data/lib/morpheus/cli/monitoring_contacts_command.rb +2 -2
  22. data/lib/morpheus/cli/monitoring_groups_command.rb +540 -10
  23. data/lib/morpheus/cli/monitoring_incidents_command.rb +88 -56
  24. data/lib/morpheus/cli/remote.rb +6 -0
  25. data/lib/morpheus/cli/roles.rb +1 -1
  26. data/lib/morpheus/cli/set_prompt_command.rb +1 -0
  27. data/lib/morpheus/cli/shell.rb +17 -8
  28. data/lib/morpheus/cli/user_groups_command.rb +574 -0
  29. data/lib/morpheus/cli/users.rb +221 -115
  30. data/lib/morpheus/cli/version.rb +1 -1
  31. data/morpheus-cli.gemspec +1 -2
  32. metadata +11 -8
@@ -5,14 +5,9 @@ class Morpheus::Cli::MonitoringIncidentsCommand
5
5
  include Morpheus::Cli::CliCommand
6
6
  include Morpheus::Cli::MonitoringHelper
7
7
 
8
- set_command_name :incidents # :'monitoring-incidents'
9
- register_subcommands :list, :stats, :get, :history, :notifications, :update, :close, :reopen, :quarantine
10
- set_default_subcommand :list
8
+ set_command_name :'monitor-incidents'
9
+ register_subcommands :list, :stats, :get, :history, :notifications, :update, :close, :reopen, :mute, :unmute
11
10
 
12
- def initialize()
13
- # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
14
- end
15
-
16
11
  def connect(opts)
17
12
  @api_client = establish_remote_appliance_connection(opts)
18
13
  @monitoring_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).monitoring
@@ -33,7 +28,7 @@ class Morpheus::Cli::MonitoringIncidentsCommand
33
28
  opts.on('--severity LIST', Array, "Filter by severity. critical, warning, info") do |list|
34
29
  params['severity'] = list
35
30
  end
36
- build_common_options(opts, options, [:list, :last_updated, :json, :csv, :fields, :json, :dry_run])
31
+ build_common_options(opts, options, [:list, :last_updated, :json, :yaml, :csv, :fields, :dry_run, :remote])
37
32
  end
38
33
  optparse.parse!(args)
39
34
  connect(options)
@@ -41,27 +36,26 @@ class Morpheus::Cli::MonitoringIncidentsCommand
41
36
  [:phrase, :offset, :max, :sort, :direction, :lastUpdated].each do |k|
42
37
  params[k] = options[k] unless options[k].nil?
43
38
  end
44
- # JD: lastUpdated 500ing, incidents don't have that property ? =o Fix it!
45
-
46
39
  if options[:dry_run]
47
40
  print_dry_run @monitoring_interface.incidents.dry.list(params)
48
41
  return
49
42
  end
50
-
51
43
  json_response = @monitoring_interface.incidents.list(params)
44
+ if options[:include_fields]
45
+ json_response = {"incidents" => filter_data(json_response["incidents"], options[:include_fields]) }
46
+ end
52
47
  if options[:json]
53
- if options[:include_fields]
54
- json_response = {"incidents" => filter_data(json_response["incidents"], options[:include_fields]) }
55
- end
56
48
  puts as_json(json_response, options)
57
49
  return 0
58
- end
59
- if options[:csv]
50
+ elsif options[:yaml]
51
+ puts as_yaml(json_response, options)
52
+ return 0
53
+ elsif options[:csv]
60
54
  puts records_as_csv(json_response['incidents'], options)
61
55
  return 0
62
56
  end
63
57
  incidents = json_response['incidents']
64
- title = "Morpheus Incidents"
58
+ title = "Morpheus Monitoring Incidents"
65
59
  subtitles = []
66
60
  # if group
67
61
  # subtitles << "Group: #{group['name']}".strip
@@ -104,7 +98,7 @@ class Morpheus::Cli::MonitoringIncidentsCommand
104
98
  opts.on( '-o', '--offset OFFSET', "Offset open incidents results for pagination." ) do |offset|
105
99
  options[:offset] = offset.to_i.abs
106
100
  end
107
- build_common_options(opts, options, [:json, :fields, :json, :dry_run])
101
+ build_common_options(opts, options, [:list, :json, :yaml, :csv, :fields, :dry_run, :remote])
108
102
  end
109
103
  optparse.parse!(args)
110
104
  connect(options)
@@ -186,7 +180,7 @@ class Morpheus::Cli::MonitoringIncidentsCommand
186
180
  opts.on(nil,'--notifications', "Display Incident Notifications") do |val|
187
181
  options[:show_notifications] = true
188
182
  end
189
- build_common_options(opts, options, [:json, :csv, :fields, :dry_run, :remote])
183
+ build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
190
184
  end
191
185
  optparse.parse!(args)
192
186
  if args.count < 1
@@ -211,14 +205,17 @@ class Morpheus::Cli::MonitoringIncidentsCommand
211
205
  json_response = @monitoring_interface.incidents.get(incident['id'])
212
206
  incident = json_response['incident']
213
207
 
208
+ if options[:include_fields]
209
+ json_response = {"incident" => filter_data(json_response["incident"], options[:include_fields]) }
210
+ end
214
211
  if options[:json]
215
- if options[:include_fields]
216
- json_response = {"incident" => filter_data(json_response["incident"], options[:include_fields]) }
217
- end
218
212
  puts as_json(json_response, options)
219
213
  return 0
214
+ elsif options[:yaml]
215
+ puts as_yaml(json_response, options)
216
+ return 0
220
217
  elsif options[:csv]
221
- puts records_as_csv([json_response['incident']], options)
218
+ puts records_as_csv(json_response['incident'], options)
222
219
  return 0
223
220
  end
224
221
 
@@ -232,6 +229,7 @@ class Morpheus::Cli::MonitoringIncidentsCommand
232
229
  "End" => lambda {|it| format_local_dt(it['endDate']) },
233
230
  "Duration" => lambda {|it| format_duration(it['startDate'], it['endDate']) },
234
231
  "Status" => lambda {|it| format_monitoring_issue_status(it) },
232
+ "Muted" => lambda {|it| it['inUptime'] ? 'No' : 'Yes' },
235
233
  "Visibility" => 'visibility',
236
234
  "Last Check" => lambda {|it| format_local_dt(it['lastCheckTime']) },
237
235
  "Last Error" => lambda {|it| it['lastError'] },
@@ -302,13 +300,10 @@ class Morpheus::Cli::MonitoringIncidentsCommand
302
300
  params = {}
303
301
  optparse = OptionParser.new do|opts|
304
302
  opts.banner = subcommand_usage("[id] [options]")
305
- # opts.on('--status LIST', Array, "Filter by status. open, closed") do |list|
306
- # params['status'] = list
307
- # end
308
303
  opts.on('--severity LIST', Array, "Filter by severity. critical, warning, info") do |list|
309
304
  params['severity'] = list
310
305
  end
311
- build_common_options(opts, options, [:list, :last_updated, :json, :csv, :fields, :json, :dry_run])
306
+ build_common_options(opts, options, [:list, :last_updated, :json, :csv, :fields, :json, :dry_run, :remote])
312
307
  end
313
308
  optparse.parse!(args)
314
309
  if args.count < 1
@@ -366,7 +361,7 @@ class Morpheus::Cli::MonitoringIncidentsCommand
366
361
  options = {}
367
362
  optparse = OptionParser.new do|opts|
368
363
  opts.banner = subcommand_usage("[id] [options]")
369
- build_common_options(opts, options, [:list, :json, :csv, :fields, :json, :dry_run])
364
+ build_common_options(opts, options, [:list, :json, :csv, :fields, :json, :dry_run, :remote])
370
365
  end
371
366
  optparse.parse!(args)
372
367
  if args.count < 1
@@ -505,55 +500,92 @@ class Morpheus::Cli::MonitoringIncidentsCommand
505
500
  end
506
501
 
507
502
 
508
- def quarantine(args)
503
+ def mute(args)
509
504
  options = {}
510
505
  params = {'enabled' => true}
511
- optparse = OptionParser.new do|opts|
512
- opts.banner = subcommand_usage("[id list]")
513
- # this one is a bit weird.. it's a way to toggle incident.inUptime
514
- # opts.on("--enabled BOOL", String, "Quarantine can be removed with --enabled false") do |val|
515
- # params['enabled'] = ['on','true'].include?(val.to_s.downcase)
516
- # end
517
- opts.on("-d", "--disabled", "Disable Quarantine instead") do
506
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
507
+ opts.banner = subcommand_usage("[id]")
508
+ opts.on(nil, "--disable", "Disable mute state instead, the same as unmute") do
518
509
  params['enabled'] = false
519
510
  end
520
- build_common_options(opts, options, [:json, :dry_run, :quiet])
511
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet, :remote])
512
+ opts.footer = "Mute an incident." + "\n" +
513
+ "[id] is required. This is the id of an incident."
521
514
  end
522
515
  optparse.parse!(args)
523
- if args.count < 1
516
+ if args.count != 1
524
517
  puts optparse
525
- exit 1
518
+ return 1
526
519
  end
527
520
  connect(options)
528
-
529
521
  begin
530
522
  incident = find_incident_by_id(args[0])
531
-
532
- if params.empty?
533
- print_red_alert "Specify atleast one option to update"
534
- puts optparse
535
- exit 1
523
+ # construct payload
524
+ payload = nil
525
+ if options[:payload]
526
+ payload = options[:payload]
527
+ else
528
+ payload = params
529
+ end
530
+ if options[:dry_run]
531
+ print_dry_run @monitoring_interface.incidents.dry.quarantine(incident["id"], payload)
532
+ return 0
533
+ end
534
+ json_response = @monitoring_interface.incidents.quarantine(incident["id"], payload)
535
+ if options[:json]
536
+ puts as_json(json_response, options)
537
+ elsif !options[:quiet]
538
+ if params['enabled']
539
+ print_green_success "Muted incident #{incident['id']}"
540
+ else
541
+ print_green_success "Unmuted incident #{incident['id']}"
542
+ end
543
+ _get(incident['id'], {})
536
544
  end
545
+ return 0
546
+ rescue RestClient::Exception => e
547
+ print_rest_exception(e, options)
548
+ exit 1
549
+ end
550
+ end
537
551
 
538
- # payload = {
539
- # 'incident' => {id: incident["id"]}
540
- # }
541
- # payload['incident'].merge!(params)
542
- payload = params
552
+ def unmute(args)
553
+ options = {}
554
+ params = {'enabled' => false}
555
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
556
+ opts.banner = subcommand_usage("[id]")
557
+ build_common_options(opts, options, [:payload, :json, :dry_run, :quiet, :remote])
558
+ opts.footer = "Unmute an incident." + "\n" +
559
+ "[id] is required. This is the id of an incident."
560
+ end
561
+ optparse.parse!(args)
562
+ if args.count != 1
563
+ puts optparse
564
+ return 1
565
+ end
566
+ connect(options)
543
567
 
568
+ begin
569
+ incident = find_incident_by_id(args[0])
570
+ # construct payload
571
+ payload = nil
572
+ if options[:payload]
573
+ payload = options[:payload]
574
+ else
575
+ payload = params
576
+ end
544
577
  if options[:dry_run]
545
- print_dry_run @monitoring_interface.incidents.dry.update(incident["id"], payload)
546
- return
578
+ print_dry_run @monitoring_interface.incidents.dry.quarantine(incident["id"], payload)
579
+ return 0
547
580
  end
548
-
549
- json_response = @monitoring_interface.incidents.update(incident["id"], payload)
581
+ json_response = @monitoring_interface.incidents.quarantine(incident["id"], payload)
550
582
  if options[:json]
551
583
  puts as_json(json_response, options)
552
584
  elsif !options[:quiet]
553
- print_green_success "Quarantined incident #{incident['id']}"
585
+ print_green_success "Unmuted incident #{incident['id']}"
554
586
  _get(incident['id'], {})
555
587
  end
556
-
588
+ return 0
557
589
  rescue RestClient::Exception => e
558
590
  print_rest_exception(e, options)
559
591
  exit 1
@@ -555,6 +555,8 @@ EOT
555
555
  end
556
556
  print_green_success "Deleted remote #{appliance_name}"
557
557
  list([])
558
+ # recalcuate echo vars
559
+ Morpheus::Cli::Echo.recalculate_variable_map()
558
560
  # recalculate shell prompt after this change
559
561
  if Morpheus::Cli::Shell.instance
560
562
  Morpheus::Cli::Shell.instance.reinitialize()
@@ -595,6 +597,8 @@ EOT
595
597
  appliance[:active] = true
596
598
  appliance = ::Morpheus::Cli::Remote.save_remote(appliance_name, appliance)
597
599
 
600
+ # recalcuate echo vars
601
+ Morpheus::Cli::Echo.recalculate_variable_map()
598
602
  # recalculate shell prompt after this change
599
603
  if Morpheus::Cli::Shell.instance
600
604
  Morpheus::Cli::Shell.instance.reinitialize()
@@ -623,6 +627,8 @@ EOT
623
627
  end
624
628
  Morpheus::Cli::Remote.clear_active_appliance()
625
629
  puts "You are no longer using the appliance #{@appliance_name}"
630
+ # recalcuate echo vars
631
+ Morpheus::Cli::Echo.recalculate_variable_map()
626
632
  # recalculate shell prompt after this change
627
633
  if Morpheus::Cli::Shell.instance
628
634
  Morpheus::Cli::Shell.instance.reinitialize()
@@ -151,7 +151,7 @@ class Morpheus::Cli::Roles
151
151
  print cyan
152
152
  description_cols = {
153
153
  "ID" => 'id',
154
- "Name" => 'name',
154
+ "Name" => 'authority',
155
155
  "Description" => 'description',
156
156
  "Scope" => lambda {|it| it['scope'] },
157
157
  "Type" => lambda {|it| format_role_type(it) },
@@ -43,6 +43,7 @@ EOT
43
43
  self.my_terminal.prompt = args[0]
44
44
  # Morpheus::Terminal.instance.prompt = args[0]
45
45
  Morpheus::Cli::Shell.instance.recalculate_prompt()
46
+ # Morpheus::Cli::Echo.recalculate_variable_map()
46
47
 
47
48
  return true
48
49
  end
@@ -63,14 +63,15 @@ class Morpheus::Cli::Shell
63
63
  #@prompt = Morpheus::Terminal.instance.prompt.to_s #.dup
64
64
  @prompt = my_terminal.prompt
65
65
 
66
- var_map = {
67
- '%cyan' => cyan, '%magenta' => magenta, '%reset' => reset, '%dark' => dark,
68
- '%remote' => @appliance_name.to_s, '%username' => @current_username.to_s,
69
- '%remote_url' => @appliance_url.to_s
70
- }
66
+ variable_map = Morpheus::Cli::Echo.variable_map
67
+ # variable_map = {
68
+ # '%cyan' => cyan, '%magenta' => magenta, '%red' => red, '%mgreen' => green, '%yellow' => yellow, '%dark' => dark, '%reset' => reset,
69
+ # '%remote' => @appliance_name.to_s, '%remote_url' => @appliance_url.to_s,
70
+ # '%username' => @current_username.to_s
71
+ # }
71
72
  @calculated_prompt = @prompt.to_s.dup
72
- var_map.each do |var_key, var_value|
73
- @calculated_prompt.gsub!(var_key.to_s, var_value.to_s)
73
+ variable_map.each do |k, v|
74
+ @calculated_prompt.gsub!(k.to_s, v.to_s)
74
75
  end
75
76
  # cleanup empty brackets caused by var value
76
77
  @calculated_prompt = @calculated_prompt.gsub("[]", "").gsub("<>", "").gsub("{}", "")
@@ -237,11 +238,19 @@ class Morpheus::Cli::Shell
237
238
  n_commands = input.sub(/^history\s?/, '').sub(/\-n\s?/, '')
238
239
  n_commands = n_commands.empty? ? 25 : n_commands.to_i
239
240
  cmd_numbers = @history.keys.last(n_commands)
240
- puts "Last #{cmd_numbers.size} commands"
241
+ if cmd_numbers.size == 1
242
+ puts "Last command"
243
+ else
244
+ puts "Last #{cmd_numbers.size} commands"
245
+ end
241
246
  cmd_numbers.each do |cmd_number|
242
247
  cmd = @history[cmd_number]
243
248
  puts "#{cmd_number.to_s.rjust(3, ' ')} #{cmd}"
244
249
  end
250
+ last_cmd = cmd_numbers.last ? @history[cmd_numbers.last] : nil
251
+ if input != last_cmd # no consecutive
252
+ log_history_command(input)
253
+ end
245
254
  return 0
246
255
  elsif input == 'clear'
247
256
  print "\e[H\e[2J"
@@ -0,0 +1,574 @@
1
+ require 'morpheus/cli/cli_command'
2
+ require 'morpheus/cli/mixins/accounts_helper'
3
+
4
+ class Morpheus::Cli::UserGroupsCommand
5
+ include Morpheus::Cli::CliCommand
6
+ include Morpheus::Cli::AccountsHelper
7
+
8
+ set_command_name :'user-groups'
9
+ register_subcommands :list, :get, :add, :update, :remove
10
+ register_subcommands :'add-user' => :add_user
11
+ register_subcommands :'remove-user' => :remove_user
12
+
13
+ def connect(opts)
14
+ @api_client = establish_remote_appliance_connection(opts)
15
+ @user_groups_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).user_groups
16
+ @users_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).users
17
+ @accounts_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).accounts
18
+ end
19
+
20
+ def handle(args)
21
+ handle_subcommand(args)
22
+ end
23
+
24
+ def list(args)
25
+ options = {}
26
+ params = {}
27
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
28
+ opts.banner = subcommand_usage()
29
+ build_common_options(opts, options, [:list, :json, :yaml, :csv, :fields, :dry_run, :remote])
30
+ end
31
+ optparse.parse!(args)
32
+ connect(options)
33
+ begin
34
+ [:phrase, :offset, :max, :sort, :direction, :lastUpdated].each do |k|
35
+ params[k] = options[k] unless options[k].nil?
36
+ end
37
+
38
+ if options[:dry_run]
39
+ print_dry_run @user_groups_interface.dry.list(nil, params)
40
+ return
41
+ end
42
+
43
+ json_response = @user_groups_interface.list(nil, params)
44
+ if options[:include_fields]
45
+ json_response = {"userGroups" => filter_data(json_response["userGroups"], options[:include_fields]) }
46
+ end
47
+ if options[:json]
48
+ puts as_json(json_response, options)
49
+ return 0
50
+ elsif options[:csv]
51
+ puts records_as_csv(json_response['userGroups'], options)
52
+ return 0
53
+ elsif options[:yaml]
54
+ puts as_yaml(json_response, options)
55
+ return 0
56
+ end
57
+ user_groups = json_response['userGroups']
58
+ title = "Morpheus User Groups"
59
+ subtitles = []
60
+ # if group
61
+ # subtitles << "Group: #{group['name']}".strip
62
+ # end
63
+ # if cloud
64
+ # subtitles << "Cloud: #{cloud['name']}".strip
65
+ # end
66
+ if params[:phrase]
67
+ subtitles << "Search: #{params[:phrase]}".strip
68
+ end
69
+ print_h1 title, subtitles
70
+ if user_groups.empty?
71
+ print cyan,"No user groups found.",reset,"\n"
72
+ else
73
+ print_user_groups_table(user_groups, options)
74
+ print_results_pagination(json_response, {:label => "user group", :n_label => "user groups"})
75
+ # print_results_pagination(json_response)
76
+ end
77
+ print reset,"\n"
78
+ rescue RestClient::Exception => e
79
+ print_rest_exception(e, options)
80
+ exit 1
81
+ end
82
+ end
83
+
84
+ def get(args)
85
+ options = {}
86
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
87
+ opts.banner = subcommand_usage("[name]")
88
+ build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
89
+ end
90
+ optparse.parse!(args)
91
+ if args.count < 1
92
+ puts optparse
93
+ exit 1
94
+ end
95
+ connect(options)
96
+ id_list = parse_id_list(args)
97
+ return run_command_for_each_arg(id_list) do |arg|
98
+ _get(arg, options)
99
+ end
100
+ end
101
+
102
+ def _get(id, options)
103
+
104
+ begin
105
+ user_group = find_user_group_by_name_or_id(nil, id)
106
+ if user_group.nil?
107
+ return 1
108
+ end
109
+ if options[:dry_run]
110
+ print_dry_run @user_groups_interface.dry.get(nil, user_group['id'])
111
+ return
112
+ end
113
+ json_response = @user_groups_interface.get(nil, user_group['id'])
114
+ user_group = json_response['userGroup']
115
+ users = user_group['users'] || []
116
+ if options[:include_fields]
117
+ json_response = {"userGroup" => filter_data(json_response["userGroup"], options[:include_fields]) }
118
+ end
119
+ if options[:json]
120
+ puts as_json(json_response, options)
121
+ return 0
122
+ elsif options[:yaml]
123
+ puts as_yaml(json_response, options)
124
+ return 0
125
+ elsif options[:csv]
126
+ puts records_as_csv([json_response['userGroup']], options)
127
+ return 0
128
+ end
129
+
130
+ print_h1 "User Group Details"
131
+ print cyan
132
+ description_cols = {
133
+ "ID" => lambda {|it| it['id'] },
134
+ #"Account" => lambda {|it| it['account'] ? it['account']['name'] : '' },
135
+ "Name" => lambda {|it| it['name'] },
136
+ "Description" => lambda {|it| it['description'] },
137
+ "Server Group" => lambda {|it| it['serverGroup'] },
138
+ "Sudo Access" => lambda {|it| format_boolean it['sudoAccess'] },
139
+ # "Shared User" => lambda {|it| format_boolean it['sharedUser'] },
140
+ "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
141
+ "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
142
+ }
143
+ print_description_list(description_cols, user_group)
144
+
145
+ ## Users
146
+ if users.size == 1
147
+ print_h2 "User (1)"
148
+ else
149
+ print_h2 "Users (#{users.size})"
150
+ end
151
+ if users.size == 0
152
+ print yellow,"No users",reset,"\n"
153
+ else
154
+ user_columns = [
155
+ {"ID" => lambda {|user| user['id'] } },
156
+ {"USERNAME" => lambda {|user| user['username'] } },
157
+ {"NAME" => lambda {|user| user['displayName'] } },
158
+ ]
159
+ print as_pretty_table(users, user_columns)
160
+ end
161
+
162
+ print reset,"\n"
163
+
164
+ rescue RestClient::Exception => e
165
+ print_rest_exception(e, options)
166
+ exit 1
167
+ end
168
+ end
169
+
170
+ def add(args)
171
+ options = {}
172
+ params = {}
173
+ user_ids = nil
174
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
175
+ opts.banner = subcommand_usage("[name]")
176
+ opts.on('--name VALUE', String, "Name") do |val|
177
+ params['name'] = val
178
+ end
179
+ opts.on('--description VALUE', String, "Description") do |val|
180
+ params['description'] = val
181
+ end
182
+ opts.on('--sudoUser [on|off]', String, "Sudo Access") do |val|
183
+ params['sudoUser'] = val.nil? || val.to_s == 'on' || val.to_s == 'true'
184
+ end
185
+ opts.on('--serverGroup VALUE', String, "Server Group") do |val|
186
+ params['name'] = val
187
+ end
188
+ opts.on('--users LIST', Array, "Users to include in this group, comma separated list of IDs or usernames") do |list|
189
+ if list.size == 1 && list[0] == 'null' # hacky way to clear it
190
+ user_ids = []
191
+ else
192
+ user_ids = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
193
+ end
194
+ end
195
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote, :quiet])
196
+ opts.footer = "Create a new user group." + "\n" +
197
+ "[name] is required and can be passed as --name instead."
198
+ end
199
+ optparse.parse!(args)
200
+ if args.count > 1
201
+ print_error Morpheus::Terminal.angry_prompt
202
+ puts_error "wrong number of arguments, expected 0-1 and got #{args.count}\n#{optparse}"
203
+ return 1
204
+ end
205
+ # support [name] as first argument
206
+ if args[0]
207
+ params['name'] = args[0]
208
+ end
209
+ connect(options)
210
+ begin
211
+ # construct payload
212
+ payload = nil
213
+ if options[:payload]
214
+ payload = options[:payload]
215
+ else
216
+ # merge -O options into normally parsed options
217
+ params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
218
+ users = []
219
+ if user_ids
220
+ user_ids.each do |user_id|
221
+ user = find_user_by_username_or_id(nil, user_id)
222
+ return 1 if user.nil?
223
+ users << user
224
+ end
225
+ params['users'] = users.collect {|it| it['id'] }
226
+ end
227
+ # todo: prompt?
228
+ payload = {'userGroup' => params}
229
+ end
230
+ if options[:dry_run]
231
+ print_dry_run @user_groups_interface.dry.create(nil, payload)
232
+ return
233
+ end
234
+ json_response = @user_groups_interface.create(nil, payload)
235
+ if options[:json]
236
+ puts as_json(json_response, options)
237
+ elsif !options[:quiet]
238
+ user_group = json_response['userGroup']
239
+ print_green_success "Added user group #{user_group['name']}"
240
+ _get(user_group['id'], {})
241
+ end
242
+ return 0
243
+ rescue RestClient::Exception => e
244
+ print_rest_exception(e, options)
245
+ exit 1
246
+ end
247
+ end
248
+
249
+
250
+ def update(args)
251
+ options = {}
252
+ params = {}
253
+ user_ids = nil
254
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
255
+ opts.banner = subcommand_usage("[name]")
256
+ opts.on('--name VALUE', String, "Name for this user group") do |val|
257
+ params['name'] = val
258
+ end
259
+ opts.on('--description VALUE', String, "Description") do |val|
260
+ params['description'] = val
261
+ end
262
+ opts.on('--sudoUser [on|off]', String, "Sudo Access. Default is off.") do |val|
263
+ params['sudoUser'] = val.nil? || val.to_s == 'on' || val.to_s == 'true'
264
+ end
265
+ opts.on('--users LIST', Array, "Users to include in this group, comma separated list of IDs or usernames") do |list|
266
+ if list.size == 1 && list[0] == 'null' # hacky way to clear it
267
+ user_ids = []
268
+ else
269
+ user_ids = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
270
+ end
271
+ end
272
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote, :quiet])
273
+ opts.footer = "Update a user group." + "\n" +
274
+ "[name] is required. This is the name or id of a user group."
275
+ end
276
+ optparse.parse!(args)
277
+ if args.count != 1
278
+ print_error Morpheus::Terminal.angry_prompt
279
+ puts_error "wrong number of arguments, expected 1 and got #{args.count}\n#{optparse}"
280
+ return 1
281
+ end
282
+ connect(options)
283
+ begin
284
+ user_group = find_user_group_by_name_or_id(nil, args[0])
285
+ if user_group.nil?
286
+ return 1
287
+ end
288
+ # construct payload
289
+ payload = nil
290
+ if options[:payload]
291
+ payload = options[:payload]
292
+ else
293
+ # merge -O options into normally parsed options
294
+ params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
295
+ if user_ids
296
+ users = []
297
+ user_ids.each do |user_id|
298
+ user = find_user_by_username_or_id(nil, user_id)
299
+ return 1 if user.nil?
300
+ users << user
301
+ end
302
+ params['users'] = users.collect {|it| it['id'] }
303
+ else
304
+ # prevent server from clearing this!
305
+ params['users'] = user_group['users'] #.collect {|it| it['id'] }
306
+ end
307
+ # todo: prompt?
308
+ payload = {'userGroup' => params}
309
+ end
310
+ if options[:dry_run]
311
+ print_dry_run @user_groups_interface.dry.update(nil, user_group["id"], payload)
312
+ return
313
+ end
314
+ json_response = @user_groups_interface.update(nil, user_group["id"], payload)
315
+ if options[:json]
316
+ puts as_json(json_response, options)
317
+ elsif !options[:quiet]
318
+ print_green_success "Updated user group #{user_group['name']}"
319
+ _get(user_group['id'], {})
320
+ end
321
+ return 0
322
+ rescue RestClient::Exception => e
323
+ print_rest_exception(e, options)
324
+ exit 1
325
+ end
326
+ end
327
+
328
+ def add_user(args)
329
+ options = {}
330
+ params = {}
331
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
332
+ opts.banner = subcommand_usage("[name] [user]")
333
+ build_common_options(opts, options, [:json, :dry_run, :remote, :quiet])
334
+ opts.footer = "Add a user to a user group.\n" +
335
+ "[name] is required. This is the name or id of a user group.\n" +
336
+ "[user] is required. This is the username or id of a user. More than one can be passed."
337
+ end
338
+ optparse.parse!(args)
339
+ if args.count < 2
340
+ puts optparse
341
+ return 1
342
+ end
343
+ connect(options)
344
+ begin
345
+ user_group = find_user_group_by_name_or_id(nil, args[0])
346
+ if user_group.nil?
347
+ return 1
348
+ end
349
+
350
+
351
+ # construct payload
352
+ payload = nil
353
+ if options[:payload]
354
+ payload = options[:payload]
355
+ else
356
+ user_ids = args[1..-1]
357
+ users = []
358
+ user_ids.each do |user_id|
359
+ user = find_user_by_username_or_id(nil, user_id)
360
+ return 1 if user.nil?
361
+ users << user
362
+ end
363
+ add_user_ids = users.collect {|it| it['id'] }
364
+ current_users = (user_group['users'] || [])
365
+ current_user_ids = current_users.collect {|it| it['id'] }
366
+ new_user_ids = (current_user_ids + add_user_ids).uniq
367
+ user_group_payload = {} # user_group
368
+ user_group_payload['users'] = new_user_ids
369
+ payload = {'userGroup' => user_group_payload}
370
+ end
371
+ if options[:dry_run]
372
+ print_dry_run @user_groups_interface.dry.update(nil, user_group["id"], payload)
373
+ return 0
374
+ end
375
+ json_response = @user_groups_interface.update(nil, user_group["id"], payload)
376
+ if options[:json]
377
+ puts as_json(json_response, options)
378
+ elsif !options[:quiet]
379
+ if users.size == 1
380
+ print_green_success "Added #{users[0]['username']} to user group #{user_group['name']}"
381
+ else
382
+ print_green_success "Added #{users.size} users to user group #{user_group['name']}"
383
+ end
384
+ _get(user_group['id'], {})
385
+ end
386
+ return 0
387
+ rescue RestClient::Exception => e
388
+ print_rest_exception(e, options)
389
+ exit 1
390
+ end
391
+ end
392
+
393
+ def remove_user(args)
394
+ options = {}
395
+ params = {}
396
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
397
+ opts.banner = subcommand_usage("[name] [user]")
398
+ build_common_options(opts, options, [:json, :dry_run, :remote, :quiet])
399
+ opts.footer = "Remove a user from a user group.\n" +
400
+ "[name] is required. This is the name or id of a user group.\n" +
401
+ "[user] is required. This is the username or id of a user. More than one can be passed."
402
+ end
403
+ optparse.parse!(args)
404
+ if args.count < 2
405
+ puts optparse
406
+ return 1
407
+ end
408
+ connect(options)
409
+ begin
410
+ user_group = find_user_group_by_name_or_id(nil, args[0])
411
+ if user_group.nil?
412
+ return 1
413
+ end
414
+
415
+ # construct payload
416
+ payload = nil
417
+ if options[:payload]
418
+ payload = options[:payload]
419
+ else
420
+ user_ids = args[1..-1]
421
+ users = []
422
+ user_ids.each do |user_id|
423
+ user = find_user_by_username_or_id(nil, user_id)
424
+ return 1 if user.nil?
425
+ users << user
426
+ end
427
+ remove_user_ids = users.collect {|it| it['id'] }
428
+ current_users = (user_group['users'] || [])
429
+ current_user_ids = current_users.collect {|it| it['id'] }
430
+ new_user_ids = (current_user_ids - remove_user_ids).uniq
431
+ user_group_payload = {} # user_group
432
+ user_group_payload['users'] = new_user_ids
433
+ payload = {'userGroup' => user_group_payload}
434
+ end
435
+ if options[:dry_run]
436
+ print_dry_run @user_groups_interface.dry.update(nil, user_group["id"], payload)
437
+ return 0
438
+ end
439
+ json_response = @user_groups_interface.update(nil, user_group["id"], payload)
440
+ if options[:json]
441
+ puts as_json(json_response, options)
442
+ elsif !options[:quiet]
443
+ if users.size == 1
444
+ print_green_success "Added #{users[0]['username']} to user group #{user_group['name']}"
445
+ else
446
+ print_green_success "Added #{users.size} users to user group #{user_group['name']}"
447
+ end
448
+ _get(user_group['id'], {})
449
+ end
450
+ return 0
451
+ rescue RestClient::Exception => e
452
+ print_rest_exception(e, options)
453
+ exit 1
454
+ end
455
+ end
456
+
457
+ def remove(args)
458
+ options = {}
459
+ params = {}
460
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
461
+ opts.banner = subcommand_usage("[name]")
462
+ build_common_options(opts, options, [:json, :dry_run, :quiet, :auto_confirm])
463
+ end
464
+ optparse.parse!(args)
465
+ if args.count < 1
466
+ puts optparse
467
+ return 127
468
+ end
469
+ connect(options)
470
+
471
+ begin
472
+ user_group = find_user_group_by_name_or_id(nil, args[0])
473
+ if user_group.nil?
474
+ return 1
475
+ end
476
+
477
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to delete user group '#{user_group['name']}'?", options)
478
+ return false
479
+ end
480
+
481
+ # payload = {
482
+ # 'userGroup' => {id: user_group["id"]}
483
+ # }
484
+ # payload['userGroup'].merge!(user_group)
485
+ payload = params
486
+
487
+ if options[:dry_run]
488
+ print_dry_run @user_groups_interface.dry.destroy(nil, user_group["id"])
489
+ return
490
+ end
491
+
492
+ json_response = @user_groups_interface.destroy(nil, user_group["id"])
493
+ if options[:json]
494
+ puts as_json(json_response, options)
495
+ elsif !options[:quiet]
496
+ print_green_success "Deleted user group #{user_group['name']}"
497
+ end
498
+ return 0, nil
499
+ rescue RestClient::Exception => e
500
+ print_rest_exception(e, options)
501
+ return 1
502
+ end
503
+ end
504
+
505
+
506
+ private
507
+
508
+ def print_user_groups_table(user_groups, opts={})
509
+ columns = [
510
+ {"ID" => lambda {|user_group| user_group['id'] } },
511
+ {"NAME" => lambda {|user_group| user_group['name'] } },
512
+ {"DESCRIPTION" => lambda {|user_group| user_group['description'] } },
513
+ {"USERS" => lambda {|user_group|
514
+ users = user_group['users']
515
+ if users
516
+ n_users = 3
517
+ if users.size > n_users
518
+ users.first(n_users).collect { |user| user['username'] }.join(", ") + ", (#{users.size - n_users} more)"
519
+ else
520
+ users.collect { |user| user['username'] }.join(", ")
521
+ end
522
+ else
523
+ ""
524
+ end
525
+ } },
526
+ # {"KEY PAIR" => lambda {|user_group|
527
+ # user_group['keyPair'] ? user_group['keyPair']['name'] : user_group['sharedKeyPairId']
528
+ # } },
529
+ # {"SUDO" => lambda {|user_group| format_boolean(user_group['sudoUser']) } },
530
+ ]
531
+ if opts[:include_fields]
532
+ columns = opts[:include_fields]
533
+ end
534
+ print as_pretty_table(user_groups, columns, opts)
535
+ end
536
+
537
+ def find_user_group_by_name_or_id(account_id, val)
538
+ if val.to_s =~ /\A\d{1,}\Z/
539
+ return find_user_group_by_id(account_id, val)
540
+ else
541
+ return find_user_group_by_name(account_id, val)
542
+ end
543
+ end
544
+
545
+ def find_user_group_by_id(account_id, id)
546
+ begin
547
+ json_response = @user_groups_interface.get(account_id, id.to_i)
548
+ return json_response['userGroup']
549
+ rescue RestClient::Exception => e
550
+ if e.response && e.response.code == 404
551
+ print_red_alert "User Group not found by id #{id}"
552
+ else
553
+ raise e
554
+ end
555
+ end
556
+ end
557
+
558
+ def find_user_group_by_name(account_id, name)
559
+ user_groups = @user_groups_interface.list(account_id, {name: name.to_s})['userGroups']
560
+ if user_groups.empty?
561
+ print_red_alert "User Group not found by name #{name}"
562
+ return nil
563
+ elsif user_groups.size > 1
564
+ print_red_alert "#{user_groups.size} user groups found by name #{name}"
565
+ print_user_groups_table(user_groups, {color: red})
566
+ print_red_alert "Try using ID instead"
567
+ print reset,"\n"
568
+ return nil
569
+ else
570
+ return user_groups[0]
571
+ end
572
+ end
573
+
574
+ end