morpheus-cli 4.2.8 → 4.2.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api.rb +1 -1
  4. data/lib/morpheus/api/activity_interface.rb +9 -0
  5. data/lib/morpheus/api/api_client.rb +83 -27
  6. data/lib/morpheus/api/apps_interface.rb +21 -0
  7. data/lib/morpheus/api/dashboard_interface.rb +5 -21
  8. data/lib/morpheus/api/instances_interface.rb +3 -10
  9. data/lib/morpheus/api/invoice_line_items_interface.rb +14 -0
  10. data/lib/morpheus/api/invoices_interface.rb +7 -12
  11. data/lib/morpheus/api/library_layouts_interface.rb +8 -0
  12. data/lib/morpheus/api/ping_interface.rb +20 -0
  13. data/lib/morpheus/api/projects_interface.rb +33 -0
  14. data/lib/morpheus/api/setup_interface.rb +19 -36
  15. data/lib/morpheus/api/user_settings_interface.rb +0 -6
  16. data/lib/morpheus/api/whoami_interface.rb +4 -8
  17. data/lib/morpheus/benchmarking.rb +16 -26
  18. data/lib/morpheus/cli.rb +10 -5
  19. data/lib/morpheus/cli/access_token_command.rb +5 -8
  20. data/lib/morpheus/cli/activity_command.rb +146 -0
  21. data/lib/morpheus/cli/apps.rb +312 -121
  22. data/lib/morpheus/cli/archives_command.rb +1 -1
  23. data/lib/morpheus/cli/auth_command.rb +4 -11
  24. data/lib/morpheus/cli/blueprints_command.rb +196 -137
  25. data/lib/morpheus/cli/change_password_command.rb +1 -1
  26. data/lib/morpheus/cli/cli_command.rb +225 -72
  27. data/lib/morpheus/cli/cli_registry.rb +2 -2
  28. data/lib/morpheus/cli/cloud_datastores_command.rb +1 -1
  29. data/lib/morpheus/cli/clouds.rb +5 -20
  30. data/lib/morpheus/cli/clusters.rb +4 -28
  31. data/lib/morpheus/cli/commands/standard/alias_command.rb +2 -9
  32. data/lib/morpheus/cli/commands/standard/benchmark_command.rb +2 -0
  33. data/lib/morpheus/cli/commands/standard/curl_command.rb +2 -3
  34. data/lib/morpheus/cli/commands/standard/history_command.rb +3 -6
  35. data/lib/morpheus/cli/commands/standard/man_command.rb +10 -7
  36. data/lib/morpheus/cli/commands/standard/ssl_verification_command.rb +10 -9
  37. data/lib/morpheus/cli/containers_command.rb +3 -3
  38. data/lib/morpheus/cli/credentials.rb +13 -16
  39. data/lib/morpheus/cli/error_handler.rb +18 -12
  40. data/lib/morpheus/cli/errors.rb +45 -0
  41. data/lib/morpheus/cli/execute_schedules_command.rb +1 -1
  42. data/lib/morpheus/cli/execution_request_command.rb +4 -4
  43. data/lib/morpheus/cli/groups.rb +84 -132
  44. data/lib/morpheus/cli/hosts.rb +6 -16
  45. data/lib/morpheus/cli/instances.rb +100 -183
  46. data/lib/morpheus/cli/invoices_command.rb +505 -71
  47. data/lib/morpheus/cli/library_layouts_command.rb +254 -166
  48. data/lib/morpheus/cli/library_option_lists_command.rb +0 -87
  49. data/lib/morpheus/cli/library_option_types_command.rb +0 -96
  50. data/lib/morpheus/cli/license.rb +3 -0
  51. data/lib/morpheus/cli/login.rb +17 -37
  52. data/lib/morpheus/cli/logout.rb +9 -5
  53. data/lib/morpheus/cli/mixins/accounts_helper.rb +83 -7
  54. data/lib/morpheus/cli/mixins/operations_helper.rb +41 -0
  55. data/lib/morpheus/cli/mixins/option_source_helper.rb +255 -0
  56. data/lib/morpheus/cli/mixins/print_helper.rb +18 -4
  57. data/lib/morpheus/cli/mixins/provisioning_helper.rb +222 -13
  58. data/lib/morpheus/cli/mixins/remote_helper.rb +139 -0
  59. data/lib/morpheus/cli/monitoring_checks_command.rb +11 -3
  60. data/lib/morpheus/cli/network_groups_command.rb +8 -2
  61. data/lib/morpheus/cli/option_types.rb +1 -1
  62. data/lib/morpheus/cli/ping.rb +252 -0
  63. data/lib/morpheus/cli/price_sets_command.rb +16 -27
  64. data/lib/morpheus/cli/prices_command.rb +34 -27
  65. data/lib/morpheus/cli/processes_command.rb +81 -7
  66. data/lib/morpheus/cli/projects_command.rb +607 -0
  67. data/lib/morpheus/cli/recent_activity_command.rb +87 -65
  68. data/lib/morpheus/cli/remote.rb +965 -974
  69. data/lib/morpheus/cli/reports_command.rb +3 -15
  70. data/lib/morpheus/cli/roles.rb +8 -31
  71. data/lib/morpheus/cli/service_plans_command.rb +25 -31
  72. data/lib/morpheus/cli/setup.rb +392 -0
  73. data/lib/morpheus/cli/shell.rb +144 -56
  74. data/lib/morpheus/cli/subnets_command.rb +71 -11
  75. data/lib/morpheus/cli/tasks.rb +3 -3
  76. data/lib/morpheus/cli/user_sources_command.rb +4 -4
  77. data/lib/morpheus/cli/users.rb +135 -109
  78. data/lib/morpheus/cli/version.rb +1 -1
  79. data/lib/morpheus/cli/whitelabel_settings_command.rb +7 -7
  80. data/lib/morpheus/cli/whoami.rb +90 -129
  81. data/lib/morpheus/cli/wiki_command.rb +2 -14
  82. data/lib/morpheus/ext/rest_client.rb +36 -0
  83. data/lib/morpheus/formatters.rb +42 -5
  84. data/lib/morpheus/rest_client.rb +0 -10
  85. data/lib/morpheus/terminal.rb +41 -1
  86. data/lib/morpheus/util.rb +24 -0
  87. metadata +16 -3
  88. data/lib/morpheus/cli/command_error.rb +0 -22
