morpheus-cli 0.9.9 → 0.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/morpheus/api/api_client.rb +4 -0
  3. data/lib/morpheus/api/app_templates_interface.rb +74 -0
  4. data/lib/morpheus/api/instance_types_interface.rb +9 -0
  5. data/lib/morpheus/api/instances_interface.rb +16 -0
  6. data/lib/morpheus/api/roles_interface.rb +37 -1
  7. data/lib/morpheus/cli.rb +4 -1
  8. data/lib/morpheus/cli/accounts.rb +82 -58
  9. data/lib/morpheus/cli/app_templates.rb +908 -0
  10. data/lib/morpheus/cli/apps.rb +226 -187
  11. data/lib/morpheus/cli/cli_command.rb +57 -30
  12. data/lib/morpheus/cli/clouds.rb +50 -65
  13. data/lib/morpheus/cli/deployments.rb +18 -33
  14. data/lib/morpheus/cli/deploys.rb +1 -3
  15. data/lib/morpheus/cli/groups.rb +54 -38
  16. data/lib/morpheus/cli/hosts.rb +86 -80
  17. data/lib/morpheus/cli/instance_types.rb +42 -29
  18. data/lib/morpheus/cli/instances.rb +192 -69
  19. data/lib/morpheus/cli/key_pairs.rb +70 -87
  20. data/lib/morpheus/cli/license.rb +7 -9
  21. data/lib/morpheus/cli/load_balancers.rb +23 -53
  22. data/lib/morpheus/cli/mixins/accounts_helper.rb +7 -8
  23. data/lib/morpheus/cli/mixins/print_helper.rb +67 -0
  24. data/lib/morpheus/cli/mixins/provisioning_helper.rb +461 -0
  25. data/lib/morpheus/cli/option_types.rb +71 -18
  26. data/lib/morpheus/cli/roles.rb +725 -34
  27. data/lib/morpheus/cli/security_group_rules.rb +50 -70
  28. data/lib/morpheus/cli/security_groups.rb +61 -48
  29. data/lib/morpheus/cli/shell.rb +123 -14
  30. data/lib/morpheus/cli/tasks.rb +24 -59
  31. data/lib/morpheus/cli/users.rb +86 -71
  32. data/lib/morpheus/cli/version.rb +1 -1
  33. data/lib/morpheus/cli/virtual_images.rb +21 -51
  34. data/lib/morpheus/cli/workflows.rb +14 -29
  35. data/lib/morpheus/ext/nil_class.rb +5 -0
  36. data/lib/morpheus/formatters.rb +1 -0
  37. metadata +7 -3
  38. data/lib/morpheus/cli/error_handler.rb +0 -44
@@ -24,7 +24,7 @@ module Morpheus
24
24
  end
25
25
  end
26
26
 
27
- def self.prompt(option_types, options={}, api_client=nil,api_params={})
27
+ def self.prompt(option_types, options={}, api_client=nil,api_params={}, no_prompt=false)
28
28
  results = {}
29
29
  options = options || {}
30
30
  # puts "Options Prompt #{options}"
@@ -37,15 +37,51 @@ module Morpheus
37
37
  context_map = results[option_type['fieldContext']]
38
38
  if options[option_type['fieldContext']] and options[option_type['fieldContext']].key?(option_type['fieldName'])
39
39
  value = options[option_type['fieldContext']][option_type['fieldName']]
40
+ if option_type['type'] == 'number'
41
+ value = value.to_i
42
+ end
40
43
  value_found = true
41
44
  end
42
45
  end
43
46
 
44
47
  if value_found == false && options.key?(option_type['fieldName'])
45
48
  value = options[option_type['fieldName']]
49
+ if option_type['type'] == 'number'
50
+ value = value.to_i
51
+ end
46
52
  value_found = true
47
53
  end
48
54
 
55
+ # no_prompt means skip prompting and instead
56
+ # use default value or error if a required option is not present
57
+ no_prompt = no_prompt || options[:no_prompt]
58
+ if no_prompt
59
+ if !value_found
60
+ if option_type['defaultValue']
61
+ value = option_type['defaultValue']
62
+ value_found = true
63
+ end
64
+ if !value_found
65
+ # select type is special because it supports skipSingleOption
66
+ # and prints the available options on error
67
+ if option_type['type'] == 'select'
68
+ value = select_prompt(option_type, api_client, api_params, true)
69
+ value_found = !!value
70
+ end
71
+ if !value_found
72
+ if option_type['required']
73
+ print Term::ANSIColor.red, "\nMissing Required Option\n\n"
74
+ print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - ", Term::ANSIColor.reset , "#{option_type['description']}\n"
75
+ print "\n"
76
+ exit 1
77
+ else
78
+ next
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+
49
85
  if !value_found
50
86
  if option_type['type'] == 'number'
51
87
  value = number_prompt(option_type)
@@ -115,7 +151,7 @@ module Morpheus
115
151
  value_found = false
116
152
  value = nil
117
153
  while !value_found do