@@ -0,0 +1,255 @@
1
+ require 'morpheus/cli/mixins/print_helper'
2
+
3
+ # Mixin for Morpheus::Cli command classes
4
+ # Provides common methods for fetching objects via /api/options/:optionSource
5
+ # The including class must establish @options_interface or @api_client
6
+ # This is useful for when the user does not need to have permission to other endpoints
7
+ module Morpheus::Cli::OptionSourceHelper
8
+
9
+ def self.included(klass)
10
+ klass.send :include, Morpheus::Cli::PrintHelper
11
+ end
12
+
13
+ def options_interface
14
+ api_interface = @options_interface
15
+ api_interface = @api_client.options if api_interface.nil? && @api_client
16
+ # @api_client.options
17
+ raise "#{self.class} has not defined @options_interface or @api_client" if api_interface.nil?
18
+ api_interface
19
+ end
20
+
21
+ # todo: rewrite these specific methods to use the generic one.
22
+
23
+ def get_available_user_options(refresh=false)
24
+ if !@available_user_options || refresh
25
+ option_results = options_interface.options_for_source('users',{})
26
+ @available_user_options = option_results['data'].collect {|it|
27
+ {"name" => it["name"], "value" => it["value"],
28
+ "username" => it["name"], "id" => it["value"]}
29
+ }
30
+ end
31
+ return @available_user_options
32
+ end
33
+
34
+ def find_available_user_option(name)
35
+ users = get_available_user_options().select {|it|
36
+ name && (it['name'].to_s.downcase == name.to_s.downcase || it['value'].to_s == name.to_s) }
37
+ if users.empty?
38
+ print_red_alert "User not found by username or id '#{name}'"
39
+ return nil
40
+ elsif users.size > 1
41
+ print_red_alert "#{users.size} users found by username or id '#{name}'"
42
+ return nil
43
+ else
44
+ return users[0]
45
+ end
46
+ end
47
+
48
+
49
+ def get_group_options(refresh=false, api_params={})
50
+ if !@available_group_options || refresh
51
+ option_results = options_interface.options_for_source('groups', api_params)
52
+ @available_group_options = option_results['data'].collect {|it|
53
+ {"name" => it["name"], "value" => it["value"], "id" => it["value"]}
54
+ }
55
+ end
56
+ return @available_group_options
57
+ end
58
+
59
+ def find_group_option(group_id, refresh=false, api_params={})
60
+ if group_id.to_s.strip == ""
61
+ print_red_alert "Group not found by for blank id"
62
+ return nil
63
+ end
64
+ groups = get_group_options(refresh, api_params).select {|it| (it['name'].to_s == group_id.to_s || it['id'].to_s == group_id.to_s) }
65
+ if groups.empty?
66
+ print_red_alert "Group not found by '#{group_id}'"
67
+ return nil
68
+ elsif groups.size > 1
69
+ print_red_alert "#{groups.size} groups found by '#{group_id}'"
70
+ return nil
71
+ else
72
+ return groups[0]
73
+ end
74
+ end
75
+
76
+ def get_cloud_options(refresh=false, api_params={})
77
+ if !@available_cloud_options || refresh
78
+ option_results = options_interface.options_for_source('clouds', api_params)
79
+ @available_cloud_options = option_results['data'].collect {|it|
80
+ {"name" => it["name"], "value" => it["value"], "id" => it["value"]}
81
+ }
82
+ end
83
+ return @available_cloud_options
84
+ end
85
+
86
+ def find_cloud_option(cloud_id, refresh=false, api_params={})
87
+ if cloud_id.to_s.strip == ""
88
+ print_red_alert "Cloud not found by for blank id"
89
+ return nil
90
+ end
91
+ clouds = get_cloud_options(refresh, api_params).select {|it| (it['name'].to_s == cloud_id.to_s || it['id'].to_s == cloud_id.to_s) }
92
+ if clouds.empty?
93
+ print_red_alert "Cloud not found by '#{cloud_id}'"
94
+ return nil
95
+ elsif clouds.size > 1
96
+ print_red_alert "#{clouds.size} clouds found by '#{cloud_id}'"
97
+ return nil
98
+ else
99
+ return clouds[0]
100
+ end
101
+ end
102
+
103
+ def get_tenant_options(refresh=false, api_params={})
104
+ if !@available_tenant_options || refresh
105
+ # source should be 'tenants' or 'allTenants'
106
+ # allTenants includes the current tenant
107
+ # I think we should always use that so you can use your own id
108
+ option_source = 'allTenants'
109
+ option_results = options_interface.options_for_source(option_source, api_params)
110
+ @available_tenant_options = option_results['data'].collect {|it|
111
+ {"name" => it["name"], "value" => it["value"], "id" => it["value"]}
112
+ }
113
+ end
114
+ return @available_tenant_options
115
+ end
116
+
117
+ def find_tenant_option(tenant_id, refresh=false, api_params={})
118
+ tenant_id = tenant_id.to_s.strip
119
+ tenant_id_downcase = tenant_id.to_s.downcase
120
+ if tenant_id == ""
121
+ print_red_alert "Tenant not found by for blank id"
122
+ return nil
123
+ end
124
+ tenants = get_tenant_options(refresh, api_params).select {|it| (it['name'].to_s.downcase == tenant_id_downcase || it['id'].to_s == tenant_id.to_s) }
125
+ if tenants.empty?
126
+ print_red_alert "Tenant not found by '#{tenant_id}'"
127
+ return nil
128
+ elsif tenants.size > 1
129
+ print_red_alert "#{tenants.size} tenants found by '#{tenant_id}'"
130
+ return nil
131
+ else
132
+ return tenants[0]
133
+ end
134
+ end
135
+
136
+ # parse cloud names or IDs into a name, works with array or csv
137
+ # skips validation of ID for now, just worry about translating names/codes
138
+ # def parse_cloud_id_list(id_list)
139
+ # cloud_ids = parse_id_list(id_list).collect {|cloud_id|
140
+ # if cloud_id.to_s =~ /\A\d{1,}\Z/
141
+ # cloud_id
142
+ # else
143
+ # cloud = find_cloud_option(cloud_id)
144
+ # if cloud.nil?
145
+ # # exit 1
146
+ # return nil
147
+ # end
148
+ # cloud['id']
149
+ # end
150
+ # }
151
+ # return cloud_ids
152
+ # end
153
+
154
+ # todo: some other common ones, accounts (tenants), etc.
155
+
156
+
157
+
158
+ # a generic set of parse and find methods for any option source data
159
+ def load_option_source_data(option_source, api_params={}, refresh=false, &block)
160
+ @_option_source_cache ||= {}
161
+ option_source_hash = "#{option_source}#{api_params.empty? ? '' : api_params.to_s}"
162
+ data = @_option_source_cache[option_source_hash]
163
+ if data.nil? || refresh
164
+ json_response = options_interface.options_for_source(option_source, api_params)
165
+ data = json_response['data'].collect {|it|
166
+ {
167
+ "name" => it["name"],
168
+ "value" => it["value"],
169
+ "id" => (it["id"] || it["value"]),
170
+ "code" => it["code"]
171
+ }
172
+ }
173
+ @_option_source_cache[option_source_hash] = data
174
+ end
175
+ return data
176
+ end
177
+
178
+ # todo: some other common ones, accounts (tenants), etc.
179
+ # todo: a generic set of parse and find methods like
180
+ # like this:
181
+ def parse_option_source_id_list(option_source, id_list, api_params={}, refresh=false)
182
+ option_source_label = option_source.to_s # .capitalize
183
+ option_data = load_option_source_data(option_source, api_params, refresh)
184
+ found_ids = []
185
+ parse_id_list(id_list).each {|record_id|
186
+ lowercase_id = record_id.to_s.downcase
187
+ # need to parameterize this behavior
188
+ if record_id.to_s.empty?
189
+ # never match blank nil or empty strings
190
+ print_red_alert "#{option_source_label} cannot be not found by with a blank id!"
191
+ return nil
192
+ # elsif record_id.to_s =~ /\A\d{1,}\Z/
193
+ # # always allow any ID for now..
194
+ # found_ids << record_id
195
+ else
196
+ # search with in a presedence by value, then name, then id (usually same as value)
197
+ # exact match on value first.
198
+ matching_results = []
199
+ matching_results = option_data.select {|it| it['value'] && it['value'] == record_id }
200
+ # match on value case /i
201
+ if matching_results.empty?
202
+ matching_results = option_data.select {|it| it['value'] && it['value'].to_s.downcase == lowercase_id }
203
+ end
204
+ # match on name case /i
205
+ if matching_results.empty?
206
+ matching_results = option_data.select {|it| it['name'] && it['name'].to_s.downcase == lowercase_id }
207
+ end
208
+ # match on id too, in case it is returned and different from value?
209
+ if matching_results.empty?
210
+ matching_results = option_data.select {|it| it['id'] && it['id'].to_s.downcase == lowercase_id }
211
+ end
212
+ if matching_results.empty?
213
+ print_red_alert "No #{option_source_label} found matching name or id '#{record_id}'"
214
+ return nil
215
+ elsif matching_results.size > 1
216
+ print_red_alert "#{matching_results.size} #{option_source_label} found matching name '#{record_id}'. Try specifying the id instead."
217
+ return nil
218
+ else
219
+ matching_result = matching_results[0]
220
+ if matching_result['value']
221
+ found_ids << matching_result['value']
222
+ else
223
+ found_ids << matching_result['id']
224
+ end
225
+ end
226
+ end
227
+ }
228
+ return found_ids
229
+ end
230
+
231
+ def parse_cloud_id_list(id_list, api_params={}, refresh=false)
232
+ parse_option_source_id_list('clouds', id_list, api_params, refresh)
233
+ end
234
+
235
+ def parse_group_id_list(id_list, api_params={}, refresh=false)
236
+ parse_option_source_id_list('groups', id_list, api_params, refresh)
237
+ end
238
+
239
+ def parse_user_id_list(id_list, api_params={}, refresh=false)
240
+ parse_option_source_id_list('users', id_list, api_params, refresh)
241
+ end
242
+
243
+ def parse_tenant_id_list(id_list, api_params={}, refresh=false)
244
+ parse_option_source_id_list('allTenants', id_list, api_params, refresh)
245
+ end
246
+
247
+ # def parse_blueprints_id_list(id_list)
248
+ # parse_option_source_id_list('blueprints', id_list, api_params, refresh)
249
+ # end
250
+
251
+ def parse_project_id_list(id_list, api_params={}, refresh=false)
252
+ parse_option_source_id_list('projects', id_list, api_params, refresh)
253
+ end
254
+
255
+ end
@@ -38,13 +38,16 @@ module Morpheus::Cli::PrintHelper
38
38
  # puts red message to stderr
39
39
  # why this not stderr yet? use print_error or if respond_to?(:my_terminal)
40
40
  def print_red_alert(msg)
41
- #$stderr.print "#{red}#{msg}#{reset}\n"
42
- print "#{red}#{msg}#{reset}\n"
41
+ $stderr.print "#{red}#{msg}#{reset}\n"
42
+ #print_error "#{red}#{msg}#{reset}\n"
43
43
  #puts_error "#{red}#{msg}#{reset}"
44
44
  end
45
45
 
46
46
  # puts green message to stdout
47
- def print_green_success(msg)
47
+ def print_green_success(msg=nil)
48
+ if msg.nil?
49
+ msg = "success"
50
+ end
48
51
  print "#{green}#{msg}#{reset}\n"
49
52
  end
50
53
 
@@ -59,10 +62,21 @@ module Morpheus::Cli::PrintHelper
59
62
  # print_h1(title, options={})
60
63
  # print_h1(title, subtitles, options={})
61
64
  # this can go away when we have a dirty @current_options
65
+
66
+
67
+ # auto include remote name in h1 titles
68
+ # eg. Morpheus Instances [dev]
69
+ # if title && @appliance_name
70
+ # title = "#{title} [#{@appliance_name}]"
71
+ # end
72
+
62
73
  if subtitles.is_a?(Hash)