118
- print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}: "
154
+ print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{option_type['defaultValue'] ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
119
155
  input = $stdin.gets.chomp!
120
156
  value = input.empty? ? option_type['defaultValue'] : input.to_i
121
157
  if input == '?'
@@ -127,36 +163,53 @@ module Morpheus
127
163
  return value
128
164
  end
129
165
 
130
- def self.select_prompt(option_type,api_client, api_params={})
166
+ def self.select_prompt(option_type,api_client, api_params={}, no_prompt=false)
131
167
  value_found = false
132
168
  value = nil
133
- if option_type['optionSource']
134
- source_options = load_source_options(option_type['optionSource'],api_client,api_params)
169
+ if option_type['selectOptions']
170
+ select_options = option_type['selectOptions']
171
+ elsif option_type['optionSource']
172
+ select_options = load_source_options(option_type['optionSource'],api_client,api_params)
173
+ else
174
+ raise "select_prompt() requires selectOptions or optionSource!"
135
175
  end
136
- if !source_options.nil? && !source_options.count == 1 && option_type['skipSingleOption'] == true
176
+ if !select_options.nil? && select_options.count == 1 && option_type['skipSingleOption'] == true
137
177
  value_found = true
138
- value = source_option['value']
178
+ value = select_options[0]['value']
179
+ end
180
+ if no_prompt
181
+ if !value_found
182
+ if option_type['required']
183
+ print Term::ANSIColor.red, "\nMissing Required Option\n\n"
184
+ print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - ", Term::ANSIColor.reset , "#{option_type['description']}\n"
185
+ display_select_options(select_options)
186
+ print "\n"
187
+ exit 1
188
+ else
189
+ return nil
190
+ end
191
+ end
139
192
  end
140
193
  while !value_found do
141
194
  Readline.completion_append_character = ""
142
195
  Readline.basic_word_break_characters = ''
143
- Readline.completion_proc = proc {|s| source_options.clone.collect{|opt| opt['name']}.grep(/^#{Regexp.escape(s)}/)}
144
- input = Readline.readline("#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''} ['?' for options]: ", false).to_s
196
+ Readline.completion_proc = proc {|s| select_options.clone.collect{|opt| opt['name']}.grep(/^#{Regexp.escape(s)}/)}
197
+ input = Readline.readline("#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{option_type['defaultValue'] ? ' ['+option_type['defaultValue'].to_s+']' : ''} ['?' for options]: ", false).to_s
145
198
  input = input.chomp.strip
146
- if option_type['optionSource']
147
- source_option = source_options.find{|b| b['name'] == input || (!b['value'].nil? && b['value'].to_s == input) || (b['value'].nil? && input.empty?)}
148
- if source_option
149
- value = source_option['value']
199
+ if input.empty?
200
+ value = option_type['defaultValue']
201
+ else
202
+ select_option = select_options.find{|b| b['name'] == input || (!b['value'].nil? && b['value'].to_s == input) || (b['value'].nil? && input.empty?)}
203
+ if select_option
204
+ value = select_option['value']
150
205
  elsif !input.nil? && !input.empty?
151
206
  input = '?'
152
207
  end
153
- else
154
- value = input.empty? ? option_type['defaultValue'] : input
155
208
  end
156
209
 
157
210
  if input == '?'
158
211
  help_prompt(option_type)
159
- display_select_options(source_options)
212
+ display_select_options(select_options)
160
213
  elsif !value.nil? || option_type['required'] != true
161
214
  value_found = true
162
215
  end
@@ -190,7 +243,7 @@ module Morpheus
190
243
  value_found = false
191
244
  value = nil
192
245
  while !value_found do
193
- print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}: "
246
+ print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{option_type['defaultValue'] ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
194
247
  input = $stdin.gets.chomp!
195
248
  value = input.empty? ? option_type['defaultValue'] : input
196
249
  if input == '?'
@@ -242,7 +295,7 @@ module Morpheus
242
295
  end
243
296
 
244
297
  def self.help_prompt(option_type)
245
- print Term::ANSIColor.green," * #{option_type['fieldLabel']} [-O #{option_type['fieldName']}=] - ", Term::ANSIColor.reset , "#{option_type['description']}\n"
298
+ print Term::ANSIColor.green," * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - ", Term::ANSIColor.reset , "#{option_type['description']}\n"
246
299
  end
247
300
 
248
301
 
@@ -1,7 +1,6 @@
1
1
  # require 'yaml'
2
2
  require 'io/console'
3
3
  require 'rest_client'
4
- require 'term/ansicolor'
5
4
  require 'optparse'
6
5
  require 'morpheus/cli/cli_command'
7
6
  require 'morpheus/cli/option_types'
@@ -9,7 +8,6 @@ require 'morpheus/cli/mixins/accounts_helper'
9
8
  require 'json'
10
9
 
11
10
  class Morpheus::Cli::Roles
12
- include Term::ANSIColor
13
11
  include Morpheus::Cli::CliCommand
14
12
  include Morpheus::Cli::AccountsHelper
15
13
 
@@ -21,18 +19,25 @@ class Morpheus::Cli::Roles
21
19
  def connect(opts)
22
20
  @access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).request_credentials()
23
21
  if @access_token.empty?
24
- print red,bold, "\nInvalid Credentials. Unable to acquire access token. Please verify your credentials and try again.\n\n",reset
22
+ print_red_alert "Invalid Credentials. Unable to acquire access token. Please verify your credentials and try again."
25
23
  exit 1
26
24
  end
27
25
  @api_client = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url)
28
26
  @users_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).users
29
27
  @accounts_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).accounts
30
28
  @roles_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).roles
29
+ @active_groups = ::Morpheus::Cli::Groups.load_group_file
30
+ @groups_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).groups
31
+ @options_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).options
32
+ #@clouds_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instance_types
33
+ @instance_types_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instance_types
34
+
31
35
  end