63
74
  options = subtitles
64
75
  subtitles = (options[:subtitles] || []).flatten
65
76
  end
77
+ if subtitles.is_a?(String)
78
+ subtitles = [subtitles]
79
+ end
66
80
  subtitles = (subtitles || []).flatten
67
81
  options ||= {}
68
82
  color = options[:color] || cyan
@@ -728,7 +742,7 @@ module Morpheus::Cli::PrintHelper
728
742
  # could use some options[:preferred_columns] logic here to throw away in some specified order
729
743
  # --all fields disables this
730
744
  trimmed_columns = []
731
- if options[:responsive_table] != false # && options[:include_fields].nil? && options[:all_fields] != true
745
+ if options[:wrap] != true # && options[:include_fields].nil? && options[:all_fields] != true
732
746
 
733
747
  begin
734
748
  term_width = current_terminal_width()
@@ -17,6 +17,14 @@ module Morpheus::Cli::ProvisioningHelper
17
17
  @api_client.instances
18
18
  end
19
19
 
20
+ def apps_interface
21
+ @api_client.apps
22
+ end
23
+
24
+ def servers_interface
25
+ @api_client.servers
26
+ end
27
+
20
28
  def options_interface
21
29
  @api_client.options
22
30
  end
@@ -82,7 +90,10 @@ module Morpheus::Cli::ProvisioningHelper
82
90
 