32
36
 
33
37
  def handle(args)
38
+ usage = "Usage: morpheus roles [list,details,add,update,remove,update-feature-access,update-global-group-access,update-group-access,update-global-cloud-access,update-cloud-access,update-global-instance-type-access,update-instance-type-access] [name]"
34
39
  if args.empty?
35
- puts "\nUsage: morpheus roles [list]\n\n"
40
+ puts "\n#{usage}\n\n"
36
41
  exit 1
37
42
  end
38
43
 
@@ -43,32 +48,46 @@ class Morpheus::Cli::Roles
43
48
  details(args[1..-1])
44
49
  when 'add'
45
50
  add(args[1..-1])
51
+ when 'update'
52
+ update(args[1..-1])
46
53
  when 'remove'
47
54
  remove(args[1..-1])
55
+ when 'update-feature-access'
56
+ update_feature_access(args[1..-1])
57
+ when 'update-global-group-access'
58
+ update_global_group_access(args[1..-1])
59
+ when 'update-group-access'
60
+ update_group_access(args[1..-1])
61
+ when 'update-global-cloud-access'
62
+ update_global_cloud_access(args[1..-1])
63
+ when 'update-cloud-access'
64
+ update_cloud_access(args[1..-1])
65
+ when 'update-global-instance-type-access'
66
+ update_global_instance_type_access(args[1..-1])
67
+ when 'update-instance-type-access'
68
+ update_instance_type_access(args[1..-1])
48
69
  else
49
- puts "\nUsage: morpheus hosts [list] \n\n"
70
+ puts "\n#{usage}\n\n"
50
71
  exit 127
51
72
  end
52
73
  end
53
74
 
54
75
  def list(args)
55
- account_name = nil
76
+ usage = "Usage: morpheus roles list"
56
77
  options = {}
57
- params = {}
58
78
  optparse = OptionParser.new do|opts|
59
- Morpheus::Cli::CliCommand.accountScopeOptions(opts,options)
60
- Morpheus::Cli::CliCommand.genericOptions(opts,options)
79
+ opts.banner = usage
80
+ build_common_options(opts, options, [:list, :json])
61
81
  end
62
82
  optparse.parse(args)
83
+
63
84
  connect(options)
64
85
  begin
65
- account_id = nil
66
- if !account_name.nil?
67
- account = find_account_by_name(account_name)
68
- exit 1 if account.nil?
69
- account_id = account['id']
70
- end
71
86
 
87
+ account = find_account_from_options(options)
88
+ account_id = account ? account['id'] : nil
89
+
90
+ params = {}
72
91
  [:phrase, :offset, :max, :sort, :direction].each do |k|
73
92
  params[k] = options[k] unless options[k].nil?
74
93
  end
@@ -89,40 +108,58 @@ class Morpheus::Cli::Roles
89
108
  print reset,"\n\n"
90
109
  end
91
110
  rescue RestClient::Exception => e
92
- ::Morpheus::Cli::ErrorHandler.new.print_rest_exception(e)
111
+ print_rest_exception(e, options)
93
112
  exit 1
94
113
  end
95
114
  end
96
115
 
97
116
  def details(args)
98
- usage = "Usage: morpheus roles details [name] [options]"
99
- if args.count < 1
100
- puts "\n#{usage}\n\n"
101
- exit 1
102
- end
103
- account = nil
104
- name = args[0]
117
+ usage = "Usage: morpheus roles details [name]"
105
118
  options = {}
106
- params = {}
107
119
  optparse = OptionParser.new do|opts|
108
120
  opts.banner = usage
109
- Morpheus::Cli::CliCommand.accountScopeOptions(opts,options)
110
- Morpheus::Cli::CliCommand.genericOptions(opts,options)
121
+ opts.on(nil,'--feature-access', "Display Feature Access") do |val|
122
+ options[:include_feature_access] = true
123
+ end
124
+ opts.on(nil,'--group-access', "Display Group Access") do
125
+ options[:include_group_access] = true
126
+ end
127
+ opts.on(nil,'--cloud-access', "Display Cloud Access") do
128
+ options[:include_cloud_access] = true
129
+ end
130
+ opts.on(nil,'--instance-type-access', "Display Instance Type Access") do
131
+ options[:include_instance_type_access] = true
132
+ end
133
+ opts.on(nil,'--all-access', "Display All Access Lists") do
134
+ options[:include_feature_access] = true
135
+ options[:include_group_access] = true
136
+ options[:include_cloud_access] = true
137
+ options[:include_instance_type_access] = true
138
+ end
139
+ build_common_options(opts, options, [:json])
111
140
  end
112
141
  optparse.parse(args)
142
+
143
+ if args.count < 1
144
+ puts "\n#{usage}\n\n"
145
+ exit 1
146
+ end
147
+ name = args[0]
148
+
113
149
  connect(options)
114
150
  begin
115
151
 
116
152
  account = find_account_from_options(options)
117
153
  account_id = account ? account['id'] : nil
118
154
 
119
- # todo: roles_response = @roles_interface.list(account_id, {name: name}) instead
120
- # there may be response data outside of role that needs to be displayed
121
155
  role = find_role_by_name(account_id, name)
122
156
  exit 1 if role.nil?
123
157
 