83
91
  def get_available_accounts(refresh=false)
84
92
  if !@available_accounts || refresh
85
- @available_accounts = accounts_interface.list()['accounts']
93
+ # @available_accounts = accounts_interface.list()['accounts']
94
+ @available_accounts = options_interface.options_for_source("allTenants", {})['data'].collect {|it|
95
+ {"name" => it["name"], "value" => it["value"], "id" => it["value"]}
96
+ }
86
97
  end
87
98
  @available_accounts
88
99
  end
@@ -242,6 +253,129 @@ module Morpheus::Cli::ProvisioningHelper
242
253
  return instance
243
254
  end
244
255
 
256
+ def parse_instance_id_list(id_list)
257
+ parse_id_list(id_list).collect do |instance_id|
258
+ find_instance_by_name_or_id(instance_id)['id']
259
+ end
260
+ end
261
+
262
+ ## apps
263
+
264
+ def find_app_by_id(id)
265
+ app_results = apps_interface.get(id.to_i)
266
+ if app_results['app'].empty?
267
+ print_red_alert "App not found by id #{id}"
268
+ exit 1
269
+ end
270
+ return app_results['app']
271
+ end
272
+
273
+ def find_app_by_name(name)
274
+ app_results = apps_interface.list({name: name})
275
+ apps = app_results['apps']
276
+ if apps.empty?
277
+ print_red_alert "App not found by name #{name}"
278
+ exit 1
279
+ elsif apps.size > 1
280
+ print_red_alert "#{apps.size} apps exist with the name #{name}. Try using id instead"
281
+ exit 1
282
+ end
283
+
284
+ return app_results['apps'][0]
285
+ end
286
+
287
+ def find_app_by_name_or_id(val)
288
+ if val.to_s =~ /\A\d{1,}\Z/
289
+ return find_app_by_id(val)
290
+ else
291
+ return find_app_by_name(val)
292
+ end
293
+ end
294
+
295
+ ## servers
296
+
297
+ def find_server_by_id(id)
298
+ begin
299
+ json_response = servers_interface.get(id.to_i)
300
+ return json_response['server']
301
+ rescue RestClient::Exception => e
302
+ if e.response && e.response.code == 404
303
+ print_red_alert "Server not found by id #{id}"
304
+ exit 1
305
+ else
306
+ raise e
307
+ end
308
+ end
309
+ end
310
+
311
+ def find_server_by_name(name)
312
+ results = servers_interface.list({name: name})
313
+ if results['servers'].empty?
314
+ print_red_alert "Server not found by name #{name}"
315
+ exit 1
316
+ elsif results['servers'].size > 1
317
+ print_red_alert "Multiple servers exist with the name #{name}. Try using id instead"
318
+ exit 1
319
+ end
320
+ return results['servers'][0]
321
+ end
322
+
323
+ def find_server_by_name_or_id(val)
324
+ if val.to_s =~ /\A\d{1,}\Z/
325
+ return find_server_by_id(val)
326
+ else
327
+ return find_server_by_name(val)
328
+ end
329
+ end
330
+
331
+ def parse_server_id_list(id_list)
332
+ parse_id_list(id_list).collect do |server_id|
333
+ find_server_by_name_or_id(server_id)['id']
334
+ end
335
+ end
336
+
337
+ ## hosts is the same as servers, just says 'Host' instead of 'Server'
338
+
339
+ def find_host_by_id(id)
340
+ begin
341
+ json_response = servers_interface.get(id.to_i)
342
+ return json_response['server']
343
+ rescue RestClient::Exception => e
344
+ if e.response && e.response.code == 404
345
+ print_red_alert "Host not found by id #{id}"
346
+ exit 1
347
+ else
348
+ raise e
349
+ end
350
+ end
351
+ end
352
+
353
+ def find_host_by_name(name)
354
+ results = servers_interface.list({name: name})
355
+ if results['servers'].empty?
356
+ print_red_alert "Host not found by name #{name}"
357
+ exit 1
358
+ elsif results['servers'].size > 1
359
+ print_red_alert "#{results['servers'].size} hosts exist with the name #{name}. Try using id instead"
360
+ exit 1
361
+ end
362
+ return results['servers'][0]
363
+ end
364
+
365
+ def find_host_by_name_or_id(val)
366
+ if val.to_s =~ /\A\d{1,}\Z/
367
+ return find_host_by_id(val)
368
+ else
369
+ return find_host_by_name(val)
370
+ end
371
+ end
372
+
373
+ def parse_host_id_list(id_list)
374
+ parse_id_list(id_list).collect do |host_id|
375
+ find_host_by_name_or_id(host_id)['id']
376
+ end
377
+ end
378
+
245
379
  def find_instance_type_layout_by_id(layout_id, id)
246
380
  json_results = instance_type_layouts_interface.get(layout_id, id)
247
381
  if json_results['instanceTypeLayout'].empty?
@@ -416,7 +550,7 @@ module Morpheus::Cli::ProvisioningHelper
416
550
  arbitrary_options.delete('cloud')
417
551
  arbitrary_options.delete('type')
418
552
  arbitrary_options.delete('name')
419
- arbitrary_options.delete('version')
553
+ #arbitrary_options.delete('version')
420
554
  arbitrary_options.delete('layout')
421
555
  arbitrary_options.delete('servicePlan')
422
556
  arbitrary_options.delete('description')
@@ -470,6 +604,8 @@ module Morpheus::Cli::ProvisioningHelper
470
604
  layout_id = nil
471
605
  if options[:layout]
472
606
  layout_id = options[:layout]
607
+ # elsif options[:options]['layout']
608
+ # layout_id = options[:options]['layout']
473
609
  end
474
610
  if layout_id.is_a?(Hash)
475
611
  layout_id = layout_id['id'] || layout_id['code'] || layout_id['name']
@@ -479,6 +615,8 @@ module Morpheus::Cli::ProvisioningHelper
479
615
  default_layout_value = nil
480
616
  if options[:version]
481
617
  version_value = options[:version]
618
+ elsif options[:options]['version']
619
+ version_value = options[:options]['version']
482
620
  else