158
+ json_response = @roles_interface.get(account_id, role['id'])
159
+ role = json_response['role']
160
+
124
161
  if options[:json]
125
- print JSON.pretty_generate(role)
162
+ print JSON.pretty_generate(json_response)
126
163
  print "\n"
127
164
  else
128
165
  print "\n" ,cyan, bold, "Role Details\n","==================", reset, "\n\n"
@@ -134,31 +171,685 @@ class Morpheus::Cli::Roles
134
171
  puts "Owner: #{role['owner'] ? role['owner']['name'] : nil}"
135
172
  puts "Date Created: #{format_local_dt(role['dateCreated'])}"
136
173
  puts "Last Updated: #{format_local_dt(role['lastUpdated'])}"
174
+
137
175
  print "\n" ,cyan, bold, "Role Instance Limits\n","==================", reset, "\n\n"
138
176
  print cyan
139
177
  puts "Max Storage (bytes): #{role['instanceLimits'] ? role['instanceLimits']['maxStorage'] : 0}"
140
178
  puts "Max Memory (bytes): #{role['instanceLimits'] ? role['instanceLimits']['maxMemory'] : 0}"
141
179
  puts "CPU Count: #{role['instanceLimits'] ? role['instanceLimits']['maxCpu'] : 0}"
180
+
181
+ print "\n" ,cyan, bold, "Feature Access\n","==================", reset, "\n\n"
142
182
  print cyan
183
+
184
+ if options[:include_feature_access]
185
+ rows = json_response['featurePermissions'].collect do |it|
186
+ {
187
+ code: it['code'],
188
+ name: it['name'],
189
+ access: get_access_string(it['access']),
190
+ }
191
+ end
192
+ tp rows, [:code, :name, :access]
193
+ else
194
+ puts "Use --feature-access to list feature access"
195
+ end
196
+
197
+ print "\n" ,cyan, bold, "Group Access\n","==================", reset, "\n\n"
198
+ print cyan
199
+
200
+ puts "Global Group Access: #{get_access_string(json_response['globalSiteAccess'])}\n\n"
201
+ if json_response['globalSiteAccess'] == 'custom'
202
+ if options[:include_group_access]
203
+ rows = json_response['sites'].collect do |it|
204
+ {
205
+ name: it['name'],
206
+ access: get_access_string(it['access']),
207
+ }
208
+ end
209
+ tp rows, [:name, :access]
210
+ else
211
+ puts "Use --group-access to list custom access"
212
+ end
213
+ end
214
+
215
+ print "\n" ,cyan, bold, "Cloud Access\n","==================", reset, "\n\n"
216
+ print cyan
217
+
218
+ puts "Global Cloud Access: #{get_access_string(json_response['globalZoneAccess'])}\n\n"
219
+ if json_response['globalZoneAccess'] == 'custom'
220
+ if options[:include_cloud_access]
221
+ rows = json_response['zones'].collect do |it|
222
+ {
223
+ name: it['name'],
224
+ access: get_access_string(it['access']),
225
+ }
226
+ end
227
+ tp rows, [:name, :access]
228
+ else
229
+ puts "Use --cloud-access to list custom access"
230
+ end
231
+ end
232
+
233
+ print "\n" ,cyan, bold, "Instance Type Access\n","==================", reset, "\n\n"
234
+ print cyan
235
+
236
+ puts "Global Instance Type Access: #{get_access_string(json_response['globalInstanceTypeAccess'])}\n\n"
237
+ if json_response['globalInstanceTypeAccess'] == 'custom'
238
+ if options[:include_instance_type_access]
239
+ rows = json_response['instanceTypePermissions'].collect do |it|
240
+ {
241
+ name: it['name'],
242
+ access: get_access_string(it['access']),
243
+ }
244
+ end
245
+ tp rows, [:name, :access]
246
+ else
247
+ puts "Use --instance-type-access to list custom access"
248
+ end
249
+ end
250
+
143
251
  print reset,"\n\n"
144
252
  end
145
253
  rescue RestClient::Exception => e
146
- ::Morpheus::Cli::ErrorHandler.new.print_rest_exception(e)
254
+ print_rest_exception(e, options)
147
255
  exit 1
148
256
  end
149
257
  end
150
258
 
151
259
  def add(args)
152
- print red,bold, "\nNOT YET IMPLEMENTED!\n\n",reset
153
- exit 1
260
+ usage = "Usage: morpheus roles add [options]"
261
+ options = {}
262
+ optparse = OptionParser.new do|opts|
263
+ opts.banner = usage
264
+ build_common_options(opts, options, [:options, :json])
265
+ end
266
+ optparse.parse(args)
267
+
268
+ connect(options)
269
+ begin
270
+
271
+ account = find_account_from_options(options)
272
+ account_id = account ? account['id'] : nil
273
+
274
+ params = Morpheus::Cli::OptionTypes.prompt(add_role_option_types, options[:options], @api_client, options[:params])
275
+
276
+ #puts "parsed params is : #{params.inspect}"
277
+ role_keys = ['authority', 'description', 'instanceLimits']
278
+ role_payload = params.select {|k,v| role_keys.include?(k) }
279
+ if !role_payload['instanceLimits']
280
+ role_payload['instanceLimits'] = {}
281
+ role_payload['instanceLimits']['maxStorage'] = params['instanceLimits.maxStorage'].to_i if params['instanceLimits.maxStorage'].to_s.strip != ''
282
+ role_payload['instanceLimits']['maxMemory'] = params['instanceLimits.maxMemory'].to_i if params['instanceLimits.maxMemory'].to_s.strip != ''
283
+ role_payload['instanceLimits']['maxCpu'] = params['instanceLimits.maxCpu'].to_i if params['instanceLimits.maxCpu'].to_s.strip != ''
284
+ end
285
+ if params['baseRole'].to_s != ''
286
+ base_role = find_role_by_name(account_id, params['baseRole'])
287
+ exit 1 if base_role.nil?
288
+ role_payload['baseRoleId'] = base_role['id']
289
+ end
290
+ request_payload = {role: role_payload}
291
+ response = @roles_interface.create(account_id, request_payload)
292
+
293
+ if account
294
+ print_green_success "Added role #{role_payload['authority']} to account #{account['name']}"
295
+ else
296
+ print_green_success "Added role #{role_payload['authority']}"
297
+ end
298
+
299
+ details_options = [role_payload["authority"]]
300
+ if account
301
+ details_options.push "--account-id", account['id'].to_s
302
+ end
303
+ details(details_options)
304
+
305
+ rescue RestClient::Exception => e
306
+ print_rest_exception(e, options)
307
+ exit 1
308
+ end
309
+ end
310
+
311
+ def update(args)
312
+ usage = "Usage: morpheus roles update [name] [options]"
313
+ options = {}
314
+ optparse = OptionParser.new do|opts|
315
+ opts.banner = usage
316
+ build_common_options(opts, options, [:options, :json])
317
+ end
318
+ optparse.parse(args)
319
+
320
+ if args.count < 1
321
+ puts "\n#{usage}\n\n"
322
+ exit 1
323
+ end
324
+ name = args[0]
325
+
326
+ connect(options)
327
+
328
+ begin
329
+
330
+ account = find_account_from_options(options)
331
+ account_id = account ? account['id'] : nil
332
+
333
+ role = find_role_by_name(account_id, name)
334
+ exit 1 if role.nil?
335
+
336
+ #params = Morpheus::Cli::OptionTypes.prompt(update_role_option_types, options[:options], @api_client, options[:params])
337
+ params = options[:options] || {}
338
+
339
+ if params.empty?
340
+ puts "\n#{usage}\n\n"
341
+ option_lines = update_role_option_types.collect {|it| "\t-O #{it['fieldName']}=\"value\"" }.join("\n")
342
+ puts "\nAvailable Options:\n#{option_lines}\n\n"
343
+ exit 1
344
+ end
345
+
346
+ #puts "parsed params is : #{params.inspect}"
347
+ role_keys = ['authority', 'description', 'instanceLimits']
348
+ role_payload = params.select {|k,v| role_keys.include?(k) }
349
+ if !role_payload['instanceLimits']
350
+ role_payload['instanceLimits'] = {}
351
+ role_payload['instanceLimits']['maxStorage'] = params['instanceLimits.maxStorage'].to_i if params['instanceLimits.maxStorage'].to_s.strip != ''
352
+ role_payload['instanceLimits']['maxMemory'] = params['instanceLimits.maxMemory'].to_i if params['instanceLimits.maxMemory'].to_s.strip != ''
353
+ role_payload['instanceLimits']['maxCpu'] = params['instanceLimits.maxCpu'].to_i if params['instanceLimits.maxCpu'].to_s.strip != ''
354
+ end
355
+ request_payload = {role: role_payload}
356
+ response = @roles_interface.update(account_id, role['id'], request_payload)
357
+
358
+ print_green_success "Updated role #{role_payload['authority']}"
359
+
360
+ details_options = [role_payload["authority"] || role['authority']]
361
+ if account
362
+ details_options.push "--account-id", account['id'].to_s
363
+ end
364
+ details(details_options)
365
+
366
+ rescue RestClient::Exception => e
367
+ print_rest_exception(e, options)
368
+ exit 1
369
+ end
154
370
  end
155
371
 
156
372
  def remove(args)