483
621
  available_versions = options_interface.options_for_source('instanceVersions',{groupId: group_id, cloudId: cloud_id, instanceTypeId: instance_type['id']})['data']
484
622
  default_version_value = payload['instance']['version'] ? payload['instance']['version'] : payload['version']
@@ -581,8 +719,11 @@ module Morpheus::Cli::ProvisioningHelper
581
719
  end
582
720
  end
583
721
  #todo: consolidate these, instances api looks for instance.plan.id and apps looks for plan.id
584
- payload['plan'] = {'id' => service_plan["id"], 'code' => service_plan["code"], 'name' => service_plan["name"]}
585
- payload['instance']['plan'] = {'id' => service_plan["id"], 'code' => service_plan["code"], 'name' => service_plan["name"]}
722
+ if options[:for_app]
723
+ payload['plan'] = {'id' => service_plan["id"], 'code' => service_plan["code"], 'name' => service_plan["name"]}
724
+ else
725
+ payload['instance']['plan'] = {'id' => service_plan["id"], 'code' => service_plan["code"], 'name' => service_plan["name"]}
726
+ end
586
727
  end
587
728
 
588
729
  # build config option types
@@ -964,7 +1105,6 @@ module Morpheus::Cli::ProvisioningHelper
964
1105
  default_datastore = datastore_options.find {|ds| ds['value'].to_s == volume['datastoreId'].to_s}
965
1106
  v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'datastoreId', 'type' => 'select', 'fieldLabel' => 'Root Datastore', 'selectOptions' => datastore_options, 'required' => true, 'description' => 'Choose a datastore.', 'defaultValue' => default_datastore ? default_datastore['name'] : volume['datastoreId']}], options[:options])
966
1107
  volume['datastoreId'] = v_prompt[field_context]['datastoreId']
967
- volume['hasDatastore'] = true
968
1108
  end
969
1109
 
970
1110
  volumes << volume
@@ -1029,7 +1169,6 @@ module Morpheus::Cli::ProvisioningHelper
1029
1169
  if !datastore_options.empty?
1030
1170
  v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'datastoreId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Datastore", 'selectOptions' => datastore_options, 'required' => true, 'description' => 'Choose a datastore.', 'defaultValue' => volume['datastoreId']}], options[:options])
1031
1171
  volume['datastoreId'] = v_prompt[field_context]['datastoreId']
1032
- volume['hasDatastore'] = true
1033
1172
  end
1034
1173
 
1035
1174
  volumes << volume
@@ -1507,6 +1646,45 @@ module Morpheus::Cli::ProvisioningHelper
1507
1646
  return evars
1508
1647
  end
1509
1648
 
1649
+ # Converts metadata tags array into a string like "name:value, foo:bar"
1650
+ def format_metadata(tags)
1651
+ if tags.nil? || tags.empty?
1652
+ ""
1653
+ elsif tags.instance_of?(Array)
1654
+ tags.collect {|tag| "#{tag['name']}: #{tag['value']}" }.sort.join(", ")
1655
+ elsif tags.instance_of?(Hash)
1656
+ tags.collect {|k,v| "#{k}: #{v}" }.sort.join(", ")
1657
+ else
1658
+ tags.to_s
1659
+ end
1660
+ end
1661
+
1662
+ # Parses metadata tags object (string) into an array
1663
+ def parse_metadata(val)
1664
+ metadata = nil
1665
+ if val
1666
+ if val == "[]" || val == "null"
1667
+ metadata = []
1668
+ elsif val.is_a?(Array)
1669
+ metadata = val
1670
+ else
1671
+ # parse string into format name:value, name:value
1672
+ # merge IDs from current metadata
1673
+ # todo: should allow quoted semicolons..
1674
+ metadata_list = val.to_s.split(",").select {|it| !it.to_s.empty? }
1675
+ metadata_list = metadata_list.collect do |it|
1676
+ metadata_pair = it.include?(":") ? it.split(":") : it.split("=")
1677
+ row = {}
1678
+ row['name'] = metadata_pair[0].to_s.strip
1679
+ row['value'] = metadata_pair[1].to_s.strip
1680
+ row
1681
+ end
1682
+ metadata = metadata_list
1683
+ end
1684
+ end
1685
+ return metadata
1686
+ end
1687
+
1510
1688
  # Prompts user for environment variables for new instance