157
- print red,bold, "\nNOT YET IMPLEMENTED!\n\n",reset
158
- exit 1
373
+ usage = "Usage: morpheus roles remove [name]"
374
+ options = {}
375
+ optparse = OptionParser.new do|opts|
376
+ opts.banner = usage
377
+ build_common_options(opts, options, [:auto_confirm, :json])
378
+ end
379
+ optparse.parse(args)
380
+
381
+ if args.count < 1
382
+ puts "\n#{usage}\n\n"
383
+ exit 1
384
+ end
385
+ name = args[0]
386
+
387
+ connect(options)
388
+ begin
389
+
390
+ account = find_account_from_options(options)
391
+ account_id = account ? account['id'] : nil
392
+
393
+ role = find_role_by_name(account_id, name)
394
+ exit 1 if role.nil?
395
+ unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the role #{role['authority']}?")
396
+ exit
397
+ end
398
+ json_response = @roles_interface.destroy(account_id, role['id'])
399
+
400
+ if options[:json]
401
+ print JSON.pretty_generate(json_response)
402
+ print "\n"
403
+ else
404
+ print_green_success "Role #{role['authority']} removed"
405
+ end
406
+
407
+ rescue RestClient::Exception => e
408
+ print_rest_exception(e, options)
409
+ exit 1
410
+ end
411
+ end
412
+
413
+ def update_feature_access(args)
414
+ usage = "Usage: morpheus roles update-feature-access [name] [code] [full|read|custom|none]"
415
+ options = {}
416
+ optparse = OptionParser.new do|opts|
417
+ opts.banner = usage
418
+ build_common_options(opts, options, [:json])
419
+ end
420
+ optparse.parse(args)
421
+
422
+ if args.count < 3
423
+ puts "\n#{usage}\n\n"
424
+ exit 1
425
+ end
426
+ name = args[0]
427
+ permission_code = args[1]
428
+ access_value = args[2].to_s.downcase
429
+ if !['full', 'read', 'custom', 'none'].include?(access_value)
430
+ puts "\n#{usage}\n\n"
431
+ exit 1
432
+ end
433
+
434
+ connect(options)
435
+ begin
436
+
437
+ account = find_account_from_options(options)
438
+ account_id = account ? account['id'] : nil
439
+
440
+ role = find_role_by_name(account_id, name)
441
+ exit 1 if role.nil?
442
+
443
+ params = {permissionCode: permission_code, access: access_value}
444
+ json_response = @roles_interface.update_permission(account_id, role['id'], params)
445
+
446
+ if options[:json]
447
+ print JSON.pretty_generate(json_response)
448
+ print "\n"
449
+ else
450
+ print_green_success "Role #{role['authority']} feature access updated"
451
+ end
452
+
453
+ rescue RestClient::Exception => e
454
+ print_rest_exception(e, options)
455
+ exit 1
456
+ end
457
+ end
458
+
459
+ def update_global_group_access(args)
460
+ usage = "Usage: morpheus roles update-global-group-access [name] [full|read|custom|none]"
461
+ options = {}
462
+ optparse = OptionParser.new do|opts|
463
+ opts.banner = usage
464
+ build_common_options(opts, options, [:json])
465
+ end
466
+ optparse.parse(args)
467
+
468
+ if args.count < 2
469
+ puts "\n#{usage}\n\n"
470
+ exit 1
471
+ end
472
+ name = args[0]
473
+ access_value = args[1].to_s.downcase
474
+ if !['full', 'read', 'custom', 'none'].include?(access_value)
475
+ puts "\n#{usage}\n\n"
476
+ exit 1
477
+ end
478
+
479
+ connect(options)
480
+ begin
481
+
482
+ account = find_account_from_options(options)
483
+ account_id = account ? account['id'] : nil
484
+
485
+ role = find_role_by_name(account_id, name)
486
+ exit 1 if role.nil?
487
+
488
+ params = {permissionCode: 'ComputeSite', access: access_value}
489
+ json_response = @roles_interface.update_permission(account_id, role['id'], params)
490
+
491
+ if options[:json]
492
+ print JSON.pretty_generate(json_response)
493
+ print "\n"
494
+ else
495
+ print_green_success "Role #{role['authority']} global group access updated"
496
+ end
497
+
498
+ rescue RestClient::Exception => e
499
+ print_rest_exception(e, options)
500
+ exit 1
501
+ end
502
+ end
503
+
504
+ def update_group_access(args)
505
+ usage = "Usage: morpheus roles update-group-access [name] [group_name] [full|read|none]"
506
+ options = {}
507
+ optparse = OptionParser.new do|opts|
508
+ opts.banner = usage
509
+ build_common_options(opts, options, [:json])
510
+ end
511
+ optparse.parse(args)
512
+
513
+ if args.count < 2
514
+ puts "\n#{usage}\n\n"
515
+ exit 1
516
+ end
517
+ name = args[0]
518
+ group_name = args[1]
519
+ access_value = args[2].to_s.downcase
520
+ if !['full', 'read', 'none'].include?(access_value)
521
+ puts "\n#{usage}\n\n"
522
+ exit 1
523
+ end
524
+
525
+ connect(options)
526
+ begin
527
+
528
+ account = find_account_from_options(options)
529
+ account_id = account ? account['id'] : nil
530
+
531
+ role = find_role_by_name(account_id, name)
532
+ exit 1 if role.nil?
533
+
534
+ role_json = @roles_interface.get(account_id, role['id'])
535
+
536
+ if role_json['globalSiteAccess'] != 'custom'
537
+ print "\n", red, "Global Group Access is currently: #{role_json['globalSiteAccess'].capitalize}"
538
+ print "\n", "You must first set it to Custom via `morpheus roles update-global-group-access \"#{name}\" custom`"
539
+ print "\n\n", reset
540
+ exit 1
541
+ end
542
+
543
+ # group_id = find_group_id_by_name(group_name)
544
+ # exit 1 if group_id.nil?
545
+ group = find_group_by_name(group_name)
546
+ exit 1 if group.nil?
547
+ group_id = group['id']
548
+
549
+ params = {groupId: group_id, access: access_value}
550
+ json_response = @roles_interface.update_group(account_id, role['id'], params)
551
+
552
+ if options[:json]
553
+ print JSON.pretty_generate(json_response)
554
+ print "\n"
555
+ else
556
+ print_green_success "Role #{role['authority']} global group access updated"
557
+ end
558
+
559
+ rescue RestClient::Exception => e
560
+ print_rest_exception(e, options)
561
+ exit 1
562
+ end
563
+ end
564
+
565
+ def update_global_cloud_access(args)
566
+ usage = "Usage: morpheus roles update-global-cloud-access [name] [full|custom|none]"
567
+ options = {}
568
+ optparse = OptionParser.new do|opts|
569
+ opts.banner = usage
570
+ build_common_options(opts, options, [:json])
571
+ end
572
+ optparse.parse(args)
573
+
574
+ if args.count < 2
575
+ puts "\n#{usage}\n\n"
576
+ exit 1
577
+ end
578
+ name = args[0]
579
+ access_value = args[1].to_s.downcase
580
+ if !['full', 'custom', 'none'].include?(access_value)
581
+ puts "\n#{usage}\n\n"
582
+ exit 1
583
+ end
584
+
585
+ connect(options)
586
+ begin
587
+
588
+ account = find_account_from_options(options)
589
+ account_id = account ? account['id'] : nil
590
+
591
+ role = find_role_by_name(account_id, name)
592
+ exit 1 if role.nil?
593
+
594
+ params = {permissionCode: 'ComputeZone', access: access_value}
595
+ json_response = @roles_interface.update_permission(account_id, role['id'], params)
596
+
597
+ if options[:json]
598
+ print JSON.pretty_generate(json_response)
599
+ print "\n"
600
+ else
601
+ print_green_success "Role #{role['authority']} global cloud access updated"
602
+ end
603
+
604
+ rescue RestClient::Exception => e
605
+ print_rest_exception(e, options)
606
+ exit 1
607
+ end
608
+ end
609
+
610
+ def update_cloud_access(args)
611
+ usage = "Usage: morpheus roles update-cloud-access [name] [cloud_name] [full|none]"
612
+ options = {}
613
+ optparse = OptionParser.new do|opts|
614
+ opts.banner = usage
615
+ opts.on( '-g', '--group GROUP', "Group to find cloud in" ) do |val|
616
+ options[:group] = val
617
+ end
618
+ build_common_options(opts, options, [:json])
619
+ end
620
+ optparse.parse(args)
621
+
622
+ if args.count < 2
623
+ puts "\n#{usage}\n\n"
624
+ exit 1
625
+ end
626
+ name = args[0]
627
+ cloud_name = args[1]
628
+ access_value = args[2].to_s.downcase
629
+ if !['full', 'none'].include?(access_value)
630
+ puts "\n#{usage}\n\n"
631
+ exit 1
632
+ end
633
+
634
+ connect(options)
635
+ begin
636
+
637
+ account = find_account_from_options(options)
638
+ account_id = account ? account['id'] : nil
639
+
640
+ role = find_role_by_name(account_id, name)
641
+ exit 1 if role.nil?
642
+
643
+ role_json = @roles_interface.get(account_id, role['id'])
644
+
645
+ if role_json['globalZoneAccess'] != 'custom'
646
+ print "\n", red, "Global Cloud Access is currently: #{role_json['globalZoneAccess'].capitalize}"
647
+ print "\n", "You must first set it to Custom via `morpheus roles update-global-cloud-access \"#{name}\" custom`"
648
+ print "\n\n", reset
649
+ exit 1
650
+ end
651
+
652
+ group_id = nil
653
+ if !options[:group].nil?
654
+ group_id = find_group_id_by_name(options[:group])
655
+ else
656
+ group_id = @active_groups[@appliance_name.to_sym]
657
+ end
658
+
659
+ if group_id.nil?
660
+ print_red_alert "Group not found or specified!"
661
+ exit 1
662
+ end
663
+
664
+ cloud_id = find_cloud_id_by_name(group_id, cloud_name)
665
+ exit 1 if cloud_id.nil?
666
+ params = {cloudId: cloud_id, access: access_value}
667
+ json_response = @roles_interface.update_cloud(account_id, role['id'], params)
668
+
669
+ if options[:json]
670
+ print JSON.pretty_generate(json_response)
671
+ print "\n"
672
+ else
673
+ print_green_success "Role #{role['authority']} global cloud access updated"
674
+ end
675
+
676
+ rescue RestClient::Exception => e
677
+ print_rest_exception(e, options)
678
+ exit 1
679
+ end
680
+ end
681
+
682
+ def update_global_instance_type_access(args)
683
+ usage = "Usage: morpheus roles update-global-instance-type-access [name] [full|custom|none]"
684
+ options = {}
685
+ optparse = OptionParser.new do|opts|
686
+ opts.banner = usage
687
+ build_common_options(opts, options, [:json])
688
+ end
689
+ optparse.parse(args)
690
+
691
+ if args.count < 2
692
+ puts "\n#{usage}\n\n"
693
+ exit 1
694
+ end
695
+ name = args[0]
696
+ access_value = args[1].to_s.downcase
697
+ if !['full', 'custom', 'none'].include?(access_value)
698
+ puts "\n#{usage}\n\n"
699
+ exit 1
700
+ end
701
+
702
+
703
+ connect(options)
704
+ begin
705
+
706
+ account = find_account_from_options(options)
707
+ account_id = account ? account['id'] : nil
708
+
709
+ role = find_role_by_name(account_id, name)
710
+ exit 1 if role.nil?
711
+
712
+ params = {permissionCode: 'InstanceType', access: access_value}
713
+ json_response = @roles_interface.update_permission(account_id, role['id'], params)
714
+
715
+ if options[:json]
716
+ print JSON.pretty_generate(json_response)
717
+ print "\n"
718
+ else
719
+ print_green_success "Role #{role['authority']} global instance type access updated"
720
+ end
721
+
722
+ rescue RestClient::Exception => e
723
+ print_rest_exception(e, options)
724
+ exit 1
725
+ end
726
+ end
727
+
728
+ def update_instance_type_access(args)
729
+ usage = "Usage: morpheus roles update-instance-type-access [name] [instance_type_name] [full|none]"
730
+ options = {}
731
+ optparse = OptionParser.new do|opts|
732
+ opts.banner = usage
733
+ build_common_options(opts, options, [:json])
734
+ end
735
+ optparse.parse(args)
736
+
737
+ if args.count < 2
738
+ puts "\n#{usage}\n\n"
739
+ exit 1
740
+ end
741
+ name = args[0]
742
+ instance_type_name = args[1]
743
+ access_value = args[2].to_s.downcase
744
+ if !['full', 'none'].include?(access_value)
745
+ puts "\n#{usage}\n\n"
746
+ exit 1
747
+ end
748
+
749
+ connect(options)
750
+ begin
751
+
752
+ account = find_account_from_options(options)
753
+ account_id = account ? account['id'] : nil
754
+
755
+ role = find_role_by_name(account_id, name)
756
+ exit 1 if role.nil?
757
+
758
+ role_json = @roles_interface.get(account_id, role['id'])
759
+
760
+ if role_json['globalInstanceTypeAccess'] != 'custom'
761
+ print "\n", red, "Global Instance Type Access is currently: #{role_json['globalInstanceTypeAccess'].capitalize}"
762
+ print "\n", "You must first set it to Custom via `morpheus roles update-global-instance-type-access \"#{name}\" custom`"
763
+ print "\n\n", reset
764
+ exit 1
765
+ end
766
+
767
+ instance_type = find_instance_type_by_name(instance_type_name)
768
+ exit 1 if instance_type.nil?
769
+
770
+ params = {instanceTypeId: instance_type['id'], access: access_value}
771
+ json_response = @roles_interface.update_instance_type(account_id, role['id'], params)
772
+
773
+ if options[:json]
774
+ print JSON.pretty_generate(json_response)
775
+ print "\n"
776
+ else
777
+ print_green_success "Role #{role['authority']} global instance type access updated"
778
+ end
779
+
780
+ rescue RestClient::Exception => e
781
+ print_rest_exception(e, options)
782
+ exit 1
783
+ end
159
784
  end
160
785
 
161
786
  private
162
787
 
788
+ def get_access_string(val)
789
+ val ||= 'none'
790
+ if val == 'none'
791
+ "#{white}#{val.to_s.capitalize}#{cyan}"
792
+ else
793
+ "#{green}#{val.to_s.capitalize}#{cyan}"
794
+ end
795
+ end
796
+
797
+ def add_role_option_types
798
+ [
799
+ {'fieldName' => 'authority', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
800
+ {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'displayOrder' => 2},
801
+ {'fieldName' => 'baseRole', 'fieldLabel' => 'Copy From Role', 'type' => 'text', 'displayOrder' => 3},
802
+ {'fieldName' => 'instanceLimits.maxStorage', 'fieldLabel' => 'Max Storage (bytes)', 'type' => 'text', 'displayOrder' => 8},
803
+ {'fieldName' => 'instanceLimits.maxMemory', 'fieldLabel' => 'Max Memory (bytes)', 'type' => 'text', 'displayOrder' => 9},
804
+ {'fieldName' => 'instanceLimits.maxCpu', 'fieldLabel' => 'CPU Count', 'type' => 'text', 'displayOrder' => 10},
805
+ ]
806
+ end
807
+
808
+ def update_role_option_types
809
+ add_role_option_types.reject {|it| ['baseRole'].include?(it['fieldName']) }
810
+ end
811
+
812
+
813
+ def find_group_by_name(name)
814
+ group_results = @groups_interface.get(name)
815
+ if group_results['groups'].empty?
816
+ print_red_alert "Group not found by name #{name}"
817
+ return nil
818
+ end
819
+ return group_results['groups'][0]
820
+ end
821
+
822
+ # no worky, returning {"success"=>true, "data"=>[]}
823
+ # def find_group_id_by_name(name)
824
+ # option_results = @options_interface.options_for_source('groups',{})
825
+ # puts "option_results: #{option_results.inspect}"
826
+ # match = option_results['data'].find { |grp| grp['value'].to_s == name.to_s || grp['name'].downcase == name.downcase}
827
+ # if match.nil?
828
+ # print_red_alert "Group not found by name #{name}"
829
+ # return nil
830
+ # else
831
+ # return match['value']
832
+ # end
833
+ # end
834
+
835
+ def find_cloud_id_by_name(group_id, name)
836
+ option_results = @options_interface.options_for_source('clouds', {groupId: group_id})
837
+ match = option_results['data'].find { |grp| grp['value'].to_s == name.to_s || grp['name'].downcase == name.downcase}
838
+ if match.nil?
839
+ print_red_alert "Cloud not found by name #{name}"
840
+ return nil
841
+ else
842
+ return match['value']
843
+ end
844
+ end
845
+
846
+ def find_instance_type_by_name(name)
847
+ results = @instance_types_interface.get({name: name})
848
+ if results['instanceTypes'].empty?
849
+ print_red_alert "Instance Type not found by name #{name}"
850
+ return nil
851
+ end
852
+ return results['instanceTypes'][0]
853
+ end
163
854
 
164
855
  end