1511
1689
  # returns array of metadata objects {id: null, name: "MYTAG", value: "myvalue"}
1512
1690
  def prompt_metadata(options={})
@@ -1712,8 +1890,12 @@ module Morpheus::Cli::ProvisioningHelper
1712
1890
  end
1713
1891
 
1714
1892
  if !options[:groupAccessList].empty?
1715
- group_access = options[:groupAccessList].collect {|site_id| {'id' => site_id.to_i}} || []
1716
- elsif !options[:no_prompt]
1893
+ group_access = options[:groupAccessList].collect {|site_id|
1894
+ found_group = find_group_by_name_or_id_for_provisioning(site_id)
1895
+ return 1, "group not found by #{site_id}" if found_group.nil?
1896
+ {'id' => found_group['id']}
1897
+ } || []
1898
+ elsif !options[:no_prompt] && !all_groups
1717
1899
  available_groups = options[:available_groups] || get_available_groups
1718
1900
 
1719
1901
  if available_groups.empty?
@@ -1806,19 +1988,20 @@ module Morpheus::Cli::ProvisioningHelper
1806
1988
 
1807
1989
  permissions = {'resourcePermissions' => resource_perms}
1808
1990
 
1809
- available_accounts = get_available_accounts.collect {|it| {'name' => it['name'], 'value' => it['id']}}
1991
+ available_accounts = get_available_accounts() #.collect {|it| {'name' => it['name'], 'value' => it['id']}}
1810
1992
  accounts = []
1811
1993
 
1812
1994
  # Prompts for multi tenant
1813
1995
  if available_accounts.count > 1
1814
1996
  visibility = options[:visibility]
1997
+ if !excludes.include?('visibility')
1998
+ if !visibility && !options[:no_prompt]
1999
+ visibility = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'visibility', 'fieldLabel' => 'Tenant Permissions Visibility', 'type' => 'select', 'defaultValue' => 'private', 'required' => true, 'selectOptions' => [{'name' => 'Private', 'value' => 'private'},{'name' => 'Public', 'value' => 'public'}]}], options[:options], @api_client, {})['visibility']
2000
+ end
1815
2001
 
1816
- if !visibility && !options[:no_prompt]
1817
- visibility = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'visibility', 'fieldLabel' => 'Tenant Permissions Visibility', 'type' => 'select', 'defaultValue' => 'private', 'required' => true, 'selectOptions' => [{'name' => 'Private', 'value' => 'private'},{'name' => 'Public', 'value' => 'public'}]}], options[:options], @api_client, {})['visibility']
2002
+ permissions['resourcePool'] = {'visibility' => visibility} if visibility
1818
2003
  end
1819
2004
 
1820
- permissions['resourcePool'] = {'visibility' => visibility} if visibility
1821
-
1822
2005
  # Tenants
1823
2006
  if !excludes.include?('tenants')
1824
2007
  if !options[:tenants].nil?
@@ -1980,4 +2163,30 @@ module Morpheus::Cli::ProvisioningHelper
1980
2163
 
1981
2164
  return ports
1982
2165
  end
2166
+
2167
+ def format_blueprint_type(type_code)
2168
+ return type_code.to_s # just show it as is
2169
+ if type_code.to_s.empty?
2170
+ type_code = "morpheus"
2171
+ end
2172
+ if type_code.to_s.downcase == "arm"
2173
+ "ARM"
2174
+ else
2175
+ return type_code.to_s.capitalize
2176
+ end
2177
+ end
2178
+
2179
+ def parse_blueprint_type(type_code)
2180
+ return type_code.to_s # just use it as is
2181
+ # if type_code.to_s.empty?
2182
+ # type_code = "morpheus"
2183
+ # end
2184
+ if type_code.to_s.downcase == "arm"
2185
+ "arm"
2186
+ elsif type_code.to_s.downcase == "cloudformation"
2187
+ type_code = "cloudFormation"
2188
+ else
2189
+ return type_code.to_s.downcase
2190
+ end
2191
+ end
1983
2192
  end